- 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)
91 lines
15 KiB
Markdown
91 lines
15 KiB
Markdown
# План: удаление старого модуля финансов + рестайл DI2 под дизайн iistwin
|
||
|
||
Репозиторий: `C:\1Ai\1iistwin\1Project` (main, автодеплой через push). Субапп DI2 уже в проде на `/di2` (server/finance-di2 + client-di2).
|
||
|
||
Решения пользователя (зафиксированы): /salary удалить и вести на /di2; рестайл = мост токенов + точечные правки (без замены 45 shadcn-компонентов); мобильная навигация = нижняя панель.
|
||
|
||
---
|
||
|
||
## Этап 1 — Удаление старого модуля финансов
|
||
|
||
### 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 файлов).
|
||
2. **Шедулер аудита**: создать `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 НЕ добавлять.
|
||
3. **Персистентность истории аудита**: в `server/finance-di2/audit-agent.ts` добавить загрузку/запись `data/finance-audit-history.json` (порт логики из старого audit.ts): загрузка при старте модуля, append после каждого `runIntegrityAudit`. Сейчас история в памяти и теряется при рестарте.
|
||
4. **Регистрация**: `server/routes/index.ts` — убрать `import { registerFinanceRoutes }` (стр. 33) и вызов (стр. 116), оставить `registerDi2Routes`.
|
||
5. **Env сервера** (проверка, не код): DI2 Google-флоу использует `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` — проверить наличие в `.env` сервера; старый `FINANCE_GOOGLE_REDIRECT_URI` больше не нужен.
|
||
|
||
### 1.2 Клиент (основное приложение)
|
||
1. Удалить: `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` — все внешние импорты должны отсутствовать (по разведке используются только удаляемыми файлами — перепроверить!).
|
||
2. `client/src/index.css` — удалить блок `.hover-elevate`/`.hover-elevate-2` (строки ~343-374, использовался только finance DataTable).
|
||
3. `client/src/App.tsx`:
|
||
- Убрать lazy-импорты finance-страниц (строки 61-72) и роуты `/finance`, `/finance/debt`, `/finance/calculations`, `/finance/audit`, `/income*`, `/expenses*`, `/salary` (строки 117-120, 187-200).
|
||
- ROUTE_TITLES: убрать записи этих путей.
|
||
4. Навигация:
|
||
- `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`.
|
||
5. **Не трогать**: сиды прав `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 на полный формат токенов
|
||
1. `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 при исполнении).
|
||
2. `client-di2/src/index.css`:
|
||
- Заменить собственные `:root`/`.dark` HSL-тройки (строки 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 вычисления.
|
||
3. `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).
|
||
4. Шрифты приходят из скина: `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 — Визуальный рестайл (точечный)
|
||
|
||
1. **Графики 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.
|
||
2. **Хардкод Tailwind-цветов** (133 вхождения `text-red-500`, `bg-amber-50`, `text-green-600` и т.п.): заменить на семантические классы (`text-destructive`, `text-success`, `bg-warning/10` + парные `dark:` где отсутствуют). Механическая замена по всем страницам client-di2.
|
||
3. **Таблицы по 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)]`, hover `bg-muted/40`, ячейки `px-3` (как в основном клиенте). Density из Темы 2.0 подхватится автоматически.
|
||
4. **Контролы**: скопировать `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-вид скина применится.
|
||
5. **app-sidebar**: активный пункт через `sidebar-accent` (уже так), убраить лишние отступы, привести брендинг-блок к нашему виду (оставить «ОмегаПром / Финансы»).
|
||
6. **Мобильная нижняя панель** — новый `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).
|
||
7. **Переключатель темы** в хедере DI2 (App.tsx): кнопка Sun/Moon, пишет `localStorage['theme']` и дергает перерендер Di2ThemeProvider (через кастомное событие/контекст — проще: Di2ThemeProvider экспортирует контекст с toggleTheme, как основной ThemeProvider).
|
||
8. Хедер/страницы: page-gutter через `var(--page-gutter, 1rem)` где уместно; крупные заголовки страниц — `text-lg font-semibold` (как основной клиент).
|
||
|
||
---
|
||
|
||
## Этап 4 — Проверка, деплой, документация
|
||
|
||
1. `npm run check` (оба tsc) + `npm run build` (main → di2 → server) — чисто.
|
||
2. Авторизованный smoke прод-API DI2: `/api/di2/dashboard`, `/api/income/tree-summary`, `/api/debt/analytics`, `/api/connection`, `/api/audit/history` — 200.
|
||
3. После деплоя: `/finance`, `/salary`, `/income` → 404/редирект (страниц нет); «Финансы» в Sidebar/MobileBottomNav открывает `/di2`; `/di2` светлая+тёмная, скин организации применяется; мобильная нижняя панель на телефоне.
|
||
4. Документация: 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`.
|
||
5. **Ручной шаг пользователя** (вне кода): 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, которые уже созданы).
|
||
- Если замена хардкод-цветов в графиках сломает читаемость в тёмной теме — вернуть семантические пары вручную (проверить глазами ключевые страницы).
|