Commit Graph

173 Commits

Author SHA1 Message Date
693834ac27 fix(chat): настоящая первопричина дёрганья — remount чата из-за inline-компонента ChatSection + первый кадр не у низа
- TaskDetail: ChatSection объявлен inline в теле рендера → новый тип компонента
  на каждый рендер → React перемонтировал весь чат при ЛЮБОМ ре-рендере TaskDetail
  (SSE-событие, refetch задачи, возврат на вкладку): скролл сбрасывался вверх,
  пользователь видел «старые сообщения → прыжок к последним». Фикс: useCallback
  со стабильной идентичностью (тот же паттерн, что у мобильного accordion).
- TaskChat/useChatController: скролл к низу при открытии чата — useLayoutEffect
  СИНХРОННО ДО paint (первый кадр уже у низа) + докрутка первого кадра, когда
  сообщения пришли после монтирования без кэша.
2026-09-21 21:33:26 +03:00
34838a302d feat(chat): онлайн-доставка как в Telegram — watchdog SSE, тихий скролл, персистентный кэш сообщений, невидимое обновление SW
- SSE: серверный heartbeat теперь именованное событие ping (25с) — клиент может отличить живое соединение от мёртвого; клиентский watchdog (45с без активности → принудительный reconnect), reconnect 1с вместо 3с, polling-фолбэк 5с вместо 10с
- Скролл: на скрытой вкладке скролл не трогаем (раньше автоскролл «выбрасывал» пользователя вниз при возврате); возврат на вкладку — pin к низу если был у низа; при чтении истории плавающий индикатор «↓ N новых» (оба чата)
- Персистентный кэш последних 100 сообщений чата в localStorage → initialData: мгновенный рендер после перезагрузки страницы без «Загрузка сообщений...», свежие данные в фоне; gcTime: Infinity
- SW: reload на controllerchange только на скрытой вкладке (обновление незаметно), предыдущая версия кэша сохраняется для ленивых чанков старой страницы; кэш v15
2026-09-21 20:29:13 +03:00
f8d78afd2e feat(auth): retry рефреша при 401 + вкладка «Сессии» в профиле с отзывом устройств 2026-09-21 18:13:47 +03:00
2f9d049718 fix(chat): catch-up сообщений при возврате на вкладку; fix(sw): фокус на открытой вкладке вместо навигации всех; fix(history): числа не резолвятся в имена пользователей 2026-09-21 17:03:01 +03:00
1f878b61a1 feat(chat): персистентные черновики сообщений — текст переживает перезагрузку страницы 2026-09-21 15:14:30 +03:00
e3f0ebfb89 feat(fields): мягкое удаление полей формы — архив удалённых с восстановлением и блок «Удалённые поля» в задаче 2026-09-21 14:35:45 +03:00
d5f2925886 feat(tags): полный тинт строк/карточек, цветное значение поля вместо чипа в шапке; защита от дубль-клика при открытии задач 2026-09-21 14:35:45 +03:00
c1463c71a1 feat(fields): цветные опции select — глобальная подсветка задач (главная, реестр, канбан, шапка) 2026-09-21 11:13:44 +03:00
aa316141be feat(reminders): получатели в отложенных/входящих, приватные сообщения чата, календарь, бейдж уведомлений 2026-09-21 10:26:53 +03:00
17e6ebf412 fix(ui): удалён FAB закладок — перекрывал поле ввода чата в PWA
FAB (z-40, fixed right-bottom) ложился поверх композера чата и кнопок отправки.
Добавление закладки уже доступно: меню значка «Закладки» в MobileBottomNav
(мобильный) и сайдбар (десктоп) — функционал дублировался.
2026-09-21 08:24:08 +03:00
267af046a3 fix(chat): автоскролл задач только у низа, картинки с фикс. размером, упоминания по @ и 3+ буквам имени/фамилии
- TaskChat: atBottomRef-гвард (scrollHeight-scrollTop-clientHeight<80) — чужие
  сообщения больше не дёргают чат вниз, когда пользователь читает историю;
  свои сообщения и смена задачи скроллят как раньше
