- server/finance/ (19 файлов) удалён: пул финансовой БД перенесён в server/finance-di2/db-core.ts, db-client.ts теперь реэкспортирует из ./db-core - Шедулер аудита перенесён в server/finance-di2/audit-scheduler.ts (03:00 МСК, setInterval, без node-cron), запуск из registerDi2Routes с защитой от повторного старта - История аудита DI2 теперь персистентна: загрузка/запись data/finance-audit-history.json в audit-agent.ts (порт из удалённого server/finance/audit.ts) - registerFinanceRoutes убран из server/routes/index.ts, registerDi2Routes остался на его месте - Клиент: удалены pages/finance/, components/finance/, hooks/useFinance.ts, hooks/useColumnLabels.ts, lib/format.ts, pages/salary.tsx; роуты /finance*, /income*, /expenses*, /salary и lazy-импорты убраны из App.tsx - Навигация: «Финансы» в Sidebar и MobileBottomNav ведёт на субапп /di2 (внешняя ссылка), гейт moduleAccess.finance + finance.view сохранён - index.css: удалён блок .hover-elevate/.hover-elevate-2/.active-elevate (использовался только старым модульным DataTable) - FinanceBottomNav.tsx сохранён как референс для этапа 3 (.superpowers/sdd/di2-restyle/FinanceBottomNav.reference.tsx) - Не тронуто: client-di2/, сиды прав finance.view/manage, finance_access (module-access)
15 KiB
План: удаление старого модуля финансов + рестайл DI2 под дизайн iistwin
Репозиторий: C:\1Ai\1iistwin\1Project (main, автодеплой через push). Субапп DI2 уже в проде на /di2 (server/finance-di2 + client-di2).
Решения пользователя (зафиксированы): /salary удалить и вести на /di2; рестайл = мост токенов + точечные правки (без замены 45 shadcn-компонентов); мобильная навигация = нижняя панель.
Этап 1 — Удаление старого модуля финансов
1.1 Сервер
- Перенос пула БД: содержимое
server/finance/db-client.ts→ новыйserver/finance-di2/db-core.ts(правка внутренних импортов: там только fs/path/pg). Обновитьserver/finance-di2/db-client.ts: реэкспорт из./db-coreвместо../finance/db-client. Удалить папкуserver/finance/целиком (19 файлов). - Шедулер аудита: создать
server/finance-di2/audit-scheduler.ts— портstartAuditScheduler(cron 03:00 МСК) из удаляемогоserver/finance/audit.ts:524, но вызывающий DI2-функциюrunIntegrityAuditиз./audit-agent. Запускать изregisterDi2Routes(server/finance-di2/index.ts). Реализация — setInterval-проверка времени (как в оригинале) или порт через обычный таймер; node-cron НЕ добавлять. - Персистентность истории аудита: в
server/finance-di2/audit-agent.tsдобавить загрузку/записьdata/finance-audit-history.json(порт логики из старого audit.ts): загрузка при старте модуля, append после каждогоrunIntegrityAudit. Сейчас история в памяти и теряется при рестарте. - Регистрация:
server/routes/index.ts— убратьimport { registerFinanceRoutes }(стр. 33) и вызов (стр. 116), оставитьregisterDi2Routes. - Env сервера (проверка, не код): DI2 Google-флоу использует
GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET— проверить наличие в.envсервера; старыйFINANCE_GOOGLE_REDIRECT_URIбольше не нужен.
1.2 Клиент (основное приложение)
- Удалить:
client/src/pages/finance/(14 файлов),client/src/components/finance/(22 файла),client/src/hooks/useFinance.ts,client/src/hooks/useColumnLabels.ts,client/src/lib/format.ts,client/src/pages/salary.tsx.- Перед удалением:
grep -rn "lib/format\|useFinance" client/src --include=*.tsx --include=*.ts— все внешние импорты должны отсутствовать (по разведке используются только удаляемыми файлами — перепроверить!).
- Перед удалением:
client/src/index.css— удалить блок.hover-elevate/.hover-elevate-2(строки ~343-374, использовался только finance DataTable).client/src/App.tsx:- Убрать lazy-импорты finance-страниц (строки 61-72) и роуты
/finance,/finance/debt,/finance/calculations,/finance/audit,/income*,/expenses*,/salary(строки 117-120, 187-200). - ROUTE_TITLES: убрать записи этих путей.
- Убрать lazy-импорты finance-страниц (строки 61-72) и роуты
- Навигация:
client/src/components/Sidebar.tsx(строки 1084-1122, 1544, 1641-1645):FinanceNavItemи пункт «Финансы» в меню профиля → внешняя ссылка<a href="/di2" target="_blank" rel="noopener">(паттерн как в FinanceSidebar.tsx:88-99), гейтmoduleAccess.finance !== false && (isAdmin || hasPermission('finance.view'))сохранить.client/src/components/MobileBottomNav.tsx(строки 383-389): пункт «Финансы» →/di2.
- Не трогать: сиды прав
finance.view/manage(server/storage/roles.storage.ts:300-301— gate DI2 ломается без них),finance_access(shared/schema.ts, module-access, Users.tsx матрица доступа).
1.3 Проверка этапа
npm run check + npm run build чисто; ключевые API DI2 авторизованно 200 (список из этапа 4).
Этап 2 — Мост тем/скинов iistwin в DI2
Ключевая несовместимость: DI2 ждёт голые HSL-тройки (hsl(var(--x) / <alpha>)), скины iistwin генерируют полные значения (hsl(...)). Решение — перевод DI2 на формат основного клиента + общий generated-skins.css + свой провайдер.
2.1 Общий код тем
- Vite-алиас в
vite.config.di2.ts:"@skin": path.resolve(import.meta.dirname, "client/src/themes")(через../client→ фактически1Project/client/src/themes); пути вclient-di2/tsconfig.json:"@skin/*": ["../client/src/themes/*"]. - Импортируемые модули (чистый TS, без React):
tokens.ts(generateSkinCss,Skinтипы),skins/index.ts(BUILT_IN_SKINS,DEFAULT_SKIN_ID,getBuiltInSkinById),styles/generated-skins.css— через CSS-@importв index.css (путь../../client/src/themes/styles/generated-skins.cssпервой строкой).
2.2 Di2ThemeProvider — client-di2/src/themes/Di2ThemeProvider.tsx
Порт логики client/src/themes/ThemeProvider.tsx (строки 75-92, 151-209) минус useAuth/useQuery:
- Приоритет скина:
localStorage['skin'](если id валиден среди BUILT_IN_SKINS + кастомных с сервера) →GET /api/organization/current-skin({skinId, skin}) →'iistwin'. - Кастомные скины: список через
GET /api/organization/skins, токены черезGET /api/organization/skins/:id(только для не-встроенного текущего). - Тема:
localStorage['theme']→prefers-color-scheme+ слушатель. - На
<html>:data-skin,data-theme, классlight|dark; для кастомного скина —<style id="skin-runtime">сgenerateSkinCss(skin, theme). - Ключи localStorage
'skin'/'theme'— те же, что в основном приложении: выбор пользователя синхронизируется между iistwin и /di2 автоматически. - В
client-di2/src/main.tsx:<Router base="/di2"><Di2ThemeProvider><App/></Di2ThemeProvider></Router>.
2.3 Перевод DI2 на полный формат токенов
client-di2/tailwind.config.ts: все цветаhsl(var(--x) / <alpha-value>)→var(--x)(как в корневомtailwind.config.ts);borderRadius: хардкод lg/md/sm →var(--radius)+ каскадcalc(var(--radius) - 2px)/-4px(скопировать блок из корневого конфига); добавитьwarning/success/infoиз var(--warning) и т.д. Если DI2 использует alpha-модификаторы семантических цветов (bg-primary/50) — заменить наcolor-mix(in oklab, var(--primary) 50%, transparent)(grep при исполнении).client-di2/src/index.css:- Заменить собственные
:root/.darkHSL-тройки (строки 9-169) на fallback-блоки, скопированные изclient/src/index.css:13-80(iistwin light/dark до гидратации). - DI2-only переменные — переопределить от семантических токенов через
color-mix:--card-border/--popover-border→var(--border);--primary-border,--secondary-border,--muted-border,--accent-border,--destructive-border,--sidebar-primary-border,--sidebar-accent-border→color-mix(in oklab, var(--X) 82%, black);--button-outline→color-mix(in oklab, var(--primary) 35%, transparent);--badge-outline→color-mix(in oklab, var(--muted-foreground) 35%, transparent);--elevate-1/2→rgba(0,0,0,.05/.08)(в.dark— светлые значения). Убрать--opaque-button-border-intensityиhsl(from ...)relative-color вычисления.
- Заменить собственные
client-di2/index.html:lang="ru"; убрать оба preconnect и мега-link Google Fonts (23 семейства) → оставить только Inter (+DM Mono) как вclient/index.html:23; viewport — убратьmaximum-scale=1, добавитьinteractive-widget=resizes-content(паттерн фикса 2026-08-26).- Шрифты приходят из скина:
generateSkinCssэмитит--font-sans/--font-mono— в index.css DI2 удалить жёсткие--font-sans: Open Sans.
2.4 Проверка этапа
npm run check, npm run build; визуально: /di2 рендерится, светлая/тёмная тема переключается; смена скина в /settings/appearance основного приложения меняет вид /di2 после обновления (проверить data-skin на <html>).
Этап 3 — Визуальный рестайл (точечный)
- Графики recharts: заменить hex-палитры (77 вхождений:
#2563ebи т.п. в income/expenses-overview, debt, calculations, planning, dashboard, heatmap, salary) наvar(--chart-1..5)для палитр иvar(--success)/var(--destructive)/var(--warning)для семантики. Recharts принимает CSS-переменные в fill/stroke. - Хардкод Tailwind-цветов (133 вхождения
text-red-500,bg-amber-50,text-green-600и т.п.): заменить на семантические классы (text-destructive,text-success,bg-warning/10+ парныеdark:где отсутствуют). Механическая замена по всем страницам client-di2. - Таблицы по UI Style Guide:
client-di2/src/components/ui/table.tsx— шапкаbg-muted/50 text-[12px] font-medium text-muted-foreground, строкиh-[var(--table-row-height,40px)], hoverbg-muted/40, ячейкиpx-3(как в основном клиенте). Density из Темы 2.0 подхватится автоматически. - Контролы: скопировать
client/src/components/fields/field-styles.tsвclient-di2/src/lib/field-styles.ts(зависит только отcn), применитьfieldControlClassв кастомных компонентах DI2 (column-filter, draggable-column-list, copyable-cell) — density/control-вид скина применится. - app-sidebar: активный пункт через
sidebar-accent(уже так), убраить лишние отступы, привести брендинг-блок к нашему виду (оставить «ОмегаПром / Финансы»). - Мобильная нижняя панель — новый
client-di2/src/components/bottom-nav.tsxпо образцуclient/src/components/finance/FinanceBottomNav.tsx(удаляемого — скопировать логику до удаления): три кнопки «Обзор» (дашборд), «Разделы» (dropdown: Доходы, Расходы, Дебиторка, Расчёты, Планирование, Зарплата, Аудит, Прибыльность, Тепловая карта), «Настройки»; видна только<md, контент получаетpb-20 md:pb-0, сайдбар на мобильном скрывается (hidden md:flexна обёртке + отключить Sheet-триггер). Переходы — wouter Link (base-aware). - Переключатель темы в хедере DI2 (App.tsx): кнопка Sun/Moon, пишет
localStorage['theme']и дергает перерендер Di2ThemeProvider (через кастомное событие/контекст — проще: Di2ThemeProvider экспортирует контекст с toggleTheme, как основной ThemeProvider). - Хедер/страницы: page-gutter через
var(--page-gutter, 1rem)где уместно; крупные заголовки страниц —text-lg font-semibold(как основной клиент).
Этап 4 — Проверка, деплой, документация
npm run check(оба tsc) +npm run build(main → di2 → server) — чисто.- Авторизованный smoke прод-API DI2:
/api/di2/dashboard,/api/income/tree-summary,/api/debt/analytics,/api/connection,/api/audit/history— 200. - После деплоя:
/finance,/salary,/income→ 404/редирект (страниц нет); «Финансы» в Sidebar/MobileBottomNav открывает/di2;/di2светлая+тёмная, скин организации применяется; мобильная нижняя панель на телефоне. - Документация: AGENTS.md — удалить устаревшие разделы старого модуля (мобильная навигация финансов, деревья категорий, баг дебиторки, Google-флоу
/api/finance/google/*) и дописать раздел про /di2 (мост тем, Di2ThemeProvider, нижняя панель); новая Obsidian-заметкаFeatures/2026-09-0X — Finance module replaced by Data-Insight2 subapp.md+ ссылка в Agent Guide Index; пометить устаревшей заметку2026-09-05 — Finance debt calculations audit and Data-Insight2 migration. - Ручной шаг пользователя (вне кода): Google Cloud Console — в OAuth-клиент добавить redirect URI
https://iistwin.ru/api/auth/google/callback(нужен для синхронизации зарплаты из Google Sheets в DI2; старый/api/finance/google/callbackудаляется вместе с модулем).
Риски и ограничения
- Удаление
lib/format.ts/useFinance.ts— только после grep-подтверждения отсутствия внешних импортов. /api/finance-settings,/api/audit/status, customer-sync/contract-sync,/api/finance/google/*умирают с модулем — потребителей кроме удаляемого UI нет (по разведке).- Docker-compose не меняется → бэкап serverfix не нужен; миграций БД нет (iistwin-БД не трогаем; внешняя финансовая БД — только чтение + override-таблицы DI2, которые уже созданы).
- Если замена хардкод-цветов в графиках сломает читаемость в тёмной теме — вернуть семантические пары вручную (проверить глазами ключевые страницы).