# Task 2 — Клиентский субапп Data-Insight2 (client-di2): отчёт ## Статус: DONE ## Что создано ### `client-di2/` — копия фронтенда DI2 - `src/` (78 файлов: App.tsx, pages/, components/ + ui/, hooks/, lib/, index.css), `public/favicon.png`, `index.html` — как есть, кроме правок ниже. - `shared/schema.ts` ← копия `tmp/Data-Insight2/shared/schema.ts`. - `tsconfig.json` — копия DI2-tsconfig: `include: ["src/**/*", "shared/**/*"]`, `target: ES2022` (сначала забыл — tsc ругался на ES5, добавлено), `strict`, paths `@/*`/`@shared/*` → `./src`, `./shared`, `types: ["node", "vite/client"]`, `tsBuildInfoFile: ../node_modules/typescript/tsbuildinfo-di2` (бриф предлагал `./node_modules/...` — функционально то же, но без мусорной папки client-di2/node_modules). - `tailwind.config.ts` ← копия DI2-конфига (у DI2 свои токены/радиусы, отличные от iistwin!). Content-глобы: `./client-di2/index.html`, `./client-di2/src/**/*` — **относительно cwd сборки** (npm-скрипты всегда из корня). Вариант `{ relative: true, files: [...] }` относительно конфига проверен — глобы не матчились, утилиты вырезались (css 11 kB, 0 классов `.flex/.grid/...`); с cwd-глобами — 88 kB, утилиты на месте. - `src/index.css` — добавлена директива `@config "../tailwind.config.ts";` в начало: без неё postcss-плагин tailwindcss от cwd (корень репо) взял бы корневой `tailwind.config.ts` iistwin и вырезал бы стили DI2. - `src/main.tsx` — обёртка `` (wouter 3.10, проп `base` поддерживается — проверено в types/router.d.ts и фактической версии node_modules). - `index.html` — favicon сделан относительным (`href="favicon.png"`), чтобы не 404 на проде; script-src `/src/main.tsx` — дев-режим, vite переписывает при сборке. ### `vite.config.di2.ts` (корень) Минимальный конфиг: `react()` без replit-плагинов, `base: "/di2/"`, `root: client-di2`, alias `@`/`@shared` → client-di2, `build.outDir: dist/public-di2`, `emptyOutDir: true`. `__BUILD_SHA__` в client-di2 не используется (grep пуст) — define не добавлял. server/fs-секций нет. ### `package.json` - `build`: `vite build && vite build -c vite.config.di2.ts && esbuild server/index.ts ...` (main client → di2 → server bundle); - `build:di2`, `check`: `tsc && tsc -p client-di2/tsconfig.json`, `check:di2`. - **Новых dependencies НЕ добавлено** — все пакеты DI2 (wouter, @tanstack/react-query, radix-набор, recharts, cmdk, vaul, embla, input-otp, date-fns, xlsx, tailwindcss-animate, @tailwindcss/typography и пр.) уже есть в package.json iistwin (проверено скриптом). ### Прочее - `Dockerfile` — менять не нужно: `RUN npm run build` подхватит новую сборку, `COPY --from=builder /app/dist ./dist` скопирует и public-di2. - `.gitignore` — `dist` уже игнорируется, не тронут. - **Навигация финансов**: desktop-список — `client/src/components/finance/FinanceSidebar.tsx` (пункт после navItems, plain `` с иконкой ExternalLink, стили как у Link, поддержка collapsed); мобильная — `client/src/components/finance/FinanceBottomNav.tsx` (пункт в DropdownMenu «Разделы» после категорий, тот же паттерн ``). Финансовые страницы оборачиваются в FinanceLayout → ссылка появляется на всех экранах финансов. ### Типовые фиксы предсуществующих ошибок DI2 (DI2 под tsx никогда не прогонял tsc) - `calculations.tsx`: литеральные массивы полей → `as any[]` (2 места), `addCumulativeVat(grouped as any, ...)` — дженерик `groupMonthlyData` выводил T как `{period: string}`. - `income-categories.tsx` / `expense-categories.tsx`: в типы mutationFn добавлен `ruleLogic?: "and" | "or"` (UI его отправляет, типы отставали). - `income.tsx` / `expenses.tsx` / `expenses-overview.tsx`: `new Set(...)` для filterableSet (строка не assignable в Set литерального union). Рантайм-поведение не менялось. ## Проверки 1. `npm run check` — **чисто** (оба tsc: основной + client-di2). 2. `npm run build` — **проходит**: main client (24.9s) → di2 (6.7s, `dist/public-di2/index.html` 2.03 kB, css 88.3 kB, js 1363 kB) → esbuild `dist\index.js` 2.7mb. 3. `ls dist/public-di2/` — `index.html`, `assets/index-*.css`, `assets/index-*.js`, `favicon.png`. 4. `grep -o 'src="[^"]*"' dist/public-di2/index.html` → `src="/di2/assets/index-D5btRW62.js"`, css `href="/di2/assets/index-CLXURD96.css"` — префикс `/di2/` на месте. 5. Стыковка с Task 1: серверный бандл `dist/index.js` лежит рядом с `dist/public-di2` → `path.resolve(import.meta.dirname, "public-di2")` из Task 1 сходится. Статика `/di2` отдастся через express.static + fallback sendFile index.html (глубокие ссылки wouter работают). ## Открытые вопросы / опасения - Рантайм-smoke в браузере не выполнялся (по брифу достаточно check/build + контроль путей). Первый реальный прогон — на проде после деплоя; если DI2-страница откроется неавторизованной — auth-gate из Task 1 редиректа не делает (вернёт 401 JSON на API, SPA покажет ошибки БД) — стоит глянуть глазами. - DI2-SPA шрифты грузит с Google Fonts (как в оригинале) — на проде зависит от доступности fonts.googleapis.com. - CSS предупреждение «A PostCSS plugin did not pass the `from` option» при di2-сборке — косметическое, на выхлоп не влияет. - Размер di2-бандла 1.36 МБ без код-сплиттинга (как в оригинале DI2) — принято как есть.