- ChatAttachments: img w-180 h-180 object-cover вместо max-* — высота строки
  не меняется при догрузке, measureElement виртуализатора не дёргает список
- упоминания: голый @ открывает список сразу; текстовый триггер от 3 букв;
  сервер ищет ILIKE по имени/отчеству/фамилии/email и отдаёт всех при пустом q;
  починен мёртвый fallback-резолв (Response без .json())
- groupByDate/flattenGroupedMessages обёрнуты в useMemo
2026-09-17 22:00:10 +03:00
d0112657b8 fix(auth): живая сессия на устройстве — рефреш на холодном старте, grace-окно ротации, без логаута при временных ошибках
- /api/auth/me при 401 делает одну попытку refresh (раньше refresh-retry был
  отключён для всех /api/auth/* — холодный старт после 15 мин простоя = логаут)
- ротация: повтор старого refresh-токена в пределах RACE_TOLERANCE_MS (5 мин)
  выдаёт новую пару от successor вместо deny; cookie больше не стираются при
  stale-токенах (разлогинивались все вкладки устройства); reuse после окна —
  revoke family как раньше (тесты обновлены)
- refreshSession различает 401/403/400 (сессия мертва) vs 429/5xx/сеть
  (transient — нет logout, отложенный retry)
- единый рефреш: useAuth keep-alive и useOfflineSync переведены на refreshSession;
  межвкладочная дедупликация через BroadcastChannel('auth-refresh')
- refreshLimiter: keyGenerator buildRateLimitKey (per-user, не общий IP-бакет офиса)
2026-09-17 21:59:51 +03:00
c1d5b1cbee fix(gps): остановлен 429-шторм — throttle SSE gps_asset_moved (3 сек/asset) и клиентских инвалидаций (60 сек), polling позиций 60 сек, статусов 5 мин
Каждая точка ingest (~1/сек) broadcast'илась SSE всем клиентам орг., клиент
на каждое событие делал invalidateQueries без throttle → 2+ GET/сек → исчерпание
per-user бакета apiLimiter (1500/15мин) → все API устройства 429 (формы не грузились).
Модель теперь как у нормальных трекеров: точки пишутся в БД, карта читает по интервалу.
+4 unit-теста троттла (server/gps/sse-throttle.ts)
2026-09-17 21:59:34 +03:00
e4f9a1992a feat(automations): ctx.tasks.scheduleStatusChange — отложенная смена статуса (воркер 5с, отмена при ручном переходе) 2026-09-17 16:38:56 +03:00
c1fe1c4e02 fix(registry): непрозрачный фон sticky-шапки, отказ от table-fixed
- шапка: [&_th]:bg-background вместо полупрозрачного bg-muted/50 — иначе сквозь
  sticky-шапку проступают строки и она нечитаема (в plain-варианте фона не было вовсе)
- ресайз: table-fixed + w-full таблицы + w-max wrapper давали циклическую зависимость
  ширин — колонки без заданной ширины схлопывались до нуля при первом drag.
  В auto-раскладке <colgroup> задаёт предпочтительную ширину без схлопывания

check/build чисто, vitest 129/129
2026-09-14 23:54:10 +03:00
e32def495d feat(registry): горизонтальная прокрутка внизу области, sticky-шапка, ресайз колонок, лимит строк в ячейке
- ui/table: пропс wrapperClassName (twMerge), в реестре wrapper = w-max min-w-full
  overflow-visible — переполнение по ширине всплывает в контейнер реестра,
  скроллбар оказывается внизу видимой области, а не под всей таблицей
- TableHeader sticky top-0 (фон tr для plain-варианта скина)
- drag правого края шапки меняет ширину колонки (table-fixed + colgroup,
  min 80 / max 1200 px), двойной клик — сброс; ширины хранятся в localStorage
  per-form (registry-colwidths-<formId>) — у каждого пользователя свои
- настройка «Строк в ячейке» (Авто / 1..8) в диалоге настроек реестра:
  CSS line-clamp на текстовых ячейках, хранится в registry-rowlines-<formId>;
  оценка высоты виртуализатора ниже в однострочном режиме

check/build чисто, vitest 129/129
2026-09-14 23:21:02 +03:00
c8b7741b11 fix(ui): мобильный main стал flex-колонкой — flex-1 PageTransition больше не игнорируется, h-full-цепочки страниц восстановлены
На мобильном (<768px) main#content был блочным: flex-1 обёртки PageTransition
работал вхолостую, высота страницы росла по контенту, весь скролл уходил в main,
а скроллбар широкой таблицы реестра оказывался внизу недостижимой высоты.
Десктопный main уже был flex flex-col (Sidebar.tsx:1687).
2026-09-14 23:08:30 +03:00
5af274cd0f fix(ui): двойной Sidebar на реестре задач и карточке задачи — TaskView/TaskDetail рендерили свой Sidebar поверх AppShell 2026-09-09 08:22:41 +03:00
b94406e057 feat(ui): фаза 7 — self-host шрифты Inter/DM Mono (woff2, precache в SW v13), возвращён focus-visible (кольцо --ring), active:scale для кнопок (фикс мёртвого not-disabled), плавные появление/исчезновение биллинг-оверлея, длительности тостов по шкале токенов 2026-09-08 22:11:32 +03:00
820e8865a5 refactor(chat): фаза 6 — общий useMessagePolling, TaskChat на общий MessageBubble (слоты под task-специфику) и groupByDate, виртуализация мессенджера + автоскролл только у низа, TaskChat без RHF/zod (локальный стейт), анимация появления последнего сообщения; TaskChat 2023→1754 строк 2026-09-08 21:58:03 +03:00
aa8bf6ac33 feat(ui): фаза 5 — Тема 2.0 во всех базовых ui/* (select/textarea/tabs/popover/tooltip/dropdown-menu/checkbox/dialog на рецептах и токенах), data-density/control-variant/button-radius на html, консолидация размеров кнопок (44 места → size=sm), max-h+скролл диалогов 2026-09-08 21:23:25 +03:00
113ed458b6 feat(ui): фаза 4 — скелетоны PageSkeleton/ListSkeleton/CardSkeleton/FormSkeleton (мигрированы ~19 страниц вместо спиннеров), PageTransition на framer-motion (fade 150ms, MotionConfig reducedMotion=user, гейт skinComponents.motion.enabled), StaggerList для списков задач/диалогов/справочников 2026-09-08 21:10:08 +03:00
c4fdfe4eab refactor(ui): фаза 3 — единый AppShell (Sidebar не размонтируется при навигации), Layout стал тонким контейнером, SPA-навигация вместо location.href (уведомления, тосты, TableEditor), scroll restoration, плавное появление биллинг-оверлея 2026-09-08 20:48:28 +03:00
5d219ac794 fix(ui): фаза 2 — стабилизация данных: throttle+invalidate вместо refetch-пачек на SSE, единый useUnreadBadges (polling только при обрыве SSE), TaskChat polling только при потере SSE, one-shot guard в safeLazy, плавная смена скина (transition цветов) 2026-09-08 20:38:01 +03:00
7a98bf7bf3 feat(ui): фаза 1 — шкалы motion/z-index в токенах, boot-скрипт темы в index.html, рецепты компонентов (design-system/recipes.ts) для button/input/badge/card/table, layout.taskDetail в скине, удалён мёртвый CSS (.btn-compact и др.) и зависимость tw-animate-css 2026-09-08 20:26:14 +03:00
c8d7f9b336 fix(auth-ui): тихий фейл 400 при смене пароля, валидация, пароль при создании
PLAN-user-password-fixes.md (инцидент 2026-09-07, user 55/56):
- toServiceError в client auth.service: BadRequestError → {success:false, error}
- полная клиентская валидация пароля в ProfilePasswordRow
- опциональный пароль при создании пользователя (UserModal + POST /api/users)
- onError с toast в PollCard/ReactionBar (этап 4)
- 11 новых тестов (129/129)
2026-09-08 16:42:59 +03:00
0b2319b11d feat(automations): ctx.tasks.sendMessage — сообщение в чат задачи от Системы; userId в triggerData status_changed 2026-09-08 16:31:59 +03:00
42e5ace655 feat(notifications+a11y): центр уведомлений /notifications, skip-link, axe smoke
Шаг 2.5 плана production-готовности:
- страница уведомлений (фильтр, догрузка, прочтение) + ссылки из dropdown/мобильного меню
- общие хелперы notification-utils (дедупликация dropdown)
- skip-link + id=content на main
- axe smoke-тесты (0 critical/serious), исправлены 2 нарушения в новой странице
- 3 новых теста (107/107), i18n отложен по решению пользователя
2026-09-08 09:32:36 +03:00
6e7270d2ee refactor(form-editor): разбиение FormEditor.tsx (4975→337) — шаг 2.2 завершён
Шаг 2.2 (часть 4) плана production-готовности:
- 14 файлов в components/form-editor/ (контроллер, FieldCard, вкладки, диалоги)
- сверка 13/15 блоков байт-в-байт, расхождения объяснены
- warnings 566→542, check/build чисто, vitest 104/104

Итог 2.2: Chat 2696→639, TaskDetail 3278→804, Bots 3928→163, FormEditor 4975→337
2026-09-08 08:57:55 +03:00
95d03aeeff refactor(bots): разбиение Bots.tsx (3928→163) — механическая экстракция
Шаг 2.2 (часть 3) плана production-готовности:
- 16 файлов в components/bots/ (типы, контроллер, диалоги, вкладки, секции)
- сверка 28/28 сегментов дословно, порядок хуков 1:1
- check/lint/build чисто, vitest 104/104
2026-09-08 08:12:35 +03:00
52b583c671 refactor(task-detail): разбиение TaskDetail.tsx (3278→804) — механическая экстракция
Шаг 2.2 (часть 2) плана production-готовности:
- 14 файлов в components/task-detail/ (типы, вкладки, диалоги, шапка, таб-бар,
  мобильный accordion, хук условий полей)
- посимвольная сверка: все несовпадения объяснены (export-префиксы/импорты/пропсы)
- check/lint/build чисто, vitest 104/104
2026-09-08 06:53:25 +03:00
6bbedad291 refactor(chat): разбиение Chat.tsx (2696→639) — механическая экстракция без смены поведения
Шаг 2.2 (часть 1) плана production-готовности:
- 8 файлов в components/chat/: типы, утилиты, MessageBubble, диалоги,
  TypingIndicator, useChatController (логика перенесена дословно)
- неизменность доказана посимвольной сверкой с оригиналом
- check/lint/build чисто, vitest 104/104
2026-09-08 01:29:37 +03:00
75ec0f25bb chore(repo): дедуп formatAmount, пины образов, дефолты MinIO, доля any
Шаги 2.3 и 2.4 плана production-готовности:
- lib/format.ts formatAmount вместо 2 локальных копий
- пины тегов minio/rclone/ollama/traccar/pgvector + osrm по digest
- minioadmin без дефолта (env required, dev-подсказки в .env.example)
- 28 any в auto-transitions/shared типизированы из @shared/schema
- удалён _tablefield_orig.txt
- check/lint/build чисто, vitest 104/104
2026-09-08 01:07:55 +03:00
5750a3106f security(auth): anti-enumeration, returnTo-валидация, лимит попыток логина
Шаг 2.1 плана production-готовности:
- единый 401 при любом отказе логина + constant-time bcrypt
- forgot-password: идентичный ответ независимо от существования email
- sanitizeReturnTo (open redirect fix в /api/documents/generate-link)
- 10 неудачных попыток → блок 15 мин (in-memory, аудит)
- DATABASE_SSL_REJECT_UNAUTHORIZED env (дефолт true)
- доделка 0.7: статичные тексты в llm-providers/rag/finance-di2
- 8 новых тестов (104/104)
2026-09-08 00:46:57 +03:00
b1a3461c33 feat(onboarding+modules): чеклист новой org, MedSchedule за флагом, удаление Capacitor
Шаги 1.9 и 1.10 плана production-готовности:
- OnboardingChecklist (форма→коллеги→задача) на главной, фикс EmptyState в Tasks
- organizations.medschedule_enabled (миграция 0081), гейт модуля по флагу
- Capacitor-стек удалён (5 зависимостей, push-notifications.ts, capacitor.config)
- check/lint/build чисто, vitest 96/96
2026-09-08 00:27:17 +03:00
abd5d4b65f chore(ci+logs): eslint в CI, npm audit, logger с уровнями, retention error_logs
Шаги 1.6 и 1.7 плана production-готовности:
- eslint flat-config (баг-ловушки, легаси warn), lint блокирующий в pr-check
- npm audit --audit-level=high в CI (отчёт)
- фикс реального бага: условный useRef в TaskTitleInline
- server/utils/logger.ts (LOG_LEVEL/LOG_FORMAT) в 5 горячих местах
- error_logs retention 30 дней (worker), redactSensitive в captureErrorLog
- 0 errors lint, vitest 96/96
2026-09-08 00:11:38 +03:00
6eaca79cc6 perf(ui): виртуализация реестра задач и чата (@tanstack/react-virtual)
Шаг 1.3 плана production-готовности:
- TaskRegistry: виртуальные строки с динамической высотой, порог 30
- TaskChat: виртуальные сообщения, порог 100, автоскролл/scrollToMessage
- выделение/сортировка/поиск/inline-редакторы работают по полному массиву
- check/build чисто, vitest 96/96
2026-09-07 23:46:42 +03:00
701af06290 feat(billing): PaymentProvider + Mock, self-service пополнение баланса
Шаг 0.13 плана production-готовности:
- интерфейс PaymentProvider (createPayment/verifyWebhook/getPaymentStatus)
- MockPaymentProvider с настоящим webhook (sha256-подпись, идемпотентность)
- applySucceededPayment: транзакция с guard, credit, авто-снятие billingBlocked
- роуты payments + confirm-test (только mock) + публичный webhook
- Billing.tsx: Пополнить/Подтвердить (тест), фикс setLocation в рендере
- миграция 0080 billing_payments, 13 новых тестов (78/78)
2026-09-07 22:45:30 +03:00
a27766a38b perf(tasks): лимиты/пагинация /api/tasks и дельта-синк в SQL
Шаги 0.10 и 0.9 плана production-готовности:
- дефолт limit 50, кап 200; списковые ответы без description
- курсорная пагинация (updated_at, id) + nextCursor
- since-фильтр дельта-синка перенесён в SQL (updated_at >= since)
- embedding pre-count через COUNT(*) вместо выгрузки миллиона строк
- 11 новых юнит-тестов (56/56 зелёные)

Инвариант поиска сохранён: search-ветка /api/tasks?search= не изменена
2026-09-07 22:01:25 +03:00
a2b231d876 fix(history): московское время и ФИО вместо user ID в истории задачи 2026-09-07 16:55:51 +03:00
66f4e5ef53 DI2: удаление старого модуля финансов — пул БД и аудит перенесены в finance-di2
- 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)
2026-09-06 21:53:47 +03:00
23ed1ecf3d DI2 внедрение, таск 2: клиентский субапп Data-Insight2 (client-di2)
- client-di2/: копия фронтенда DI2 + shared/schema.ts, main.tsx обёрнут в wouter Router base=/di2, favicon относительный
- client-di2/tailwind.config.ts (свои токены DI2) подключён через @config в index.css — иначе postcss взял бы корневой конфиг iistwin
- vite.config.di2.ts: base /di2/, root client-di2, outDir dist/public-di2
- package.json: build/check через && включают сборку и typecheck di2 (build:di2, check:di2), новых зависимостей нет
- Навигация финансов: ссылка «Data-Insight (полная версия)» → /di2 (target=_blank) в FinanceSidebar и FinanceBottomNav
- Типовые фиксы предсуществующих ошибок DI2 (casts, ruleLogic в mutation-типах, Set<string>)
2026-09-06 19:46:28 +03:00
a60a75eda0 feat(profile): read-first редизайн страницы пользователя, per-field как в задачах
- Раскладка: шапка-профиль → Основное/Контакты → Роль и доступ/Поля+Замещение → вторичные разделы чипами
- ProfileFieldRow: двойной клик/карандаш, редактор в слоте, Esc, optimistic-кэш, все типы полей
- Замещение: надпись + ⊕ popover, список под строкой
- Удалены RHF-форма, zod-схема и кнопки «Сохранить»
- server: PATCH /api/users/:id (частичное обновление, admin-градация, аудит)
- server: history-number в REST PATCH пишет историю + пересчёт средней
2026-09-06 18:47:05 +03:00
ddc2bd9491 Поля профиля с options.readOnly: запрет редактирования «Оценки» и «Средней оценки»
- UI: read-only поля отображаются текстом (у history-number остаётся кнопка графика),
  исключены из сохранения; кнопка «Сохранить» скрыта, если все поля вкладки read-only
- Сервер: POST/PATCH field-values профиля отклоняют запись в поле с options.readOnly (400);
  автоматизации пишут через storage напрямую и не затрагиваются
- Данные: options.readOnly=true у полей «Оценка» и «Средняя оценка»
2026-09-06 15:08:23 +03:00
ec86fa4c11 Рейтинг сотрудников: history-number поле = последняя оценка, среднее за месяц — в поле options.avgFieldCode
- setUserProfileFieldValueFromAutomation: для history-number значением поля становится переданное число (последняя оценка), а не среднее
- recomputeUserFieldMonthlyAverage: среднее за текущий месяц пишется в поле, указанное в options.avgFieldCode history-number поля (если не задано — в само поле, обратная совместимость)
- Автоматизация «Оценка сотрудникам» (форма 11): сумма трёх числовых полей «Оценка 1/2/3» вместо одного select-поля
- Документация sandbox обновлена во всех четырёх местах (UI, MCP, Obsidian, AGENTS.md)
2026-09-06 11:55:15 +03:00
a7a14d947a Финансы: деревья категорий как в Data-Insight2 — группировка авто/ручные, свёрнуто по умолчанию, полосы сумм, нераспределённые 2026-09-06 09:53:45 +03:00
4b34a4bea5 Финансы: мобильная нижняя навигация (Обзор / Разделы / Настройки) вместо боковой панели на телефоне 2026-09-05 17:59:34 +03:00
7a8db03163 Финансы: дебиторка (vw_debt_active), расчёты НДС/налога на прибыль, аудит целостности с автопланировщиком, синхронизация зарплат из Google Sheets, миграция настроек Data-Insight2 2026-09-05 15:07:36 +03:00
d2c993b613 fix: мультивыбор user/роль — мгновенное обновление выбора (галки/чипы) в карточке задачи 2026-09-05 12:33:14 +03:00
ba723aa0b7 feat: ctx.users.recalcFieldAverage + документация автопересчёта среднего history-number 2026-09-04 20:14:51 +03:00