Система управления проектами
v0.98.0
Экосистема
Управление проектами, задачами и документами в одном окне
Платформа для координации работы между командами и клиентами: направления, проекты, задачи, документы и безопасные гостевые ссылки под контролем
Что это за платформа
Экосистема — система управления проектами, под которой работает команда ООО «Студия ИПД»: от постановки задачи до сдачи результата клиенту.
Направления и проекты
Управление проектами с привязкой клиентов, владельцев и направлений — всё в одном окне.
Задачи с шагами
Создание и отслеживание задач с поэтапными шагами выполнения и перепиской.
Безопасные гостевые ссылки
Токены для безопасного доступа гостей к проектам и документам без регистрации.
Документы и файлы
Хранение и версионирование документов, вложения с контролем видимости.
Синхронизация данных
Обмен данными с внешними системами через API и планировщик синхронизации.
Панель администратора
Аналитика, журнал действий, управление пользователями и настройками.
Информация о системе
Технологическая база, на которой работает платформа.
ПлатформаЭкосистема · v0.98.0
Версия PHP8.4.27
СтекPHP 8.4.27 + Laravel 13.19.0 + Vite (Tailwind 4)
База данныхMySQL
АвторизацияLaravel Auth + Remember Me
СерверHerd · my.ipd.test
СредаProduction
Платформу развивает ООО «Студия ИПД»
Это не «самоделка на коленке», а официально работающая система под управлением юридического лица. Рядом с администрированием — подтверждённая экспертиза в партнёрских программах.
Официальное юридическое лицо
Платформа развивается в рамках ООО «Студия ИПД». Направление — разработка, администрирование и автоматизация цифровых сервисов.
Алексей Гапеев — сертифицированный партнёр amoCRM
Открыть ↗
ООО «Студия ИПД» — сертифицированный партнёр amoCRM
Открыть ↗
Свежий сертификат партнёра amoCRM: Алексей Гапеев и ООО «Студия ИПД» подтвердили официальную экспертизу в программе amoSTART — сертификат получен 31 июля 2026. Управление платформой и администрирование amoCRM — соседние направления одной команды: можно заказать и то, и другое.
Связаться с нами
Пишите в Telegram, звоните или отправляйте email — отвечаем быстро.

Алексей Гапеев
Руководитель и ведущий разработчик платформы
История версий
Как развивается платформа: что сделано, для чего и какую ценность это даёт.
v0.98.0
19.10.2026
MINOR
Текущая
Предупреждения о запланированной рассылке в Telegram
ТЗ 289 «Контроль рассылки». 19 октября 2026 года с продакшена my.ipd.by ушла массовая рассылка, о которой администратор узнал постфактум: письма формируются по расписанию и уходят сами (буфер PreparedEmail и кампании Campaign), а отдельного предупреждения о запуске не было. Теперь платформа заранее сообщает администратору в Telegram: за 24 часа, 4 часа, 1 час и 15 минут до отправки ближайших писем приходит одно сводное сообщение со числом писем и списком получателей и тем. Каждое пороговое окно отправляется один раз (дедупликация в таблице mailing_alerts), повторные запуски не дублируют предупреждения. Версия v0.98.0.
Что сделано
- Предупреждение до отправки - Администратору в Telegram (admin-бот) приходит сообщение за 24 часа, 4 часа, 1 час и 15 минут до отправки ближайших писем: сколько писем уйдёт, когда и кому — получатели и темы. Окно выбирается по остатку времени до самого раннего письма, поэтому «за час» не придёт раньше, чем письмо окажется в часе от отправки.
- Триггеры — буфер и кампании - Мониторятся оба источника плановой рассылки: готовые письма в буфере (PreparedEmail со статусом queued и scheduled_at) и запланированные кампании (Campaign со статусом scheduled).
- Одно сообщение на окно, без повторов - Один запуск команды собирает все письма, попадающие в пороговое окно, и отправляет одно сводное сообщение; факт отправки фиксируется в mailing_alerts (тип + id + порог), поэтому повторные запуски каждые 5 минут не дублируют предупреждения.
- Только оповещение - Фича ничего не отменяет и не задерживает: рассылка идёт как шла, предупреждение лишь информирует. Управляется флагом MAILING_ALERTS_ENABLED; команда mail:preflight-alerts поддерживает --dry-run — показать, что было бы отправлено, не отправляя.
Проверено
- Тесты - tests/Feature/MailingPreflightAlertsTest.php — 7 тестов: порог выбирается по остатку времени, письма вне окна не попадают, одно сообщение на окно, повторный запуск не дублирует, отключённый флаг молчит, --dry-run ничего не отправляет и не помечает, сообщение содержит получателя и тему. Полный прогон платформы: 478 тестов, 477 пройдено, 1 условно пропущен (2501 утверждение).
- Схема versions - Восстановлены пропущенные колонки таблицы versions (released_at, notes): при релизе 0.97.1 их завели только в сидер, из-за чего сид падал на чистой базе и часть тестов не запускалась. Теперь сид и весь набор тестов зелёные.
- Регрессия ссылок - Название расписания на странице расписаний оформлено как ссылка по правилу ТЗ 280 (color: var(--link) + text-decoration: underline) — TextLinkVisibilityTest проходит.
- Версия платформы - Поднята до v0.98.0: config/app.php, APP_VERSION в .env и .env.example; VersionSeeder — v0.98.0 актуальная. VersionFeatureTest и SeederKeepsWorkingDataCleanTest проходят.
v0.97.1
Открыть версию →
v0.96.3
Открыть версию →
v0.96.2
06.10.2026
PATCH
Категории проектов: постоянные адреса разделов и статусов
ТЗ 284 «Категории проектов». Раздел проектов жил на одном адресе /admin/projects с параметром ?scope=all|clients|mine — состояние страницы нельзя было запомнить, отправить в ссылке или открыть в новой вкладке. Теперь у каждой категории свой постоянный адрес: /admin/projects (вход — клиентские активные, как раньше), /admin/projects/clients|all|mine и /admin/projects/clients|all|mine/active|completed|all (например /admin/projects/clients/completed — завершённые клиентские). Сверху два ряда вкладок: разделы и статусы, каждый со своим счётчиком; счётчики считаются в рамках выбранного раздела до применения статуса, поэтому «Клиентские · завершённые · 7» честно показывает, сколько станет после клика. Старые ссылки ?scope=X ведут на постоянный адрес того же раздела, поиск/сортировка/пагинация остаются параметрами и не меняют адрес. Версия v0.96.2.
Что сделано
- Постоянные адреса - /admin/projects — вход (клиентские активные), /admin/projects/clients|all|mine — раздел, /admin/projects/<раздел>/<статус> — раздел со статусом: active (все, кроме закрытых — completed+archived), completed (завершённые и архив), all (все статусы). Адрес полностью описывает страницу — его можно запомнить, отправить и открыть в новой вкладке.
- Два ряда вкладок - Первый ряд — разделы (Клиентские / Все проекты / Мои), второй — статусы (Активные / Завершённые / Все) с числом проектов рядом с подписью. Счётчики статусов считаются в рамках выбранного раздела, счётчики разделов — в рамках выбранного статуса; каждая ссылка собирается из текущих фильтров, поэтому ни один выбор не сбрасывает другой.
- Заголовок и пустой список - Заголовок страницы называет выбранное состояние («Проекты клиентов · завершённые»), а пустой список объясняет, чего именно нет («В этом разделе нет завершённых проектов») вместо безликого «Ничего не найдено».
- Старые ссылки не ломаются - ?scope=all и другие прежние адреса редиректом уходят на постоянный адрес того же раздела, остальные параметры (поиск, сортировка, размер страницы) сохраняются; неизвестный раздел или статус — 404.
Проверено
- Тесты - tests/Feature/AdminProjectAddressesTest.php — 8 тестов: корень открывает клиентские активные, адреса разделов и статусов работают, вкладки — ссылки без «?», фильтры едут параметрами, счётчики соответствуют разделу, неизвестные адреса — 404, ?scope ведёт на постоянный адрес. Обновлён AdminProjectsScopeTest. Полный прогон платформы: 471 тест, 470 пройдено, 1 условно пропущен.
- Версия платформы - Поднята до v0.96.2: config/app.php, APP_VERSION в .env и .env.example; VersionSeeder — v0.96.2 актуальная. VersionFeatureTest и SeederKeepsWorkingDataCleanTest проходят.
v0.96.1
06.10.2026
PATCH
Задачи клиентов по умолчанию и переключатель «Клиентские / Все / Мои»
ТЗ 283 «Задачи клиентов». Страница /admin/tasks-platform по умолчанию открывалась списком «моих» направлений (config('sync.my_directions')) — админ видел работу разработчика, а не то, что ждёт клиентов. Теперь по умолчанию показываются клиентские задачи: всё, кроме зоны ответственности — направлений «Производство», «Поддержка, сервис» и задач, где исполнитель (meta.assignee) — он сам. Сверху появился переключатель-чипсы «Клиентские (дефолт) / Все / Мои» (?who=clients|all|mine) с живыми счётчиками: «мои» = всё, что связано со мной — мои направления, мои проекты (клиент проекта — я сам, clients.personal_client_id) и задачи, где исполнитель — «Администратор» либо «Алексей Гапеев», «клиентские» — всё остальное, «все» — весь список. Счётчики чипсов направлений и статусов считаются в рамках выбранного режима (в «Клиентских» направления зоны ответственности показывают 0 — это честно, клик ведёт в пустой список). Старые ссылки ?direction=my|all|<id> сохраняют прежний смысл: направление по-прежнему открывает полный список задач направления, а ?direction=my работает как «Мои». Версия v0.96.1.
Что сделано
- Клиентские задачи — по умолчанию - Открывая «Задачи с платформы задач», админ сразу видит, что ждёт клиентов: по умолчанию показываются все задачи, которые не связаны с ним — кроме направлений config('sync.my_directions'), его собственных проектов (клиент проекта — он сам, clients.personal_client_id) и задач, где исполнитель (meta.assignee) — он («Администратор» либо «Алексей Гапеев»). Подзаголовок объясняет правило, а внизу списка — расшифровка: «„Мои“ — ваши направления, ваши проекты (клиент — вы) и задачи, где вы исполнитель; „Клиентские“ — всё остальное».
- Переключатель «Клиентские / Все / Мои» - Три чипса вверху списка: «Клиентские» (активен по умолчанию), «Все», «Мои» — параметр ?who=clients|all|mine со счётчиками задач в каждом режиме. Чипсы направлений и статусов, поиск, сортировка и пагинация сохраняют выбранный режим; в поисковую строку режим тоже подставляется скрытым полем.
- Честные счётчики - Счётчики чипсов считаются в контексте остальных фильтров: числа отражают, что реально появится после клика. В режиме «Клиентские» направления зоны ответственности показывают 0 (там только свои задачи), направления клиентов — свои количества.
- Старые ссылки не ломаются - ?direction=my по-прежнему открывает «мои» задачи, ?direction=<id> и ?direction=none — полный список соответствующего направления (как раньше, без отбора «клиентские»).
Проверено
- Тесты - tests/Feature/AdminPlatformTaskWhoFilterTest.php — 9 тестов: дефолтная страница показывает только клиентские задачи и скрытое поле who, ?who=mine включает задачи моего направления и с моим исполнителем, ?who=all показывает всё, клиентский режим с фильтром направления исключает мою задачу, проекты, где клиент — сам админ, и задачи с исполнителем «Алексей Гапеев» не попадают в клиентские, старые ссылки ?direction=my и ?direction=<id> работают как прежде, счётчики чипсов соответствуют выбранному режиму. Полный прогон платформы: 463 теста, 462 пройдено, 1 условно пропущен.
- Версия платформы - Поднята до v0.96.1: config/app.php, APP_VERSION в .env и .env.example; VersionSeeder — v0.96.1 актуальная. VersionFeatureTest и SeederKeepsWorkingDataCleanTest проходят.
v0.96.0
06.10.2026
MINOR
Обращение к получателю на карточке пользователя и уведомление о пустых полях
ТЗ 282 «Обращение к получателю». Поле «Обращение к получателю» было видно только в редактировании, о пустоте админ узнавал по письмам: когда поле пустое, письмо начинается строкой «Здравствуйте, <первое слово ФИО>!» (для «Авраменко Виолетта Владимировна» — «Здравствуйте, Авраменко!», то есть с фамилией). Теперь на карточке пользователя в списке и на его странице сразу видно, как система обратится к человеку: заполненное обращение — зелёный бейдж, пустое — красное предупреждение с тем, что уйдёт в письмо по умолчанию. В колокольчик администратору приходит уведомление «Не заполнено обращение» с числом таких клиентов (считаются рабочие клиенты: статус «В работе», активный вход, роль «Клиент») и ссылкой на список. Изменения полей пользователя логируются понятными подписями («Обращение к получателю: «…» → «…»») с автором действия. Версия v0.96.0.
Что сделано
- Обращение видно на карточке пользователя - В списке пользователей у каждого человека появилась строка «Обращение»: заполненное поле — зелёный бейдж с текстом обращения (подпись «так система обратится к пользователю в письмах»), пустое — красное «Обращение: не задано — „Здравствуйте, <первое слово>!“» с пояснением в подсказке. Пустых полей больше не нужно искать по письмам.
- Та же подсказка на странице пользователя - Карточка «Обращение к получателю» показывала просто «—». Теперь заполненное обращение — зелёный бейдж с пояснением, пустое — красное «Не заполнено» и строка о том, что письма начнутся именем по умолчанию и поле стоит заполнить.
- Уведомление в колокольчик - Если у активных клиентов «Обращение» пустое, в колокольчике администратора появляется пункт «Не заполнено обращение»: сколько таких клиентов, ссылка на список пользователей. Считаются только рабочие клиенты (статус «В работе», активный вход, роль «Клиент»); админы, «На проверке», «В корзине» и «Удалённые» не учитываются. Уведомление живёт, пока есть пустые поля, и само исчезает после заполнения.
- Понятный лог изменений - Изменения полей пользователя пишутся человеческими подписями вместо английских ключей: «Имя», «Обращение к получателю», «Email», «Компания», «Телефон», «Telegram» — в формате «было: … → стало: …». На странице пользователя в «Логи активности» добавлена колонка «Автор».
Проверено
- Тесты - tests/Feature/UsersSalutationFeatureTest.php — 6 тестов: заполненное и пустое обращение на карточке в списке, подсказка на странице пользователя, человеческие подписи полей в логе изменения, уведомление в колокольчик появляется при пустых полях и исчезает после заполнения, «не в работе» и админы не считаются. Полный прогон платформы: 454 тестов, 453 пройдено.
- Версия платформы - Поднята до v0.96.0: config/app.php, APP_VERSION в .env и .env.example; VersionSeeder — v0.96.0 актуальная. VersionFeatureTest и SeederKeepsWorkingDataCleanTest проходят.
v0.95.0
06.10.2026
MINOR
Дедлайн проекта и задачи со сроками на странице проекта
Дедлайн проекта был завязан только на активную подписку: у проектов без подписки карточка «Дедлайн» показывала «—», хотя срок завершения у проекта есть. У проекта появилось собственное поле deadline (планируемая дата завершения) с отдельным полем в редактировании; на странице проекта показывается оно, а если пусто — дедлайн из подписки с пометкой «по договору». Заодно задачи на странице проекта превращены из списка с процентами в карточки: поставлена дата и время, крайний срок, дата выполнения, описание, прогресс (с платформы задач, иначе доля выполненных шагов) и шаги с отметками «сделано / не сделано». Выполненная задача никогда не считается просроченной и всегда показывается на 100% со всеми шагами отмеченными; шаги идут вертикальным списком с номерами. Версия v0.95.0.
Что сделано
- Свой дедлайн проекта - В редактировании проекта появилось поле «Дедлайн проекта» (планируемая дата завершения), отдельное от подписки. На странице проекта карточка «Дедлайн» показывает его, а если поле пусто — дедлайн из активной подписки с пометкой «по договору».
- Задачи — карточки со сроками - У каждой задачи на странице проекта видно: когда поставлена («Поставлена: дд.мм.гггг чч:мм» — дата начала с платформы задач, иначе дата появления в MY IPD), крайний срок (просроченные подсвечены и подписаны «просрочена»), дату выполнения, полное описание, прогресс (процент с платформы задач через meta.remote_progress, иначе доля выполненных шагов) и шаги с отметками «сделано / не сделано».
- Ссылка на полную карточку - Название задачи в карточке ведёт на страницу задачи (admin.projects.tasks.show) и оформлено как ссылка по правилу ТЗ 280: color: var(--link) + text-decoration: underline.
- Выполненная задача не бывает просроченной - Если задача выполнена (статус или completed_at), даже при сроке в прошлом «Крайний срок» выводится спокойным цветом как справочная дата, пометка «просрочена» остаётся только у незавершённых. Прогресс выполненной задачи всегда 100%, все шаги отмечены — даже если на платформе шаги не проставлены (задача пришла уже выполненной).
- Шаги — вертикальный список - Список шагов в карточке переведён из нескольких колонок в один столбец с нумерацией: декомпозицию легче читать. Выполненные шаги — зелёная галочка и зачёркнутый текст.
- Дата «Выполнена» - Берётся из completed_at; если его нет (задача импортирована уже выполненной) — из истории переходов статусов (TaskStatusChange) по to_status = completed, когда она есть.
Проверено
- Тесты - tests/Feature/TasksReadOnlyTest.php: добавлены 3 теста — богатая карточка задачи (даты поставлена/срок/выполнена, описание; выполненная задача — 100%, все шаги отмечены и не считается просроченной: «просрочена» на странице ровно одна; активная — «80% — 1 из 2 шагов»), дедлайн проекта и запасной из подписки с пометкой «по договору», сохранение дедлайна из формы редактирования. Полный прогон платформы: 448 тестов, 447 пройдено, 1 условно пропущенный.
- Версия платформы - Поднята до v0.95.0: config/app.php, APP_VERSION в .env и .env.example; VersionSeeder — v0.95.0 актуальная. VersionFeatureTest и SeederKeepsWorkingDataCleanTest проходят.
- Регрессия ссылок - TextLinkVisibilityTest (ТЗ 280) проходит: новая ссылка-заголовок задачи в карточке не нарушает правило «ссылка в тексте синяя и подчёркнутая».
v0.94.2
06.10.2026
PATCH
Ссылки в тексте: синие и подчёркнутые
ТЗ 280 «Ссылки в тексте». Ссылка выглядела как обычный текст, и человек не знал, что на неё можно нажать. Причина оказалась не в вёрстке, а в токенах: общее правило a { color: var(--link); text-decoration: underline } в resources/css/app.css было и раньше, но локальные правила экранов его перебивали — красили ссылки в --accent (в тёмной теме #e2e8f0, в светлой #0f172a, то есть в цвет текста) и убирали подчёркивание. Исправлено 26 файлов blade и сам app.css. Кнопки, вкладки, карточки, кебаб, пагинация, сортировка, хлебные крошки и навигация сайта остались без подчёркивания — это элементы управления, у них свой вид. Версия v0.94.2.
Что сделано
- Ссылка в тексте — токен, а в письме — прямой цвет - На страницах платформы ссылки переведены на color: var(--link) + text-decoration: underline, чтобы не тащить цвет из одной темы в другую. В письмах (resources/views/emails/*) и в предпросмотрах писем внутри платформы цвет остался прямым: почтовый клиент не понимает CSS-переменных и оставил бы ссылку вообще без цвета. Там исправлено главное — убрано text-decoration: none.
- Пройдены все экраны платформы - Панель письма и его предпросмотр, списки писем и пользователей, карточки проектов и задач, рабочий стол админа и клиента, еженедельные отчёты, кампании и рассылки, заявки в поддержку, расписание, мастер выгрузки людей, кабинет клиента по проекту, подпись писем — 26 файлов blade плюс resources/css/app.css.
- Подчёркивание не пропадает при наведении - Раньше наведение местами убирало подчёркивание, и ссылка «терялась» ровно в момент, когда человек решал нажать. Теперь меняется только цвет (--link-hover), линия остаётся.
- Кнопки и навигация выглядят как раньше - Элементы управления намеренно остались без подчёркивания: у них рамка, заливка или подсветка фоном, и подчёркивание им мешает. Различать их помогает оформление (рамка, заливка, иконка рядом) — это правило зафиксировано в тесте, чтобы «ослабление» не расползлось по платформе.
Проверено
- Тесты - tests/Feature/TextLinkVisibilityTest.php: 5 тестов, 17 проверок — общий стиль ссылки, различие токена --link в тёмной и светлой темах, отсутствие «ссылок как текста» в CSS-правилах и инлайновых стилях, сохранение подчёркивания при наведении и пример из жалобы (панель данных письма). Полный прогон платформы: 445 тестов, 444 пройдено, 1 условно пропущенный.
- Сам тест проверен на регрессию - Первый вариант разбирал тег регуляркой <a[^>]*> и потому обрывался на стрелке «->» внутри $email->client->id: атрибут style выпадал из проверки, и сломанная ссылка проходила молча. Тег теперь разбирается посимвольно с учётом кавычек, а тест проверен мутацией — со старым видом ссылки он падает и называет проблемное место.
v0.94.1
06.10.2026
PATCH
Буфер писем: управление письмами прямо со страницы
ТЗ 279 «Буфер писем, управление». В буфере было видно, что письмо зависло, но нельзя было ничего с ним сделать: ни удалить, ни отредактировать — приходилось искать письмо в общем списке и терять его там. Теперь у каждого письма в буфере есть галочка и меню действий, а над списком — панель массовых действий: отметили пять ненужных писем и удалили их одним действием, не покидая страницу. Попутно исправлена ошибка, из-за которой после успешного массового удаления показывалось ложное сообщение об ошибке, и счётчики групп буфера перестали расходиться с тем, что на экране. Версия v0.94.1.
Что сделано
- Галочка у каждого письма в буфере - Строка буфера перестала быть ссылкой целиком: теперь это контейнер из трёх частей — галочка слева, сама письмо в середине и меню действий справа. Так галочка не оказалась вложена внутрь ссылки, а по строке по-прежнему можно открыть письмо одним кликом.
- Массовое удаление прямо из буфера - Отметили нужные письма галочками (или одной галочкой «выбрать все») — над списком появилась панель «Выбрано: N» с действием «Удалить выбранные». Оно спрятано в меню опасных действий и спрашивает подтверждение с числом писем. Отправляется тем же маршрутом `/admin/emails/bulk-action`, который уже обслуживал список и корзину, — новое действие на сервере не заводилось.
- Меню действий у письма - В кебабе строки: «Смотреть», «Редактировать», «Копия» и «Удалить» с подтверждением. Удаление и одиночное, и массовое уводит письмо в корзину, откуда его можно восстановить.
- Починен баг с ложной ошибкой - Функция обновления панели выбора была объявлена внутри изолированной функции и потому была недоступна обработчику массового действия. После успешного удаления код падал с ReferenceError, и человек вил «Не удалось выполнить операцию» — притом, что письма были удалены. Заодно панель теперь пересчитывается после того, как строки действительно исчезли, а не заранее.
- Счётчики перестали расходиться с экраном - Счётчик группы показывает все письма группы, включая спрятанные за «Ещё N», поэтому пересчитать его по оставшимся строкам нельзя. Скрипт уменьшает его на число реально исчезнувших строк, а ответ сервера теперь содержит и счётчики групп буфера — плитки «Просрочено», «Сегодня», «Завтра» показывают новое число сразу, без перезагрузки. Когда группа опустела, показывается честная пустая строка.
- Меню действий не обрезается - Блок группы перестал обрезать содержимое по своему краю: выпадающее меню в последней строке раньше могло уйти за границу блока.
Проверено
- Тесты - tests/Feature/AdminPreparedEmailBufferViewTest.php: 17 тестов, 84 проверки — добавлены 6 тестов на управление из буфера: галочка и кебаб в строке, панель массовых действий, выбор не предлагает черновики и отправленные, пустой буфер не показывает панель, массовое удаление уводит письма в корзину и возвращает свежие счётчики групп, у групп есть данные для пересчёта на месте. Полный прогон платформы: 440 тестов, 439 пройдено, 1 условно пропущенный.
- Существующие виды - «Список» и «Корзина» не сломаны: тот же обработчик массового действия используется всеми тремя видами, исправление ошибки проверило и их.
v0.94.0
06.10.2026
MINOR
Буфер писем: что уйдёт сегодня, завтра и на неделе
ТЗ 278 «Буфер писем». Ссылка «Буфер писем» в меню вела в общий список всех писем, где нужное письмо найти невозможно, а плитка «Сегодня/завтра» считала ближайшие 24 часа от текущей минуты и показывала 0, хотя письма на сегодня были. По умолчанию теперь открывается буфер: просроченные письма отдельным блоком, дальше сегодня, завтра и каждый следующий день до конца недели. Периоды считаются по календарным дням. Версия v0.94.0.
Что сделано
- Буфер открывается по умолчанию - `/admin/emails` без параметров открывает новый вид «Буфер» — только письма в статусе «в очереди», разложенные по дням отправки. Вкладки «Список», «Календарь» и «Корзина» остались на месте и открываются явно.
- Почему старая плитка показывала 0 - Граница «Сегодня/завтра» считалась как «сейчас + 24 часа». В 21:30 это окно заканчивается завтра в 21:30, и письма, запланированные на сегодня в 08:30, в него не попадали — ни в плитку, ни в фильтр. Теперь «сегодня» значит до 23:59 сегодняшних суток, «завтра» — целые следующие сутки. Проверено на живых данных: при 21:30 в плитке было 0 при 184 письмах на сегодня.
- Просроченные видно сразу - Письма, у которых время отправки уже прошло, а статус всё ещё «в очереди», вынесены в отдельный блок «Просрочено». На тесте таких оказалось 184 — они молчали в общем списке. Блок обычный, внимание привлекает формулировкой, а не красным цветом. Отдельный блок «Дальше» честно показывает число писем за пределами недели, а не прячет их.
- Горизонты 3 дня и неделя - Добавлены плитки «На 3 дня» и «На неделю» с календарными границами. Плитки просроченного и ближайших дней показывают число нейтральным цветом.
- В списке не больше 50 строк на блок - В каждом блоке показываются первые 50 писем, дальше честная строка «Ещё N — открыть все в списке». Ничего не обрезается молча.
- Убрана вводящая в заблуждение подпись - Плитка плана по расписанию была подписана «завтра (пт)» — день недели был зашит в текст. Сегодня понедельник, и подпись врала. Теперь дата и день недели считаются, а в подсказке разделено: план по расписанию — это не фактический буфер.
Проверено
- Тесты - Новый tests/Feature/AdminPreparedEmailBufferViewTest.php: 11 тестов, 47 проверок — границы по календарным дням, просроченное письмо попадает в свой блок, горизонт 3 дня обрезается по 00:00 четвёртого дня, письма за пределами недели не вываливаются в страницу, одно письмо не дублируется между блоками, черновики/отправленные/ошибки в буфер не попадают, виды list/calendar/trash не сломаны. Полный прогон платформы: 434 теста, 433 пройдено, 1 условно пропущенный.
- Ширины экрана - /admin/emails проверен в браузере на ширинах от 1920 до 360: горизонтальной прокрутки нет ни на одной, ни один заголовок блока и ни одна тема письма не обрезаются. Плитки: 13 в ряду при 1920, 7 при 1024, 2 при 360, ни одна без числа.
- Шрифт и вкладки - Все новые размеры шрифта — 14px и выше, правило «не меньше 14px» не смягчалось. Вкладка «Буфер» сделала перенос строки вкладок обязательным: на 360px четыре вкладки в одну строку не помещались.
v0.93.3
05.10.2026
MINOR
Список пользователей: карточки вместо таблицы
ФИО в списке пользователей переносилось по две буквы. Причина найдена замером, а не на глаз: в таблице из 11 колонок колонка «Имя» сжималась до 63px при ширине окна 1440px — «Тариф» с длинным названием занимал 287px, а «Роль», «Статус» и «Вход» по 90–144px каждая; сверху `overflow-wrap:anywhere` разрешал рвать слово в любом месте. Список переведён с таблицы на карточки: у каждого поля своя ширина, имя занимает свободное место целой строкой и переносится только по словам. Замер после изменения: ширина имени 419px при 1366px, одна строка на всех ширинах до 360px. Сортировка вынесена в отдельную строку над списком, все поля и меню действий сохранены. Версия v0.93.3.
Что сделано
- Замер вместо догадок - Ширина каждой колонки измерена в браузере на 10 ширинах экрана. Картина: при 1440px «Имя» — 63px, «Тариф» — 287px, «Роль» — 144px. Таблица из 11 колонок физически не помещается в рабочую область ноутбука, и это не лечится брейкпоинтами — приходилось выбирать, кому станет плохо.
- Список стал карточками - Вместо таблицы — решётка карточек шириной 380px: при 1920 в ряд встают 4, при 1366 — 2, при 1024 — 1. В карточке у каждого поля своя ширина, поэтому имя больше не соревнуется за место с тарифом.
- Имя не рвётся по буквам - `overflow-wrap:anywhere` на ячейках таблицы был второй причиной переноса: он разрешал разрыв слова в любом месте. В карточке имя переносится только по словам (`break-word`). Замер: ширина имени 63px → 419px при 1366px, одна строка на всех ширинах до 360px.
- Сортировка вынесена отдельно - У карточек нет заголовков колонок, поэтому сортировка стала отдельной строкой над списком: «Сортировать: Имя, Email, Роль, Статус, Вход, Создан». Ссылки те же (`sort`/`dir`), активные фильтры сохраняются, направление переключается по клику.
- Ничего не скрыто - Раньше часть колонок приходилось прятать по ширине экрана. В карточке осталось всё: роль, тариф, статус, вход, почта со ссылкой «написать», счётчик проектов с подписью «проект / проекта, владелец», дата последнего входа, дата создания и меню действий.
- Убрана битая кнопка «На странице» - Внизу списка стоял select с одной неактивной опцией и знаком «—» — он ничего не делал. Настоящий выбор размера страницы остался в форме фильтров.
Проверено
- Тесты - tests/Feature/AdminUsersListPortfolioCountTest.php переписан под карточную вёрстку: 7 тестов, 38 проверок — есть карточки, сортировка, `break-word`, и при этом таблицы со списком в разметке нет. Полный прогон платформы: 422 теста, 421 пройдено, 1 условно пропущенный на отсутствии прод-клиента.
- Ширины экрана - /admin/users проверен в браузере на ширинах 1920, 1440, 1366, 1280, 1152, 1024, 900, 768, 640, 420 и 360 пикселей: горизонтальной прокрутки страницы нет ни на одной, ни одно имя не переносится по частям слов.
- Шрифт - Правило «шрифт не меньше 14px» не смягчалось: имя 15px, остальной текст 14px, на узких экранах уменьшаются рамки и отступы.
v0.93.2
05.10.2026
MINOR
Счётчик проектов в списке пользователей и адаптивность admin-разделов
ТЗ 277 «У Дмитрия ноль проектов и горизонтальная прокрутка». В списке пользователей счётчик проектов считал только участие человека в проекте (client_project). Владелец проекта в эту связь не попадает намеренно, поэтому у клиента с собственным проектом стоял ноль. Теперь счётчик берёт объединение «владелец + участник» без двойного учёта, а роль в счётчике подписана явно. Тот же подсчёт перенесён в фильтр «С проектами» и в блок статистики — раньше список и счётчик показывали разные числа. Вторая часть задачи — адаптивность: таблица пользователей из 11 колонок скрывает второстепенные колонки по ширине экрана, широкие таблицы прокручиваются внутри себя вместо растягивания всей страницы, шапка переносится на узких экранах. Версия v0.93.2.
Что сделано
- Счётчик проектов учитывает владельца - В списке пользователей (/admin/users) счётчик брал только проекты, где человек участник (client_project). Владелец в эту связь не пишется намеренно (иначе он дублировался бы участником), из-за чего у клиента с собственным проектом стоял «0». Теперь счётчик — объединение проектов, где человек владелец, и проектов, где участник, без двойного учёта; роль подписана явно («владелец» / «участник» / «владелец и участник»), а в подсказке видно, из чего сложился итог.
- Один подсчёт для списка, фильтра и статистики - Фильтр «С проектами» и блок статистики считали только участие, поэтому список и счётчик показывали разные числа. Все три места переведены на один и тот же подсчёт владельцев и участников.
- Таблица пользователей подстраивается под экран - В таблице 11 колонок, на ноутбуках они не помещались. Второстепенные колонки скрываются по ширине экрана: до 1400px — Email и Тариф, до 1024px — Роль и Вход, до 860px — даты. Всегда остаются аватар, имя, статус, проекты и действия. Email дублировал ссылку в имени, поэтому скрывается первым.
- Горизонтальная прокрутка убрана со страницы - Рабочей области добавлен min-width: 0. Широкая таблица раньше растягивала flex-контейнер вместе с отступом бокового меню и уводила за границу экрана всю страницу целиком. Теперь лишняя ширина уходит в прокрутку самой таблицы, страница остаётся на месте.
- Шапка переносится на узких экранах - На ширине 420px и меньше заголовок, метка окружения и блок пользователя не помещались в одну строку и выталкивали страницу за границу. Шапка переносится на вторую строку, длинный заголовок сокращается многоточием.
- Подпись клиента в блоке «Расписание» - На главной админки длинное название клиента выносило подпись за границу экрана на 420px. Подпись теперь переносится по словам и не выходит за контейнер. На ширине до 480px строка расписания переносится целиком: статус с датой и кнопка действий уходят на вторую строку, а не выталкивают меню за край окна на 360px.
- Длинные куски в описаниях версий - В описаниях версий встречаются неразрывные куски — пути к тестам, имена файлов, идентификаторы. На публичной странице /versions они растягивали страницу шире окна: при ширине 360 документ уходил до 645. Текст описаний теперь переносится по любому символу, страница остаётся на месте.
Проверено
- Тесты - tests/Feature/AdminUsersListPortfolioCountTest.php (6 тестов, 25 проверок) плюс AdminUserAddressesTest, AdminUserStatusTest, AdminDashboardHintsFeatureTest, ClientProjectsPageTest, AdminPeopleExportWizardFeatureTest, DesignSystemRenderTest, AdminProjectsScopeTest и ClientPortfolioFeatureTest: 87 тестов, 832 проверки, падений нет. Полный прогон платформы: 421 тест, 420 пройдено, 1 условно пропущенный на отсутствии прод-клиента.
- Ширины экрана - /admin/users, /admin, /admin/projects, /versions и /about проверены в браузере на ширинах 1920, 1440, 1366, 1280, 1024, 900, 768, 640, 420 и 360 пикселей: горизонтальной прокрутки страницы нет ни на одной, широкая таблица проектов прокручивается внутри своего контейнера.
- Шрифт - Правило «шрифт не меньше 14px» не смягчалось ради того, чтобы таблица поместилась: на узких экранах уменьшаются рамки и отступы, а не размер текста.
v0.93.0
02.10.2026
MINOR
Письма на неделю/месяц: генерация и пересборка upcoming
Восстановлен функционал управления письмами на неделю/месяц: добавлены кнопки «Сформировать на неделю/месяц», «Пересобрать на неделю/месяц» (с отменой будущих в выбранном периоде и повторной генерацией), роуты и методы контроллера. После генерации автоматически очищаются просроченные queued (scheduled_at <= now). Статистика «На неделю/месяц» в админке стала корректной.
UI (Буфер писем)
- Кнопки генерации - Добавлены кнопки «Сформировать на неделю», «Сформировать на месяц».
- Кнопки пересборки - Добавлены «Пересобрать на неделю», «Пересобрать на месяц» (отменяют queued-письма в ближайшие 7/30 дней и формируют новые).
Backend
- Контроллер - В AdminPreparedEmailController добавлены методы generateUpcoming/generateWeek/generateMonth/rebuildUpcoming.
- Роуты - Добавлены POST-роуты admin.prepared-emails.generate-upcoming/week/month и rebuild-upcoming.
- Очистка после генерации - Просроченные queued (scheduled_at <= now) автоматически отменяются, чтобы очередь оставалась только будущей.
v0.92.1
02.10.2026
PATCH
Очередь подготовленных писем: просроченные в очереди не отправляются
Исправлена проблема, когда письма в статусе «В очереди» с запланированной датой в прошлом оставались в очереди и могли быть отправлены позже. Теперь команда email:send-prepared автоматически отменяет сильно просроченные queued (>48ч) и берёт в отправку только актуальные по расписанию.
Что сделано
- Защита от просроченных queued-писем - В `app/Console/Commands/SendPreparedEmails.php` добавлена автоотмена queued с `scheduled_at < now()-48h` (cancelled с комментарием в `admin_notes`). В отправку берутся только queued в окне (now-48h .. now).
v0.92.0
02.10.2026
MINOR
Одна кнопка на всех: люди, проекты, связи и задачи за один проход
ТЗ 273 «Люди загрузились, а проектов у них нет». Загрузка людей и загрузка проектов были разными кнопками в двух мастерах, и порядок зависел от того, какую нажали: люди успевали появиться раньше проектов — и проект оставался без владельца, а у клиента не появлялось ни одного проекта. Причина в сопоставлении: у человека без почты на площадке person_external_id пустой, а почта в карточке проекта и в списке людей расходится, поэтому владелец не находился вообще. Теперь одна кнопка «Синхронизировать всё» делает всё в правильном порядке (направления → люди → проекты → связи «человек → проект» → задачи) и одной транзакцией, а владелец проекта ищется через общий список людей по person_id — это работает и без почты. Версия v0.92.0.
Что сделано
- Одна кнопка вместо трёх - На странице «Синхронизация данных» (/admin/sync) — кнопка «Синхронизировать всё с платформы задач». За один проход и в одной транзакции: направления → люди → проекты (с владельцем) → связи «человек → проект» → задачи и шаги. Письма по задачам не отправляются: массовый импорт не должен слать клиентам письма, их готовят мастера писем отдельно. После запуска — страница итога с цифрами: сколько людей, проектов, связей и задач создано и обновлено.
- Владелец проекта находится и без почты - Порядок поиска: person_external_id → person_id через общий список людей выгрузки (PersonMatcher: внешний id, почта, затем имя + организация) → person_email из карточки проекта → person_name. Раньше использовались только person_external_id и person_email, поэтому у человека без почты владелец не находился никогда. Связь по person_id однозначна: 41 проект из 41 сопоставляется с человеком из списка людей.
- Связи внутри того же прохода - Привязка людей к проектам перенесена в тот же запуск и ту же транзакцию, что и импорт проектов (linkClientsToProjectsFromPayload), и работает по уже полученной выгрузке — без второго обращения к платформе. Владелец участником не дублируется.
- Повторный запуск чинит накопленное - Кнопка безопасна для повторов: связи только добавляются, владелец проставляется, если его нет, люди и проекты не создаются заново. Поэтому один запуск закрывает и уже накопившийся долг, без ручной доводки по спискам.
Найдено в данных
- 2 проекта были без владельца - «Медтехника Гродно» (проект 9 на площадке) и «АльденаГрупп — сопровождение и развитие (2026)» (4509).
- 5 клиентов были без единого проекта - Воронов Вадим, Калита Алексей, Михнюк Дмитрий, Покалюк Денис (ООО «АльденаГрупп») и Кипеть Александр (РУП «Медтехника» Могилёв) — ровно участники этих двух проектов. Все без почты, поэтому и не находились.
- 20 задач без проекта — это другое - Их проектов уже нет на платформе (личные и архивные проекты IPD). Не трогались: это случай осиротевших задач, а не синхронизации.
Проверено
- 5 тестов на полную синхронизацию - tests/Feature/FullPlatformSyncTest.php: владелец без почты находится по person_id и появляется владельцем проекта; все участники получают проект в том же запуске; владелец не дублируется участником; второй запуск ничего не создаёт и не ломает; проект, оставшийся без владельца, получает его при следующем запуске; кнопка и страница итога работают через HTTP.
- Полный набор тестов зелёный - 403 теста, падений нет.
v0.91.0
02.10.2026
MINOR
Одна синхронизация людей, проектов и задач: без почты создаём, смену статуса показываем в письме
ТЗ 272 «Одна кнопка и правдивые письма». Люди без email молча пропускались, а смена статуса задачи нигде не сохранялась — платформа отдаёт только текущий статус, поэтому письмо об изменении просто не могло появиться. Теперь: клиент без email создаётся с пустой почтой (появится в проектах и списках, писем не получит); каждый переход статуса «было → стало» пишется при синхронизации в новую таблицу task_status_changes и выводится в отчётных письмах плейсхолдером {{status_changes_list}}; сопоставление человека с клиентом вынесено в один сервис PersonMatcher для превью и импорта сразу. Попутно исправлена ошибка сопоставления по кириллице: сравнение шло через SQL LOWER(), который в SQLite не понижает регистр кириллицы, из-за чего людей без почты создавали заново при каждом импорте. Версия v0.91.0.
Что сделано
- Человек без email создаётся - Раньше он просто исчезал: шаг 1 показывал его в блоке «не будет добавлен», шаг 2 пропускал. Теперь клиент создаётся с пустым email (поле стало nullable), попадает в проекты и списки; писем он не получает, потому что отбор идёт по наличию адреса. Оба мастера — «Синхронизация всех людей» и «Синхронизация людей с платформы задач» — показывают его отдельным блоком «добавятся, но писем не получат».
- История смены статуса задачи - Новая таблица task_status_changes (task_id, project_id, from_status, to_status, source, origin, changed_at). Заполняется в момент синхронизации — это единственное место, где известны оба значения. Переход из закрытой задачи в историю пишется, но письмо не отправляет: это факт, а не повод слать «задача открыта заново».
- Письма показывают смену статусов - ReportDispatcher собирает блок «Изменился статус задач» за отчётный период (не больше 20 переходов) и подставляет его через новый плейсхолдер {{status_changes_list}} в шаблоны report-wednesday и report-friday. Пустой период даёт пустую строку — лишний заголовок в письме не появляется. Переходы по внутренним (невидимым клиенту) задачам в письмо не попадают.
- Одно сопоставление вместо двух - Новый сервис App\Services\Sync\PersonMatcher используют и предпросмотр (DiffEngine), и импорт (SyncManager), и оба контроллера: порядок поиска один — внешний id человека (= id клиента в MY IPD), затем email, затем имя вместе с организацией. Одного имени без организации недостаточно: однофамильцев с разными компаниями склеивать нельзя.
- Исправлена ошибка сопоставления по кириллице - Сравнение имени и email шло через SQL LOWER()/TRIM(), а SQLite не понижает регистр кириллических букв: «Человек Без Почты» никогда не совпадал с «человек без почты», и при каждом импорте создавался новый клиент. Сравнение перенесено в PHP с нормализацией пробелов и регистра.
- Организация и адреса больше не теряются - Организация человека с платформы пишется в clients.company — по ней он и находится в следующих импортах. Обновление email и списка emails происходит только когда платформа реально прислала адреса: иначе у человека без почты на площадке стирались бы адреса, добавленные в MY IPD.
Проверено
- Полный набор тестов зелёный - 398 тестов, падений нет. Обновлены тесты двух мастеров под новое правило (люди без email больше не пропускаются), добавлены TaskStatusChangeSyncFeatureTest (3 теста: переход записан со старым и новым статусом, при неизменившемся статусе история пуста, переоткрытая задача не шлёт письмо) и три теста на блок смены статусов в WeeklyReportCompositionTest (внутренняя задача скрыта, переход вне периода не показывается).
- Миграции применены на TEST - clients.email стал nullable, создана task_status_changes, в существующие шаблоны report добавлен плейсхолдер.
v0.90.1
01.10.2026
PATCH
Задачи только с платформы: убран мусор, снято 196 ложных пометок
ТЗ 271 «Задачи велись в двух местах». Задачи приходят с платформы task.ipd, но в MY IPD у синхро-задач были свои формы правки: маршруты create/store/edit/update/destroy и отдельный AdminStepController для шагов. Правка жила до следующей синхронизации и затиралась, удалённая задача возвращалась из выгрузки снова. Для синхро-задач маршруты правки удалены, карточка задачи только читается, кнопка «+ Задача» на странице проекта убрана; удаление осталось только в сверке мусора. Причина заваленности базы: 1112 синхро-задач, из них 327 на платформе нет вообще (тестовые D-04…D-111 и прочие), а 196 задач были ошибочно помечены «нет на платформе» — синхронизация сверялась по ответам на отдельные запросы вместо полного списка задач платформы. Предпросмотр и SyncManager теперь считают известные идентификаторы по объединению всех источников. Добавлена команда tasks:reconcile-platform с режимами --dry-run, --restore, --purge. На TEST удалено 327 задач и 20 шагов, 196 задач восстановлено. Версия v0.90.1.
Что сделано
- Задачи только читаются - У синхро-задач убраны маршруты create/store/edit/update/destroy и AdminStepController, удалены шаблоны tasks/create.blade.php и tasks/edit.blade.php. Карточка задачи показывает шаги, пришедшие с платформы («Шаги приходят с платформы задач»), ничего не редактируя на месте. Клиентские ТЗ (client_tasks) не затронуты.
- Ложные пометки «нет на платформе» - 196 задач были помечены как отсутствующие на платформе, потому что синхронизация сверялась с ответами на отдельные запросы, а не с полным списком задач платформы. Такие задачи скрывались у клиента и попадали в отчёт об удалённых. Сверка теперь идёт по объединению известных идентификаторов (knownIds ∪ remoteTaskIds) в предпросмотре и в SyncManager при загрузке ответа задачи.
- Команда tasks:reconcile-platform - Сверяет локальные синхро-задачи с платформой и показывает четыре группы: есть на платформе, нет на платформе, ошибочно помеченные, скрытый мусор без пометки. Режимы: --dry-run (только отчёт, по умолчанию), --restore (снять ложные пометки, вернуть видимость клиенту), --purge (удалить отсутствующие на платформе), --yes (без подтверждения), --project (один проект). Всё в одной транзакции с откатом при ошибке.
- Удаление задачи чистит всё - Task::purgeWithRelations() удаляет задачу вместе со шагами, сообщениями и вложениями, включая файлы на приватном диске. Раньше удаление из контроллера трогало только запись в tasks, а файлы вложений оставались на диске. Этим же методом пользуются одиночное и массовое удаление мусора.
Почищено на TEST
- Удалено 327 задач, которых нет на платформе - Тестовые задачи D-04…D-111, «Тестовая задача», «Тестовая задача E2E» и прочие; вместе с ними удалено 20 шагов. Скрытого мусора (нет на платформе, но без пометки) не оказалось — синхронизация пометила всё, что не нашла.
- Возвращено 196 задач - Снята пометка «нет на платформе», включена видимость клиенту. Итог сверки: локальных синхро-задач 785, есть на платформе 785, нет на платформе 0, ошибочно помеченных 0.
- Пересчитаны счётчики проектов - tasks_total/tasks_completed в 41 проекте совпадают с фактическим числом задач, расхождений 0.
- Проверка целостности ссылок - Сирот нет в tasks.project_id, steps.task_id, task_messages.task_id, task_attachments.task_id, support_requests.task_id.
- Бэкап - storage/app/private/my_ipd_local_before_task_purge_2026-10-01_142538.sql (24,5 МБ, проверен импортом в чистую базу my_ipd_restore_check).
v0.90.0
01.10.2026
MINOR
Настоящие даты закрытия задач и запрет пустых месячных писем
ТЗ 270 «Месячные письма приходят пустыми». Найдено две причины. Первая: экспорт платформы задач /api/export/full вообще не отдаёт дату закрытия задачи — синхронизация её никогда не заполняла, поэтому 667 закрытых задач оставались без даты, а отчётный период — это диапазон дат, и задача без даты не попадала ни в один месяц. Добавлена команда tasks:import-completion-dates, которая берёт completed_date из БД платформы напрямую (подключение task_platform) и сверяет задачи по внешнему идентификатору; на TEST проставлено 460 настоящих дат (162 за июль, 95 за август, 123 за сентябрь), снесено 60 выдуманных дат, поправлено 3. Вторая: пустое письмо всё равно уходило клиенту. Добавлен гейт наполнения — письмо, в котором нет ни задач, закрытых внутри отчётного месяца, ни задач в работе, не отправляется, а остаётся в черновиках с причиной; тело письма пересобирается по свежим данным проекта прямо перед постановкой в очередь, потому что буфер готовится за месяцы вперёд. Версия v0.90.0.
Почему письма были пустыми
- Экспорт не отдаёт дату закрытия - В блоке «задачи» ответа /api/export/full (task.ipd 2.52.5) ключа даты закрытия нет, тогда как у проектов он есть. Синхронизация completed_at в my.ipd не заполнялась никогда: 667 закрытых задач оставались без даты, а месячный отчёт отбирает задачи по диапазону дат — задача без даты не попадает ни в один месяц.
- Даты, выдуманные моделью - App\Models\Task при переходе в статус completed сам проставляет completed_at = now(). У задач, закрытых в MY IPD без даты с платформы, completed_at совпадал с updated_at — это отметка синхронизации, а не момент выполнения. Такие даты команда сносит, а даты, проставленные вручную (completed_at != updated_at), не трогает.
- Пустое письмо уходило клиенту - Даже когда задач в периоде не было, месячное письмо всё равно попадало в очередь на отправку. На проде 01.10.2026 ушло 5 месячных писем, из них 4 содержали нули.
Что сделано
- Команда tasks:import-completion-dates - Берёт настоящие completed_date из БД платформы напрямую (подключение task_platform в config/database.php, параметры TASK_PLATFORM_DB_* в .env). Сверяет задачи по tasks.external_id = платформа tasks.task_id. Опции: --project (один проект), --dry-run. Платформа — источник истины: есть дата — проставляем/исправляем, нет даты — сносим выдуманную (равную updated_at).
- Даты ставятся поштучно по своим значениям - Задачи группируются по дате и обновляются отдельным UPDATE на каждую дату. Одним общим UPDATE всем ушла бы дата первой задачи — из-за такой ошибки первый прогон дал всем 397 задач 27.07.2026 и был откачен по бэкапу.
- Гейт наполнения месячного письма - Новый сервис App\Services\Reports\ReportContentCheck считает наполнение по живым данным проекта: задачи, закрытые внутри периода, и задачи в работе, видимые клиенту. Если обе цифры нулевые — письмо не отправляется: mail:send-monthly кладёт его в черновик с причиной, email:send-prepared отменяет отправку, email:rebuild-monthly показывает «Пустых отчётов: N». Счётчики проекта (tasks_total, progress_pct) в проверке не участвуют — они устаревают и показывали нули при полном отчёте.
- Письмо пересобирается перед отправкой - Буфер писем готовится на месяцы вперёд, и задачи клиента закрываются уже после сборки. Перед постановкой в очередь тело, тема и metadata письма пересобираются по свежим данным проекта (MonthlyDigestService::refresh), в metadata пишется content с числами закрытых и идущих в работу задач.
Что важно знать
- Пустой отчёт — это нормальное состояние - Для будущих месяцев (ноябрь 2026 и далее) отчёт закономерно пуст: данных за отчётный месяц ещё нет. Письмо остаётся в буфере и наполнится, когда клиенту закроют задачи; отправка заблокирована только если к дате отправки наполнения так и не появится.
- Задание платформы не менялось - Правки только в my.ipd. task.ipd не трогался: даты читаются из его локальной копии БД в режиме чтения.
- Что сделано на TEST - Проставлено 460 дат закрытия: июль 2026 — 162, август — 95, сентябрь — 123; снесено 60 отметок синхронизации, исправлено 3. Месячные письма с 01.10.2026 по 01.03.2027 пересобраны (60 писем), в пересборке видно «закрыто N, в работе M» по каждому клиенту.
- Бэкапы - Перед заливкой дат: storage/app/private/tasks_before_completion_dates_2026-10-01_101513.sql. Перед пересборкой месячных: storage/app/private/prepared_emails_monthly_before_rebuild_2026-10-01_082408.sql.
v0.89.0
01.10.2026
MINOR
Месячный отчёт — за предыдущий месяц, по одному письму на клиента
ТЗ 269 «Месячные письма называют не тот месяц». Генератор будущих писем email:generate-upcoming готовил месячный дайджест с датой отправки как отчётной: письмо, намеченное на 1 октября, называлось «Ваш проект — итоги за Октябрь 2026» ещё до начала октября, и список выполненных задач отбирался за неверный месяц. Сборка месячного письма вынесена в единый сервис MonthlyDigestService, где отчётный период всегда равен месяцу отправки минус один. Месячный отчёт — один на клиента и собирается по его текущему проекту (вместо письма на каждый активный проект), а требование активной подписки снято: получателем является любой клиент со статусом «В работе». Добавлена команда email:rebuild-monthly для пересборки буфера. На тестовом стенде пересобраны месячные письма с октября 2026 по март 2027 — 60 писем для 10 клиентов «в работе», каждое с подписью предыдущего месяца. Версия v0.89.0.
Что сделано
- Правильный отчётный месяц - Отчётный период месячного письма считается как месяц отправки минус один: письмо на 1 октября подписывается «итоги за Сентябрь 2026» и собирает задачи, завершённые в сентябре. Раньше и генератор будущих писем, и список задач смотрели в месяц отправки.
- Одно письмо на клиента - Месячный дайджест собирается по текущему проекту клиента (Client::resolveCurrentProject) и отправляется один раз в месяц. Раньше письмо создавалось на каждый активный проект — у клиента с 29 проектами это означало бы 29 писем в месяц.
- Без требования подписки - Получателем месячного отчёта является любой клиент со статусом «В работе», у которого заполнен адрес и который не отписался от рассылки. Требование активной подписки (client_subscriptions) снято: письмо не должно зависеть от того, как заведён тариф.
- Отчётный период в данных письма - В письмо пишутся metadata.report_period (тип, границы, подпись, ключ периода) и task_ids — буфер и предпросмотр показывают, за какой месяц и за какие задачи собрано письмо. Раньше это заполняла только команда mail:send-monthly.
- Пересборка буфера - Добавлена команда email:rebuild-monthly (--from, --months, --dry-run): обновляет уже подготовленные месячные письма, создаёт недостающие, отменяет дубли и письма клиентам вне рассылки. Уже отправленные письма не трогаются.
Что важно знать
- Пересобрано на TEST - Месячные письма с 01.10.2026 по 01.03.2027 (6 месяцев) пересобраны для 10 клиентов «в работе» (№ 5, 6, 18, 19, 23, 24, 27, 29, 32, 36) — по одному письму на клиента, все с подписью предыдущего месяца. Клиенты в корзине, на проверке и без активного проекта письма не получают.
- Письма 1 сентября остаются историей - Шесть писем, отправленных 01.09.2026, оставлены без изменений: они уже ушли клиентам, переписывать историю нельзя.
- Список выполненных зависит от дат задач - В отчёт попадают только задачи с заполненной датой завершения, попадающей в отчётный месяц. Задачи без даты завершения в отчёт не берутся — если у проекта не проставлены даты закрытия, месячный список выполненных будет пустым, и письмо об этом честно сообщает.
v0.88.0
01.10.2026
MINOR
Гейт получателя: буфер писем больше не пишет тем, кому писать нельзя
ТЗ 268 «На production уходят письма не тем клиентам». Найдена и устранена первопричина: команда email:send-prepared отправляла всё, что лежит в буфере со статусом queued, не проверяя текущий статус клиента — адрес в письме был снимком на момент подготовки, а не текущими данными клиента. Теперь перед отправкой проверяется, что клиент активен, имеет статус «В работе», не отписался от email-рассылки и что адрес письма всё ещё входит в список его адресов; иначе письмо снимается в статус «Отменено» с причиной в admin_notes и не отправляется. Генераторы месячных писем mail:send-monthly и email:generate-upcoming тоже переведены на Client::emailActive, поэтому клиентам в корзине и на проверке письма больше не создаются вовсе.
Что сделано
- Проверка получателя при отправке - В email:send-prepared добавлена проверка получателя: письмо без клиента, со статусом клиента не «В работе», с is_active=false, с отказом от email-рассылки или с адресом to_email, которого больше нет среди адресов клиента (clients.email и clients.emails), снимается со статусом «Отменено». Причина отмены записывается в служебное поле письма и видна в админке.
- Меньше лишних писем - mail:send-monthly и email:generate-upcoming выбирают клиентов через Client::emailActive — то есть «В работе», активные, не отписавшиеся и с заполненным адресом. Раньше месячные письма готовились в том числе клиентам в корзине и на проверке.
- Покрытие - Добавлены 14 тестов: 9 на гейт отправки (включая отмену при удалённом клиенте и при адресе, вышедшем из списка) и 5 на генераторы (активный клиент получает письмо, корзина, проверка и отказ — не получают).
Что важно знать
- Косается только получателя - Содержимое писем, отбор задач, отчёты за период и календарь отчётов не менялись: правка касается только того, кому письмо имеет право уйти.
- Снятые письма не удаляются - Отменённое письмо остаётся в буфере со статусом «Отменено» и причиной — его видно в админке, при необходимости его можно вернуть в очередь вручную.
- Пустые адреса - У клиента без единого адреса любое подготовленное письмо снимается: отправлять письмо, адрес которого не подтверждён ни одним адресом клиента, нельзя.
v0.87.0
30.09.2026
MINOR
Отчёты за период: только задачи, закрытые внутри него
ТЗ «Отчёты перестали повторять старые задачи». Список выполненных в отчётах больше не строится как «все completed, limit(5)»: добавлена дата закрытия задачи tasks.completed_at, которая проставляется в момент реального перехода в статус «выполнена». В письмо попадает только то, что закрыто внутри выбранного периода — недели, месяца, календарного сезона или года. Каждое отсеяние объясняется причиной, администратор видит состав отчёта в предпросмотре и может снять задачу вручную, а повторная отправка за тот же период больше не дублирует уже отправленные задачи. Версия v0.87.0.
Что сделано
- Дата закрытия - В таблицу задач добавлено поле completed_at. Оно заполняется только в момент фактического перехода задачи в статус «выполнена», а не при каждом сохранении: иначе любая пересинхронизация старой задачи проставила бы ей сегодняшнее число и отчёт за тот период втянул бы чужую работу.
- Отчётный период - Появился сервис периодов: неделя (пн–вс), месяц, календарный сезон (зима — декабрь–февраль через два года, весна — март–май, лето — июнь–август, осень — сентябрь–ноябрь) и год. Период письма хранится в его метаданных, поэтому пересборка и предпросмотр старого письма не «уезжают» на текущий период.
- Прозрачный отбор - Новый отборщик возвращает и включённые задачи, и отсеянные с причиной: завершена вне периода, скрыта от клиента, нет задач на платформе, нет даты завершения, уже была в отправленном отчёте, снята вручную. Причина показывается и в CLI, и в предпросмотре в админке.
- Ручное исключение - В предпросмотре «Итоги недели» появилась панель «Состав отчёта»: задачи с галочками уходят клиенту, снятая галочка сохраняется в письме и учитывается как при предпросмотре, так и при реальной отправке.
- Защита от повторов - Уже отправленные за этот же период задачи не попадают в новое письмо повторно. Учитываются только реально отправленные письма, поэтому пересборка письма не исключает задачи по собственным же данным этого письма.
- Команды - mail:send-reports получил параметры --period (week|month|season|year) и --at, а --dry-run теперь показывает состав отчёта с причинами отсечения. Месячное письмо 1-го числа подписывается итогами прошлого месяца: 1 октября — это итоги сентября.
Что важно знать
- Старые закрытые задачи - Даты закрытия у них достоверно восстановить нельзя: в базе нет ни одного источника точной даты, а updated_at перезаписывается синхронизацией (все 605 закрытых задач лежат на 5 датах запусков синка). Автоматически проставлять им дату нельзя — весь бэклог попал бы в отчёт за сентябрь. Поэтому у них completed_at остаётся пустым: в отчёт они не попадают и видны в предпросмотре с причиной «Нет даты завершения».
- Осознанное решение оператора - Новая команда tasks:assign-completed-at проставляет дату вручную: по конкретной дате или по концу периода, с ограничением по проекту и лимиту, с --dry-run.
Что не сломалось
- Письма обновлений - Рассылка обновлений проекта (task_update) не менялась: она по-прежнему строится по событиям синхронизации.
- Текущие в работе - Списки задач «в работе» и планы не фильтруются по периоду — период относится только к выполненным.
- Календарь отчётов - Поиск писем в календаре «Итоги недели» теперь учитывает оба типа служебных писем (report и reports), поэтому плановые и фактические письма больше не теряются из списка.
v0.86.0
30.09.2026
MINOR
Изолированный просмотр письма в админке
ТЗ «Дизайн стилей при просмотре письма». Просмотр письма в /admin/emails/{id} переведён на изолированный рендер: содержимое письма отдаётся отдельным HTML-документом (iframe srcdoc на странице и полноценный standalone-документ по маршруту /admin/emails/{id}/preview), поэтому стили письма больше не протекают в админку (чёрный текст на чёмном фоне, белые кнопки с белым текстом), а стили админки не ломают письмо. Добавлена кнопка «Открыть письмо» — открывает письмо отдельным документом в новой вкладке. Версия v0.86.0.
Что сделано
- Изоляция превью - В show.blade.php email больше не встраивается «сырым» HTML в админ-лейаут: он рендерится в iframe srcdoc как отдельный автономный документ. Стили письма и админки больше не пересекаются ни в одну сторону.
- Отдельный просмотр - Добавлена кнопка «Открыть письмо»: письмо открывается полноценным standalone-документом по существовавшему, но не закрытому маршруту /admin/emails/{id}/preview (view admin.prepared-emails.preview теперь возвращает email напрямую, а не отсутствующий шаблон).
- Общая логика рендера - Логика сборки email вынесена в приватный renderEmailHtml() и переиспользуется и на странице, и в маршруте preview; частичное тело (task_status без DOCTYPE) оборачивается в минимальный автономный документ.
Что не сломалось
- Сами письма - Публичный preview (preview-public) и отправляемые тела не изменены; изолировано только отображение в админке.
- Админка - Тема, кнопки и панели админ-интерфейса остаются в своих стилях при просмотре любого письма.
v0.85.3
30.09.2026
PATCH
Дозированная отправка писем на один ящик
ТЗ 263 «Дозированная отправка». Письма на один и тот же почтовый ящик (получатель ИЛИ BCC-копия администратору) теперь уходят с паузой по умолчанию 5 секунд (настраивается MAIL_SAME_RECIPIENT_INTERVAL), чтобы адресат не получил залп из десятков писем разом — типичный триггер спам-фильтров. Разным получателям письма отправляются сразу. Очередное письмо дожидается паузы только если в тот же ящик реально ушло предыдущее; пропущенные дубли и dry-run паузу не сбивают. Версия v0.85.3.
Что сделано
- Интервал для одного ящика - В email:send-prepared добавлена дозировка: письма на один ящик (to_email или BCC админиистрации) отправляются с паузой по умолчанию 5 секунд (ключа MAIL_SAME_RECIPIENT_INTERVAL в .env). Ранее ящик с 30–70 письмами (например, 43 обновления для info@ipd.by + копии администратору) получал всё одним залпом.
Что не сломалось
- Разные получатели - Письма разным клиентам отправляются без задержки; пауза считается только для совпадающих ящиков.
- Дубли и dry-run - Пропущенные дубли и режим --dry-run не влияют на дозировку.
v0.85.2
30.09.2026
PATCH
Обновления проекта — полные карточки задач и обращение «Отец Николай»
ТЗ 262 «Обновления проекта как 5468». Все queued/draft письма «Обновление проекта» (62 письма, № 5552–5613) пересобраны по архитектуре эталона 5468: изменения сгруппированы по задачам, каждая задача показана полной карточкой (чип статуса, прогресс-бар, даты «Задача поставлена / Планируем завершить», этапы с галочками, описание), события — бейджами с датами. Обращение к духовным лицам («Отец Николай», «Отец Досифей») больше не усекается до «Отец» — greeting в письмах «Здравствуйте, отец Николай!». Месячные письма отца Николая (6 писем, № 5409–5414 на октябрь 2026 – март 2027) пересобраны с новым обращением. Версия v0.85.2.
Что сделано
- Карточки задач в обновлениях - 62 queued/draft письма «Обновление проекта» пересобраны: события восстановлены из уведомлений клиента (группировка по минуте создания), изменения сгруппированы по задачам, каждая задача рендерится полной карточкой EmailTaskCards (статус, прогресс, даты, этапы, описание) с бейджами событий («Новая задача», «Выполнен шаг», «Статус задачи изменён»).
- Обращение к духовным лицам - Client::first_name больше не усекает «Отец Николай» до «Отец»: для имён с титулом «Отец»/«Батюшка» возвращается полное имя, greeting — «Здравствуйте, отец Николай!».
- Месячные письма отца Николая - 6 месячных дайджестов клиента 23 (№ 5409–5414) пересобраны с новым обращением на все запланированные месяцы.
Что не сломалось
- Обычные клиенты - Для имён без титула по-прежнему используется первое слово («Егор Савинский» → «Егор»).
- Отправленные письма - Уже отправленные письма не изменялись.
v0.85.1
30.09.2026
PATCH
Месячные дайджесты пересобраны в цветных карточках с обращением по имени
ТЗ 261 «Месячные письма как 5468». Queued месячные дайджесты (24 письма на 01.10.2026–01.03.2027) пересобраны по архитектуре эталона 5468: вместо плоских зелёных блоков задачи рендерятся карточками со чипом статуса, прогресс-баром, датами «Задача поставлена / Планируем завершить» и описанием. Обращение в письме теперь по имени («Здравствуйте, Егор!»), а не по ФИО. Синхронизирован БД-шаблон monthly-digest с богатым телом; отправленные сентябрьские письма не тронуты. Удалены 6 дублей-копий старого шаблона и 3 устаревших черновика. Письма Марины Лакатун (клиент 24) перенаправлены с удалённого проекта «Ришелье стиль» (355) на актуальный проект 371; месячные письма клиентов Татьяны (25) и Любови (26), исключённых из рассылки, удалены. Версия v0.85.1.
Что сделано
- Карточки задач в месячных письмах - 24 queued месячных дайджеста на 01.10.2026–01.03.2027 пересобраны через MonthlyEmailRenderer: задачи рендерятся цветными карточками со чипом статуса («Выполнена», «В работе»), прогресс-баром, датами постановки и завершения и описанием. Шаблон monthly-digest в БД синхронизирован с богатым телом.
- Обращение по имени - Письма начинаются с «Здравствуйте, Егор!» вместо «Здравствуйте, Егор Савинский!»: получатель подставляется из Client::address_name (первое слово имени).
- Уборка очереди - Удалены 6 дублей месячных писем Егора (5455–5458, 5460–5461, старый короткий формат) и 3 устаревших черновика (5369, 5371, 5372).
- Клиенты «Ришелье стиль» - Письма Марины Лакатун (клиент 24) перепривязаны с удалённого проекта 355 на актуальный проект 371 и пересобраны; месячные письма Татьяны (25) и Любови (26), больше не включённых в рассылку, удалены.
Что не сломалось
- Отправленные письма - Сентябрьские месячные дайджесты со статусом sent (5373, 5380, 5387, 5394, 5408, 5415) не изменены.
- Другие типы писем - Шаблоны недельных и изменений проекта не затронуты.
v0.85.0
30.09.2026
MINOR
Несколько email у человека: основной и дополнительные
ТЗ 260 «Несколько email». У человека на платформе задач может быть несколько email — один основной. Кнопка «Синхронизировать с платформы задач» привела email клиента и список всех его адресов к данным платформы: основной email (по нему уходят письма и идёт вход в кабинет) обновляется у той же записи, а полный список хранится в новой колонке clients.emails и виден в карточке пользователя (основной — с бейджем «основной»). История писем при смене ящика не теряется: она хранится по client_id и фактическому адресу в prepared_emails.to_email / mail_logs, поэтому старые отправленные письма остаются у старого ящика, а новые уходят на новый. Письма Егора Савинского (16 писем в очереди и черновиках) переадресованы на новый основной ящик esav2@yandex.ru. Версия v0.85.0.
Что сделано
- Полный список адресов - Новая колонка clients.emails (JSON): основной email живет в clients.email (вход, верификация, адресация писем), остальные адреса подтягиваются с платформы задач и хранятся списком. Дубликаты по регистру отбрасываются, основной адрес всегда первый.
- Синхронизация контактов кнопкой - Кнопка на карточке пользователя обновляет email и список адресов до импорта проектов: у проектов платформы задач есть только «человек_почта» (внешнего id человека на проекте нет), поэтому проект человека связывается с клиентом по актуальному email. В сообщении об успехе появилась строка «Email обновлён: … → …». Телефон и Telegram больше не затираются пустыми значениями, когда платформа их не прислала.
- Карточка пользователя - В /admin/users/{id} основной email показан с бейджем «основной», дополнительные адреса — списком; текст кнопки «Синхронизировать с платформы задач» и подтверждение теперь упоминают контакты.
- Переадресация писем Егора - 16 писем в очереди и черновиках (клиент 5, Егор Савинский) переписаны на новый основной ящик esav2@yandex.ru; отправленные письма остались в истории у старого ящика (egor@jetpeel.club).
Что не сломалось
- Вход в кабинет - Пароль и email_verified_at при синхронизации не трогаются, вход не зависит от смены ящика.
- История писем - Старые письма остались видны в карточке клиента: фильтр идёт по client_id, а фактический адрес хранится в prepared_emails.to_email / mail_logs.
v0.84.0
29.09.2026
MINOR
Письма снова в зелёных плашках: статусы вернулись к эталону 5468
ТЗ 259 «Письма как 5468». В обновлении 0.77.2 (ТЗ 247) письма-уведомления о задачах посерели: зелёные плашки «выполнено», цвета статусов и синий призыв заменили на нейтральный серый, и письмо слилось в один безликий блок. Вернули цветную архитектуру эталона 5468 и распространили её на письма, которые готовились к отправке. Статусы задач, галочка «выполнено», блок «Обслуживание и поддержка», регламент и кнопка личного кабинета снова цветные. Вступительный текст письма стал зависеть от проекта: у проекта «Медтехника Гродно» — прежний текст, у остальных — общий. Писем, лежащих в очереди отправки, это касается напрямую: их тела пересобраны по новой архитектуре. Автотест проверяет, что в шаблонах писем статусы не серые, а цветные (эталон 15/15).
Что сделано
- Цветные статусы и галочки - В письме «Статус задач» и в «Изменениях проекта» восстановлены зелёные плашки выполненных задач и шагов (#16a34a), синий призыв к действию (#1d4ed8), плашки статусов (в работе — синяя, выполнено — зелёная, ожидает — янтарная, запланировано — серая-тёплая). Серый фолбэк #334155 из шаблонов убран.
- Блоки поддержки и регламента - Блок «Обслуживание и поддержка» — на том же зелёном фоне (#f0fdf4), что и на странице SLA; регламент поддержки — в синем блоке (#eff6ff) со ссылкой calc.ipd.by/sla. Кнопка «Открыть личный кабинет» снова синяя (#1d4ed8).
- Письма в очереди пересобраны - 62 письма «Изменения проекта» (task_update), готовящиеся к отправке, переотрисованы новой архитектурой: события реконструированы из уведомлений клиента, число изменений в каждом письме совпадает с темой. Письма-эталон «Статус задач» (5268) не тронуты.
- Вступление зависит от проекта - defaultIntro() теперь получает проект: для «Медтехника Гродно» сохранён прежний текст, для остальных проектов — общий вступительный текст с именем проекта.
Что не сломалось
- Контраст кнопок в админке - Исправление 0.83.1 (ТЗ 258) не затронуто: письма — это отдельные вёрстка и токены, кнопки в административном интерфейсе остались читаемыми.
- Предпросмотр и правки текста - Поле «Вступительный текст» (ТЗ 178) продолжает работать: свой вступление остаётся как введено.
v0.83.1
29.09.2026
PATCH
Кнопки и плашки, которые перестали «светиться белым по белому»
ТЗ 258 «Контраст кнопок». Кнопки «Новое письмо», «Найти», «Сохранить», «У нас / У клиента» и им подобные на светлой теме выглядели как серые прямоугольники, а надпись на них не читалась: белая надпись лежала на светлой заливке. Причина была одна и системная — заливка кнопки бралась из токена темы, который в тёмной теме тёмный, а в светлой — светлый, и надпись на нём всегда оставалась белой. Вместо «цвет под текущую тему» появились отдельные токены заливки: --btn-solid, --btn-solid-hover и --on-btn-solid. Они одинаковы в тёмной и светлой темах, поэтому надпись читается в обеих (контраст от 6:1). Попутно та же правка убрала такие же белые надписи на зелёных и красных ховер-состояниях Telegram-виджета и формы «Оставить заявку», на аватарах, на бейдже непрочитанных уведомлений, на тостах, на кнопке закрытия в просмотре версий и на кнопке в письме-напоминании. Кнопки действий в разметке переведены с inline-стилей на классы btn btn-primary / btn-sm. Эталон тестов 321 passed, эталон сборки vite build.
Что сделано
- Отдельные токены заливки кнопок - --btn-solid / --btn-solid-hover / --on-btn-solid заданы в тёмной теме, в светлой теме и в фолбэке по системной теме. Кнопка, плашка, аватар и тост больше не берут заливку из --accent, который меняет полярность вместе с темой, и больше не зависят от «цветного» токена как от фона под белым текстом.
- Кнопки действий на классах - «Новое письмо» и «Найти» в /admin/emails, «Сохранить» в письмах, настройках, профиле, безопасности, шаблонах писем, KPI и расписании, «Настроить» у получателей уведомлений, «Все письма» в напоминаниях, «Изменить пароль», переход в тарифы — переведены с inline-стилей на btn btn-primary / btn-sm.
- Убраны белые надписи на цветном - Ховеры Telegram-виджета и формы «Оставить заявку» (включая зелёный WhatsApp), аватары в шапке и у получателей, бейдж непрочитанных, тосты (успех/ошибка/информация), кнопка закрытия в просмотре версий, переключатель «Мяч у нас / У клиента» — теперь используют токены заливки. Заодно добавлены в фолбэк темы недостающие --accent-solid, --danger-solid и --success-solid, из-за чего кнопки теряли заливку, если тему выбирала система, а не человек.
- Письмо и превью - Кнопка в письме-напоминании о статусе мяча и дата в превью письма в админке переведены на нейтральный тёмный фон с читаемой надписью.
Что не сломалось
- Цвета статусов - Красный, жёлтый и синий не тронуты: сигнал остался сигналом, а «статус» нигде не стал зелёным.
- Вид кнопок - Заливка и рамка в обеих темах те же, что были (тёмно-серый #475569, наведение #334155), меняется только источник цвета.
v0.83.0
29.09.2026
MINOR
Пользователи: постоянные адреса разделов и «В работе» по умолчанию
ТЗ 257 «Пользователи, ссылки». Слева в меню было одно слово «Пользователи», и по нему открывался общий список всех людей: сначала нужно было догадаться, где сейчас тот, с кем мы работаем, а потом ещё переключиться на нужную вкладку. Теперь у каждого состояния свой постоянный адрес: /admin/users — люди, с кем работаем сейчас, /admin/users/review — на проверке, /admin/users/trash — в корзине, /admin/users/deleted — удалённые, /admin/users/all — все вместе (нужен редко, для сплошной проверки, поэтому стоит последним и тихим). Вкладки «?bucket=…» заменены адресами, поэтому внутрь любого раздела можно добавлять под-страницы, а ссылку можно отправить другому человеку — она всегда откроет то же самое. Старые ссылки с вопросиком не сломаны: такой адрес один раз переводит на постоянный и дальше живёт по нему. Версия v0.83.0.
Что сделано
- «Пользователи» открывает тех, с кем работаем - Вход в раздел по умолчанию — вкладка «В работе» (/admin/users). Порядок вкладок повторяет жизнь человека: В работе → На проверке → В корзине → Удалённые, а «Все» — последняя, через разделитель и приглушённая: это сплошной список, в который заглядывают раз в месяц.
- Постоянные адреса вместо вопросика - /admin/users/review, /admin/users/trash, /admin/users/deleted, /admin/users/all. Внутрь адреса можно дописывать под-страницы (например /admin/users/active/import), поэтому развивать раздел дальше можно, не переделывая ссылки. Неизвестное состояние в адресе — честная 404, а не молчаливый показ «В работе».
- Старые ссылки не теряются - Адрес вида /admin/users?bucket=review один раз переводит на постоянный /admin/users/review и дальше человек живёт по нему; фильтры и поиск при этом сохраняются.
- Раздел отвечает на вопрос, а не на настроение - Заголовок страницы называет раздел словами («Пользователи», «Пользователи · на проверке», «Все пользователи»), счётчики вкладок остались, у каждой вкладки подписьtitle объясняет, что это за состояние, а у пункта меню — подсказка с адресами разделов.
v0.82.0
29.09.2026
MINOR
Корзина пользователей: «На проверке», «В корзине», «Удалённые»
ТЗ 256 «Корзина пользователей». У человека был один переключатель «Активен/Неактивен»: выключенный пользователь просто исчезал из рассылок, но оставался в общем списке без следа, почему его больше нет, и без возможности вернуть его в работу. Теперь у пользователя отдельный статус-корзина из четырёх состояний: «В работе» → «На проверке» → «В корзине» → «Удалённые», и каждое состояние возвращается в работу одним действием. Статус не пересекается с переключателем «Вход» (is_active): статус решает, отправляем ли мы человеку письма и отчёты, а «Вход» — пускаем ли мы его в личный кабинет. На странице пользователей появились вкладки с постоянными счётчиками («Все · N / В работе · N / На проверке · N / В корзине · N / Удалённые · N»), отдельная колонка «Статус» с бейджем и новые пункты кебаб-меню: «На проверку», «В корзину», «Вернуть в работу», «Восстановить», «Удалить навсегда». «Удалить» стало мягким: запись уходит в «Удалённые» вместе со своими проектами и письмами, её можно восстановить; «Удалить навсегда» доступно только оттуда и вычищает связанные данные. Письма и отчёты не отправляются всем, кто вне «В работе»: гейт стоит в scopeEmailActive, в подборке получателей кампаний, массовых уведомлений, недельных отчётов и статусов задач, а вход закрыт в логине, смене аккаунта, автовходе и восстановлении пароля. Версия v0.82.0, эталон тестов 312 passed.
Что сделано
- Статус у пользователя - Новая колонка clients.status (active / review / trash / deleted, по умолчанию active) и константы Client::STATUS_*. Хелперы: isOperational() — «в работе», статус-подпись «В работе / На проверке / В корзине / Удалён». Статус независим от is_active: один закрывает письма, другой — вход.
- Вкладки на странице пользователей - /admin/users: вкладки с живыми счётчиками («Все», «В работе», «На проверке», «В корзине», «Удалённые»), вкладка «Все» скрывает удалённых, фильтры и сортировка работают внутри вкладки, сортировка добавлена по статусу. Бывший фильтр «Статус» переименован в «Вход» (Открыт/Закрыт) — чтобы «статус» значил одно.
- Кебаб-меню по статусу - «На проверку» и «В корзину» — у человека в работе; «Вернуть в работу» и «На проверку» — в корзине; «Восстановить» и «Удалить навсегда» — в удалённых. «Войти как» показывается только для людей в работе.
- Мягкое удаление - «Удалить» переводит запись в «Удалённые» (данные, проекты и письма сохраняются), оттуда — «Восстановить» или «Удалить навсегда» с вычисткой связанных записей. Обе операции пишутся в журнал действий.
- Письма и вход - Гейт статуса добавлен туда, где выбираются клиенты: scopeEmailActive, получатели кампаний (всех типов) и массовые уведомления, недельные отчёты и статусы задач. Вход закрыт в логине, восстановлении пароля, смене аккаунта и автовходе по устройству; NextStepService подсказывает администратору, что человек вне работы, и ведёт на нужную вкладку.
v0.81.0
28.09.2026
MINOR
Вкладки в форме пользователя: редактирование без простыни
ТЗ 255 «Вкладки». Форма редактирования пользователя собирала все поля в одну простыню: постоянные данные (имя, обращение, email, компания, телефон, Telegram, подтверждения контактов) и то, что правится постоянно (роль, проекты, доступ к платформам), были на одном экране — человек прокручивал десятки полей, чтобы добраться до нужного. Форма разбита на вкладки одного экрана по образцу платформы задач (task.ipd project-edit / person-edit) и встроенной страницы проекта: «Основная информация», «Настройки», «Проекты», «Платформы». Один экран, одна форма и одна кнопка «Сохранить»: вкладки только группируют поля, маршрут обновления не изменился, внизу панель «Сохранить/Отмена/Безопасность» с подсказкой «Все вкладки — одна форма: данные сохраняются одной кнопкой». Кебаб-меню платформы научилось открываться от края окна: кнопка внизу (низ карточки пользователя) раскрывает меню вверх, а не за край экрана, у левого края — вправо, у правого — влево; авто-направление — `positionKebabMenu()` в `toggleKebab()` (`layouts/app.blade.php`). Правило зафиксировано в методологии: «03 Стиль интерфейса» раздел 7.1 (длинные формы — вкладки) и 5.1 (открытие кебаб-меню), «02 Правила разработки» — блоки проверки. Эталон тестов 305 passed.
Что сделано
- Вкладки в редактировании пользователя - /admin/users/{id}/edit стал одним экраном с четырьмя вкладками на штатных классах .tabs/.tab-btn/.tab-panel (те же, что у проекта): Основная информация / Настройки / Проекты / Платформы. Переключение — JS по data-tab, активная по умолчанию — «Основная информация».
- Группировка по смыслу работы - Постоянные данные, которые записывают один раз и редко трогают (имя, обращение, email, компания, телефон, Telegram, подтверждения контактов), — первая вкладка; служебное (роль, пароль, связь личного кабинета, активность) — «Настройки»; «над чем работают постоянно» (проекты-владелец, проекты-доступ, платформы-доступ) — отдельные вкладки.
- Методология - В «03 Стиль интерфейса» добавлен раздел 7.1 «Много полей — вкладки одного экрана»: полей больше 4–5 — не простыня, а вкладки; один экран, одна форма, одна кнопка сохранения; вкладки не создают новых URL; группировка по смыслу. В «02 Правила разработки» — блок «Длинные формы — вкладки одного экрана» с проверкой DesignSystemRenderTest (data-tab на странице пользователя) и view:cache.
v0.80.1
28.09.2026
PATCH
Мусорные проекты убраны: откуда они взялись и почему не вернутся
ТЗ 254 «Локальные проекты». Найдена причина, по которой в списке проектов появлялись пустые строки с бейджем «Локальный»: их создавал сам DatabaseSeeder — при каждом запуске `php artisan db:seed`, то есть при каждой смене версии платформы, он вставлял пять проектов-шаблонов каждому клиенту без проверок. Один запуск 25.09.2026 в 20:40:16 дал 93 проекта с нулём задач, нулём участников и нулём писем, а заодно привязал четыре демо-токена к пустым проектам. Демо-данные вынесены в отдельный DemoSeeder, который запускается только на пустой базе и больше не вызывается при смене версии; учётная запись администратора больше не переписывается (пароль не сбрасывается, дата создания не затирается). Из базы удалены 93 мусорных проекта и 4 демо-клиента вместе с их токенами.
Что было
- Источник мусора - DatabaseSeeder::seedProjects() вставлял пять проектов-шаблонов («Разработка сайта», «SEO-продвижение», «Мобильное приложение», «Техподдержка», «Дизайн-проект») каждому клиенту, кроме администратора, без проверки на повтор. Штатная процедура смены версии платформы требует `php artisan db:seed`, поэтому мусор появлялся сам, без участия человека. Доказательство: у всех 93 проектов одна и та же дата created_at 2026-09-25 20:40:16, и в ту же секунду в versions появилась запись 0.77.1.
- Что было на самом деле - Из 135 проектов 41 имел external_id (настоящие, с платформы задач), 1 был локальным, но настоящим («Ришелье стиль», 22.08.2026, 3 участника, 123 письма, папка Total Commander), и 93 были пустыми заготовками.
- Откуда взялись «непонятные люди» - Иван Петров (client@ipd.by), Тестовый Пользователь (test@ipd.by) и Демо Клиент (demo@ipd.by) создавались ClientSeeder, а Тестовый Клиент (client@project.ipd.test, 29.07.2026) был заведён вручную. Тестовый Пользователь — единственный с ролью test.
Что изменено в коде
- DemoSeeder - Новый сидер database/seeders/DemoSeeder.php: три демо-клиента, пять проектов-шаблонов на каждого и токены. DatabaseSeeder его не вызывает. Запуск только на пустой базе: если в projects есть хоть одна строка или в clients есть хоть один не-администратор, сидер бросает RuntimeException с объяснением.
- DatabaseSeeder - Вызывает только идемпотентные сидеры: TariffSeeder, ClientSeeder, ClientTaskTemplateSeeder, VersionSeeder. Методы seedProjects() и seedProjectTokens() удалены.
- ClientSeeder - Осталась только учётная запись администратора и её токен. Создаёт их, только если их нет: повторный запуск больше не переписывает created_at и не сбрасывает пароль на admin123 (раньше updateOrInsert делал именно это при каждой смене версии).
- Версия - v0.80.1 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.80.0
27.09.2026
MINOR
Состав проекта: откуда взялся каждый участник и журнал изменений
ТЗ 251 «Куча пользователей в проекте». Найдена причина, по которой в проекты попадали люди, которых никто не добавлял: синхронизация с платформой задач молча прикрепляла каждого участника из project_ids и при этом ещё раз прикрепляла владельца проекта как обычного участника. Теперь привязка владельца пропускается, а каждая новая связь получает дату, источник и автора. Управление составом вынесено на отдельную страницу /admin/projects/{id}/members: видно, кто привязан, когда, откуда (вручную в my.ipd или синхронизацией с платформой задач), кем добавлен и было ли это массовым заходом; там же — история изменений состава. На странице проекта широкая таблица пользователей заменена компактной карточкой со сводкой и переходом на новую страницу, добавление участника получило свой адрес /members/create, а сломанная вёрстка шапки страницы (незакрытый div, из-за которого вся страница попадала в flex-блок) исправлена.
Что сделано
- Найдена причина массовых привязок - SyncManager::linkClientsToProjects() и привязка владельца при импорте проектов прикрепляли каждого человека из project_ids платформы задач без проверки: владелец проекта оставался ещё и в списке участников, источник и дата не сохранялись, в журнал ничего не писалось. На тестовом стенде это дало 41 дубль «владелец = участник» из 53 связей и клиента №6 с 30 проектами.
- Источник каждой связи - Новая колонка provenance в client_project: attached_at (дата привязки), attached_by (кто привязал) и attached_source (manual — вручную в my.ipd, sync — синхронизация с платформой задач, unknown — источник неизвестен). Существующие связи не переписываются выдуманными данными: attached_at перенесён из created_at, источник помечен unknown, потому что по старым данным восстановить его нельзя.
- Журнал изменений состава - В журнал действий добавлены actor_name (снимок имени автора) и source (web / sync / system / api). Каждая привязка, снятие, назначение и снятие владельца пишется отдельной записью с автором и источником; массовая синхронизация добавляет сводную запись о числе добавленных и пропущенных связей. Слова «удалён» в журнале заменены на «снят с проекта»: данные не удаляются, а отключаются.
- Отдельная страница участников - Новая страница /admin/projects/{id}/members (admin.projects.members) со сводкой (всего людей, владелец, участники, вручную, синхронизацией, источник неизвестен, дублей владельца, массовых заходов), блоком «Откуда взялись участники» человеческим языком, таблицей с ролью, датой, автором, источником и пометками «Дубль владельца» и «Массовый заход», историей изменений состава и действиями в кебаб-меню: сделать владельцем, снять владельца, снять с проекта. Добавление участника вынесено на /admin/projects/{id}/members/create со своим адресом; повторное добавление владельца или текущего участника блокируется понятным сообщением.
- Разбор состава - Новая функция ProjectMembershipService::explain() отвечает по каждой строке на четыре вопроса: кто человек (владелец или участник), когда появилась связь, откуда и кто её создал. Массовый заход определяется по одинаковой секунде привязки. Владелец показывается всегда, даже если отдельной связи-участника у него нет, — иначе им нельзя управлять.
- Страница проекта - Вместо широкой таблицы пользователей — компактная карточка: сводка по источникам, владелец, первые участники с датой и источником, переходы «Все участники и журнал» и «+ Добавить». Инлайн-форма добавления убрана. Исправлена сломанная вёрстка: блок page-head не закрывался, из-за чего вся страница находилась внутри flex-шапки. Убраны оставшиеся жёстко заданные цвета (фиолетовая подсветка строки владельца, синие иконки писем) и синие бейджи статусов — используются переменные темы и нейтральные бейджи.
- Проверка - Новые тесты tests/Feature/ProjectMembersPageTest.php (9 проверок: рендер страницы и объяснение источников, форма добавления, запись provenance и журнала, запрет дубля владельца, назначение владельца со связью-участником, снятие с проекта без слова «удалён», ссылка со страницы проекта, разбор дубля и массового захода, неизвестный источник старых связей) и tests/Feature/SyncProjectMembershipFeatureTest.php (3 проверки: синхронизация не прикрепляет владельца повторно, новая связь помечена источником sync и попадает в журнал, существующая связь не переписывается). Обновлены ProjectOwnerRolesFeatureTest, AdminPeopleExportWizardFeatureTest и ClientPortfolioFeatureTest под исправленное поведение синхронизации. Полный прогон: 280 тестов (1624 проверки), php artisan view:cache — без ошибок.
- Версия - Текущая версия платформы: v0.80.0 (.env, .env.example, config/app.php, VersionSeeder, README.md, DESIGN_SYSTEM.md).
v0.79.0
27.09.2026
MINOR
Проекты клиента: отдельная страница с источником каждого проекта и отключением лишних
ТЗ 250 «Проекты клиента». Список проектов человека вынесен с карточки пользователя на отдельную страницу /admin/users/{id}/projects и отвечает на вопрос «почему их восемь, если проект один». Для каждого проекта видно, откуда он: пришёл с платформы задач (с её ID и датой синхронизации) или создан вручную в my.ipd, роль человека (владелец или участник), статус, дата добавления и реальное число задач. Отдельный блок «Откуда взялись проекты» объясняет состав портфеля цифрами, а проекты без задач и без связи с платформой помечаются как «Без задач», а созданные в одну секунду одинаковыми заходами у разных клиентов — как «Массовое добавление». Лишний проект можно отключить одним действием из меню «⋮»: он уходит в архив, перестаёт быть активным и попадать в отчёты и письма, но задачи и история остаются на месте, а действие обратимо.
Что сделано
- Отдельная страница - Новая страница /admin/users/{id}/projects (маршрут admin.users.projects, Admin\AdminUserController@projects, вьюха resources/views/admin/users/projects.blade.php) со своим адресом: сводка, объяснение состава, фильтр по разделам «Все / Активные / Отключённые и закрытые», таблица проектов и действия в кебаб-меню. Карточка «Проекты (всего)» на странице пользователя стала ссылкой на неё, в разделе «Проекты» вместо таблицы — кнопки «Все проекты клиента» и «Проверить проекты на платформе задач».
- Откуда взялся каждый проект - Новая функция ClientPortfolioService::explainPortfolio() разбирает портфель человека: источник (external_id платформы задач с датой синхронизации или ручное создание с датой добавления), роль (владелец/участник), статус, прогресс, дата завершения, реальное число задач одним сгруппированным запросом и число «пустых» проектов. Массовое добавление определяется по одинаковой секунде created_at: если в эту секунду появились такие же проекты (в том числе у других клиентов) — проект помечается как массовый, с указанием, сколько их было.
- Почему список длиннее, чем кажется - На странице сводка (всего, активных, отключённых и закрытых, с платформы задач, создано вручную, без задач) и блок «Откуда взялись проекты» с пояснением по каждой группе. На тестовом стенде это объяснило клиента №5: 3 проекта с платформы задач (Бобер Боб, Mysteria, Алексей Гапеев) и 5 созданных вручную 25.09.2026 в 20:40 одним заходом вместе с такими же проектами 19 других клиентов (всего 95 штук) — пустые, без задач.
- Управление проектом - Отключение и возврат в работу одним действием из кебаб-меню строки (POST /admin/users/{id}/projects/{projectId}/status, admin.users.projects.status): отключение ставит статус archived, возврат — active. Проект не удаляется: задачи, история и карточка остаются. Действие пишется в журнал (user_project_disabled / user_project_enabled). Защиты: нельзя отключить текущий проект и нельзя отключить последний активный проект человека — сначала нужно выбрать другой текущий проект; нельзя отключить проект, который человеку не принадлежит.
- Проверка - Новый тест tests/Feature/ClientProjectsPageTest.php — 13 проверок: свой адрес и состав списка, источники и ID платформы, пометки «Без задач» и «Массовое добавление», фильтр разделов, отключение и возврат с журналом, защита текущего проекта (выбранного и выбранного автоматически), защита последнего активного, чужой проект, ссылка со страницы пользователя, рендер без проектов и завершённого проекта. Существующий тест ClientPortfolioFeatureTest переведён на новую страницу. Полный прогон: 267 тестов (1559 проверок), сборка CSS и php artisan view:cache — без ошибок.
- Версия - Текущая версия платформы: v0.79.0 (.env, .env.example, config/app.php, VersionSeeder, README.md, DESIGN_SYSTEM.md).
v0.78.0
26.09.2026
MINOR
ИИ-агент под управлением: переключатель, шестерёнка и статистика
ТЗ 249 «Спросить у ИИ — настройка показа». ИИ-агент стал управляемым: администратор может одним переключателем показать или скрыть его для всей платформы и отдельно — для любого пользователя, а сам пользователь решает, видеть ли агента в своём кабинете. В заголовке окна агента появилась шестерёнка, которая ведёт прямо в настройки. Каждое открытие агента теперь записывается, и администратор видит статистику: сколько раз открывали окно, за 7 и 30 дней, сколько человек пользуются агентом, сколько у них просмотров страниц, а также самые популярные страницы, с которых открывали агента.
Что сделано
- Общая логика - Создан сервис app/Services/AiAgentService.php — единая точка правды о видимости агента: глобальный флаг в platform_settings (ai_agent_enabled, по умолчанию включён), персональный флаг в clients (ai_agent_enabled), запись каждого изменения в settings_history и адрес страницы настроек для администратора и пользователя. Виджет в resources/views/layouts/app.blade.php подключается только если агент разрешён глобально и лично.
- Переключатели - В админке появилась вкладка «ИИ-агент» (/admin/settings/ai-agent) с общим переключателем «Показывать ИИ-агента на платформе» и таблицей пользователей, где у каждого есть свой переключатель. В кабинете человека появилась страница /settings/ai-agent с переключателем «Показывать мне ИИ-агента» и личной статистикой.
- Шестерёнка в окне агента - В заголовке всплывающего окна ИИ-агента добавлена кнопка-шестерёнка (id=chatSettingsBtn) рядом с крестиком: администратору она ведёт в /admin/settings/ai-agent, пользователю — в /settings/ai-agent. Если агент скрыт, окна нет, а значит нет и кнопки.
- Логирование - Каждое открытие окна агента записывается как действие пользователя с типом ai_agent_open — вместе со страницей, с которой открыли, и её названием; заодно обновляются число открытий и время последнего открытия сессии агента. Само событие открытия больше не засчитывается как просмотр страницы: посещаемость остаётся в существующих счётчиках page_view.
- Статистика - Администратор на вкладке «ИИ-агент» видит: число открытий (всего, за 7 и 30 дней), сколько человек пользуются агентом, просмотры страниц пользователей (всего и за 7 дней), число сессий агента, сообщений и заявок, а также самые популярные страницы, с которых открывали агента. В карточке пользователя в админке — статус агента, число открытий, просмотров и последнее открытие со ссылкой на настройки. Пользователь на своей странице видит свои открытия, просмотры и последнее открытие.
- Миграция и проверка - Миграция 2026_09_26_100000_add_ai_agent_settings_to_clients_table.php добавляет clients.ai_agent_enabled, ai_agent_sessions.client_id, open_count и last_open_at. Новый тест tests/Feature/AiAgentVisibilityTest.php — 9 проверок (видимость по глобальному и личному флагам, переключатели пользователя и администратора, логирование открытия, статистика, порядок истории настроек). Полный прогон тестов, сборка CSS и php artisan view:cache — без ошибок.
- Версия - Текущая версия платформы: v0.78.0 (.env, .env.example, config/app.php, VersionSeeder, README.md, DESIGN_SYSTEM.md).
v0.77.3
26.09.2026
MINOR
Удаление — в меню действий: явных красных кнопок больше нет
ТЗ 248 «Меню, а не красная кнопка». Все разрушительные и редкие опасные действия на платформе — удаление, безвозвратное удаление, снятие и открепление, отзыв токена, ключа и ссылки, отмена отправки, отклонение заявки, завершение сессий, массовое удаление — перенесены в вертикальное кебаб-меню «⋮». Явных красных кнопок удаления в ленте не осталось: красный цвет остался только у пункта скрытого меню, где действие подтверждается дополнительным вопросом. Частые и безопасные действия (сохранить, редактировать, подробнее, одобрить, восстановить, отправить сейчас) остались обычными кнопками. Попутно устранены самодельные меню и вложенная форма тестовой отправки кампании.
Что сделано
- Общее меню действий - Создан общий blade-компонент resources/views/components/kebab-menu.blade.php (вертикальная иконка «⋮», props title/align/class/style, слот пунктов). Все меню на платформе переведены на него: кнопка, форма и разделитель внутри пункта, красный — только у пункта danger.
- Перенесено в кебаб - Удаление и отзыв: карточка проекта, карточка человека, карточка письма, письма и рассылки, кампании, задачи и платформенные задачи, тарифы, заявки на тарифы, платформы, пользователи, настройки (API-ключи), расписание писем, ссылка проекта в кабинете человека. Отмена отправки кампании и письма, завершение сессий и очистка устройств пользователя, отклонение заявки на тариф, массовое удаление писем и осиротевших задач.
- Что осталось видимым - Сохранить, редактировать, подробнее, одобрить, восстановить, отправить сейчас, отправить тест, показать/скрыть, копировать, пересобрать, включить/выключить, активировать ключ. Форма строк инструкций и поддержки в карточке проекта — нейтральная кнопка без красной заливки.
- Попутные правки - Перестал работать псевдокебаб в списке писем — заменён общим компонентом. Самодельное меню расписаний (sc-kebab) заменено общим компонентом, его CSS удалён. Убрана вложенная форма тестовой отправки кампании: форма вынесена из основной, кнопка и поле связаны атрибутом form. Удалён неиспользуемый стиль .kebab-menu__note с 13px. Кнопка выхода из всех аккаунтов на странице входа стала нейтральной, как на втором её варианте.
- Методология - В «03 Стиль интерфейса» раздел 5.1: разрушительные действия — только в кебаб-меню, красный только у пункта меню, безопасные действия не прячутся, опасная зона не используется, вложенные формы запрещены. В «02 Правила разработки» и «04 Темы и дизайн» — проверки, таблица красного и запрет самодельных меню. Версия методологии 1.6.0.
- Проверка - Новый регрессионный тест tests/Feature/DestructiveActionsInKebabTest.php: 4 проверки (нет вложенных форм, разрушительное действие оформлено пунктом кебаб-меню, нет явных красных кнопок удаления, наличие компонента и его стилей). Полный прогон: 245 тестов, сборка CSS и php artisan view:cache — без ошибок.
- Версия - Текущая версия платформы: v0.77.3 (.env, .env.example, config/app.php, VersionSeeder, README.md, DESIGN_SYSTEM.md).
v0.77.2
25.09.2026
MINOR
Серый вместо зелёного: статусы и сообщения об успехе
ТЗ 247 «Цвет, span». Информационные статусы и сообщения об успехе переведены из зелёного в нейтральный серый: зелёные токены дизайн-системы (--green, --green-soft, --success, --success-bg, --success-solid) заменены на серые, бейджи «зелёный/жёлтый» заменены на серые, состояния «в работе», «готово», «отправлено», «в очереди», «сохранено», «проверено» во всех разделах админки, кабинета человека, гостя, мастеров синхронизации и во всех письмах стали серыми. Красный сохранён для проблем, просрочек, запретов и потерь; жёлтый — только для реального внимания и срочности; синий — для ссылок, бренда и информационных категорий. Категорийные и фирменные цвета (стадии проекта, типы событий, типы писем, источники данных, диаграммы) сохранены. Светофор в блоке «Следующий шаг» не изменялся.
Что сделано
- Токены дизайн-системы - В resources/css/app.css токены --green/--green-soft/--success/--success-bg/--success-solid переведены на нейтральные значения (тёмная тема #cbd5e1 / rgba(203,213,225,.14) / #334155, светлая #475569 / rgba(71,85,105,.12)). Обе темы и все токены статусов на страницах обесцвечены одной правкой; --red, --warning, --link, --accent не тронуты.
- Бейджи и статусы - Классы badge-green и badge-yellow заменены на badge-gray; variant="green|yellow" заменён на variant="gray"; динамические карты статусных цветов (Project, ClientSubscription, ClientTariffRequest, ProjectEvent, TaskStatusDispatcher) переведены на серый. Убраны оставшиеся «зелёные» подписи: «сохранено», «отправлено», «в очереди», «доставлено», «открыто», «кликнуто», «проверено», «создано», «обновлено», «готово».
- Разделы - Админка: сводка, проекты (список/карточка/статусы/источники), пользователи (карточка, безопасность, email, платформенные проекты), письма (список, карточка, шаблоны, превью, отчёты, календарь, подготовленные письма), кампании, задачи, синхронизация (сводка и 4 мастера), расписание, платформы, тарифы, токены, настройки, журналы, уведомления. Кабинет человека: письма, тариф, настройки. Гость: страница проекта. Общие компоненты: переключатели, флеш-сообщения, тосты.
- Письма - Все 10 шаблонов resources/views/emails: статусные плашки, «выполнено», чек-боксы шагов, плашки прогресса и кнопки-переходы переведены в серый (#334155 / #1f2937). Красные и жёлтые предупреждения сохранены.
- Что сохранено - Красный (просрочка, ошибка, запрет, нет доступа, потерянный доступ клиента), жёлтый (требует внимания, высокий приоритет, тестовое окружение), синий (ссылки, бренд, вордмарка писем, служебные панели), категорийные цвета (стадии проекта, типы событий и писем, источники данных, тарифы, диаграммы), градиенты аватаров и оформление «О платформе». Светофор в блоке «Следующий шаг» (resources/views/components/next-step.blade.php, App\Services\NextStepService) не изменялся.
- Проверка - В tests/Feature/DesignSystemRenderTest добавлены 5 регрессионных тестов: нейтральность success-токенов, отсутствие зелёных статусных классов в blade-шаблонах, отсутствие зелёных цветов в письмах, сохранение светофора «Следующего шага», отсутствие зелёных цветов в отрендеренных страницах админки, кабинета и гостя.
- Версия - Текущая версия платформы: v0.77.2 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.77.1
25.09.2026
MINOR
Чёрно-бело-серый интерфейс: цвет только для статусов, путь клиента на ключевых страницах
Платформа переведена с цветного оформления на спокойное чёрно-бело-серое: синий больше не красит кнопки и бейджи, акцент — тёмно-серый. Цвет остался только там, где несёт смысл: красный — опасность, жёлтый — в работе, зелёный — готово, серый — нет данных. Ссылки стали синими с подчёркиванием, как в Яндексе, чтобы их невозможно было спутать с кнопкой. Исправлены статусы, которые врали цветом: завершённый проект больше не красный, активный больше не зелёный. На рабочем столе, карточке человека и карточке проекта появился блок «Следующий шаг» с одним главным сигналом и кнопкой, которая его закрывает; когда сигналов нет — прямо написано, что всё в порядке.
Что сделано
- Синий убран из интерфейса — кнопки (главные, акцентные, зелёные, фиолетовые), бейджи и служебные отметки стали серыми; главное действие — серая кнопка с тёмной рамкой. Локальные переопределения цветов в blade-страницах переписаны, класс btn-green больше не используется (заменён на btn-accent).
- Ссылки — синие и подчёркнутые — отдельные токены --link/--link-hover/--link-soft для светлой и тёмной темы; навигация, меню и таблицы сохранили своё оформление.
- Светофор статусов починен — «завершён» теперь зелёный (готово), «активен» и «в процессе» жёлтые (в работе), «приостановлен»/«ждёт» жёлтые, закрытые и неизвестные серые. Раньше завершённый проект светился красным, а активный — зелёным.
- Блок «Следующий шаг» — компонент x-next-step и App\Services\NextStepService: один главный сигнал страницы с уровнем светофора, объяснением и кнопкой решения. Человек: нет доступа, не подтверждён email, письма с ошибкой, пароль не установлен, нет проектов. Проект: просроченные задачи, задачи ждут подтверждения, не оплачена подписка, в активном проекте нет задач. Если сигналов нет — блок «всё в порядке».
- Рабочий стол — подсказки отсортированы по важности (красное → жёлтое → серое), зелёная подсказка о последней синхронизации стала серой, действия в подсказках — нейтральные кнопки с тёмной рамкой, добавлена ветка «Сигналов нет — всё в порядке».
- Регрессии — в DesignSystemRenderTest проверяется блок следующего шага на карточках человека и проекта, якорь секции задач, отсутствие цветных кнопок/бейджей в app.css и правильные цвета статусов.
- Версия платформы — тестовая версия поднята до v0.77.1 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.77.0
25.09.2026
MINOR
Несколько проектов у человека: статусы, даты завершения, текущий проект
Человек может вести несколько проектов, и my.ipd теперь показывает их все, а не один. Страница пользователя (/admin/users/{id}) выводит все проекты человека, включая завершённые и архивные: роль, статус, этап платформы задач, дату завершения, прогресс, задачи, синхронизацию. Появился «текущий проект» — тот, по которому идут отчёты и письма: он определяется автоматически (самый свежий по дате синхронизации среди активных проектов), но администратор может выбрать проект вручную или вернуть автоматику. Закрытые проекты текущими не становятся и в отчёты и письма не попадают. Синхронизация клиента теперь забирает с платформы задач и завершённые проекты, из выгрузки читаются дата завершения и признак завершения, статус платформы «архив» больше не превращается в «активен» — это отдельный статус «В архиве». Кнопка «Проверить проекты на платформе задач» показывает, что есть на платформе у этого человека, чего нет в my.ipd, и импортирует выбранные проекты, не перезаписывая локальные названия.
Что сделано
- Все проекты человека на странице пользователя — блок «Проекты (N)» на /admin/users/{id}: роль (владелец/участник), статус, этап, дата завершения, прогресс, задачи, синхронизация, действия; карточки «Проекты (всего)» и «Текущий проект». Завершённые и архивные проекты видны в конце списка, для закрытых показывается причина («статус платформы: завершено»).
- Текущий проект — `Client::resolveCurrentProject()`: ручной выбор (clients.current_project_id) или автоматика — самый свежий активный проект по last_synced_at, затем updated_at, затем id; форма выбора на странице пользователя с вариантом «Автоматически», маршрут admin.users.current-project, закрытый проект текущим стать не может.
- Отчёты и письма только по активным проектам — `Client::reportProjects()` (текущий первым) используется в mail:send-reports, mail:send-monthly, upcoming-письмах и превью писем; уведомления по событиям закрытые проекты не создают (`Project::isClosed()`).
- Проверка проектов на платформе задач — страница /admin/users/{id}/projects/platform (admin.users.platform-projects) показывает проекты человека на task.ipd.test: статус, дату завершения, этап, прогресс, задачи, ссылку на проект и отметку «есть в my.ipd»; выбранные проекты импортируются кнопкой (admin.users.import-platform-projects), локальные названия не перезаписываются.
- Синхронизация забирает завершённые проекты — во все выгрузки добавлен include_completed=1; `TaskExportNormalizer` понимает ключи «завершён» и «дата_завершения»; при импорте сохраняются platform_status, completed_at, stage и признак личного проекта из блока «люди → проекты».
- Статусы — у платформы появился статус «архив» (проект закрыт и убран в архив), в my.ipd это статус archived «В архиве» в фильтрах, списках и на странице проекта; константы Project::STATUS_* и CLOSED_STATUSES вместо строковых литералов; в редакторе проекта поле «Дата завершения».
- Новые поля базы — projects.completed_at, projects.platform_status, projects.stage, projects.is_personal, clients.current_project_id (миграция add_portfolio_fields_to_projects_and_clients).
- Ссылка на проект ведёт на тестовую платформу — на странице проекта ссылка на проект на платформе задач теперь ведёт на task.ipd.test, а не на продакшн task.ipd.by.
- Сервис портфеля — app/Services/Sync/ClientPortfolioService.php: локальные проекты человека (текущий → активные → закрытые), проекты на платформе, принадлежность проекта/человека клиенту, подписи этапов и дат; используется и SyncManager, и контроллером пользователей.
- Версия платформы — тестовая версия поднята до v0.77.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.76.0
25.09.2026
MINOR
Мастер «Получить данные по проекту»: 4 шага вместо одной кнопки
Кнопка «Получить данные по проекту» на странице проекта заменена на мастер из 4 шагов (проверка данных → синхронизация → результат → готово), как мастер «Синхронизация всех людей». Направление данных показано явно на каждом шаге: импорт с платформы задач (task.ipd.test) в my.ipd в одну сторону, обратно ничего не отправляется и ничего не удаляется. Шаг 1 только читает данные (`SyncManager::previewProject`): показывает, что есть на платформе и что изменится, ничего не записывая. Шаг 2 импортирует задачи и шаги только этого проекта (`executeProject`), шаг 3 показывает счётчики и списки созданного/обновлённого, шаг 4 завершает мастер. Дополнительно исправлен подсчёт шагов в предпросмотре: шаги приходят вложенными в задачах, поэтому счётчики «шагов» на шаге 1 были всегда нулевыми.
Что сделано
- Мастер вместо одной кнопки — на странице проекта (/admin/projects/{id}) кнопка «Получить данные по проекту» ведёт в мастер из 4 шагов (/admin/projects/{id}/sync-wizard, маршруты admin.projects.sync-wizard.*): 1 «Проверка данных», 2 «Синхронизация», 3 «Результат», 4 «Готово». Логика вынесена в трейт app/Http/Controllers/Admin/Concerns/AdminProjectSyncWizard.php, вьюха — resources/views/admin/projects/sync-wizard.blade.php (стили wz-* как у мастера людей).
- Шаг 1 только чтение — предпросмотр (`SyncManager::previewProject`) забирает данные с платформы задач и считает их, но ничего не пишет: показаны площадка-источник, что сейчас на платформе (задачи, шаги, выполнено) и что изменится в my.ipd (новые задачи, новые шаги).
- Шаг 2 импорт одного проекта — `SyncManager::executeProject` импортирует задачи и шаги только текущего проекта (по external_id проекта и названию проекта в выгрузке), актуализирует счётчики и last_synced_at; задачи других проектов не затрагиваются.
- Направление данных — на шагах 1, 3 и 4 показан блок «Направление данных»: task.ipd.test → my.ipd, в одну сторону; обратно ничего не отправляется, существующие данные не удаляются.
- Шаги 3–4 — результат показывает бейджи «Задач создано/обновлено», «Шагов создано/обновлено» и итог «Всего»; шаг 4 «Готово» завершает мастер, очищает сессию мастера и ведёт к проекту.
- Исправление подсчёта шагов — выгрузка платформы отдаёт шаги вложенными в задачах («шаги»), верхнеуровневого ключа steps в ответе API нет, поэтому счётчики шагов в предпросмотре были всегда 0; теперь шаги собираются из задач, а «новые шаги» считаются по названию (шаги синхронизируются по названию, без external_id).
- Версия платформы — тестовая версия поднята до v0.76.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.75.2
25.09.2026
PATCH
Мастер синхронизации: шаг 2 «Синхронизация» показывает направление данных и детальные списки созданного/обновлённого
Шаг 2 «Синхронизация» мастера «Синхронизация всех людей» стал информативнее: добавлен явный блок «Направление данных» — данные идут с платформы задач (задание: откуда сюда, куда сюда; блока нет, списки сгруппированы по типам), в верхней заметке уточнено, что именно импортировано в my.ipd в одну сторону и что обратно ничего не отправляется. Счётчик «Всего» теперь выводит сумму по объединённой статистике (включая шаги задач и людей, созданных на шаге 2), поэтому совпадает с бейджами. Таблицы «Создано» и «Обновлено» сгруппированы по типам (направления, проекты, задачи, шаги, люди) с полным числом записей, в списках показаны первые 20, у шагов — задача, у людей — email, у проектов — прогресс.
Что сделано
- Направление данных — в шаге 2 добавлен блок «Направление данных»: слева площадка-источник task.ipd.test (направления, проекты, задачи, шаги, люди), стрелка в одну сторону, справа my.ipd; пояснение «Данные идут с платформы задач в my.ipd (импорт). Ничего из my.ipd на платформу задач в этом мастере не отправляется».
- Что именно сделано — в шаге 2 таблицы «Создано» и «Обновлено» сгруппированы по типам: направления, проекты, задачи, шаги задач, люди. У каждой группы заголовок с полным числом записей («Направления · создано 1», «Задачи · обновлено 210»); в таблице показаны первые 20 записей; у шага указана задача, у задачи — проект, у человека — email, у проекта — процент прогресса.
- Итог «Всего» — счётчик итога пересчитывается по объединённой статистике (шаги задач и люди, созданные на шаге 2, входят в сумму как и в бейджи) — «Всего: создано N, обновлено M» теперь сходится с бейджами по типам.
- Версия платформы — тестовая версия поднята до v0.75.2 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.75.1
24.09.2026
PATCH
Мастер синхронизации: понятные изменения в шаге 1 — «было → станет» по каждому полю и пояснения статусов
Шаг 1 «Проверка данных» мастера «Синхронизация всех людей» стал понятнее: по каждому изменённому проекту, задаче и человеку в таблице показана колонка «Что изменится» — по полям «было → станет» (станет — значение с платформы задач, оно же будет в my.ipd после шага 2); смена email подсвечена красным. Добавлены пояснения статусов («Новый» — объект ещё не в my.ipd и будет создан; «Изменён» — значение отличается, ничего не изменяется до кнопки «Следующий шаг»), таблица различий по задачам (раньше были только счётчики), в заголовках таблиц — счётчики «новых N · изменённых M» и «Показаны первые 30 из N».
Что сделано
- «Было → станет» по полям — в таблицах шага 1 («Проекты», «Задачи», «Люди») у каждого изменения колонка «Что изменится»: список полей, старое значение зачёркнуто и «→ станет» новое (значение платформы задач). Смена email у человека подсвечена красным (критично для писем).
- Пояснения статусов — над таблицами блок «Как читать таблицы»: «Новый» — объекта нет в my.ipd, будет создан на шаге 2; «Изменён» — значение отличается от платформы, до кнопки «Следующий шаг» ничего не изменяется. В блоке «Люди» те же пояснения для клиентов (сопоставление по ID и по email).
- Таблица задач — раньше в шаге 1 были только счётчики «Задачи новые/изменённые», таблицы не было: добавлена таблица различий задач (задача, проект, статус, что изменится — включая шаги, например 6/10 → 5/10).
- Счётчики и лимит — в заголовках таблиц показано «новых N · изменённых M», при более чем 30 различиях — «Показаны первые 30 из N».
- Версия платформы — тестовая версия поднята до v0.75.1 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.75.0
24.09.2026
MINOR
Синхронизация всех людей: мастер из 4 шагов вместо однокнопочного импорта
Кнопка «Синхронизировать всех людей» на странице синхронизации (POST /admin/sync/all-people, «нажал — и всё пропало») заменена мастером из 4 шагов с явной площадкой-источником (task.ipd.test): шаг 1 «Проверка данных» показывает подключение к платформе, что сейчас на ней (проекты, задачи, люди, направления) и что изменится в my.ipd (новые/изменённые счётчики) — до нажатия кнопки ничего не изменяется; шаг 2 «Синхронизация» импортирует проекты, задачи и людей всех направлений (письма в мастер не входят); шаг 3 «Привязка к клиентам» — дополнительная привязка людей к их проектам (без дублей, повтор безопасен); шаг 4 «Готово» — ссылки на проекты и пользователей. В каждом шаге проактивное сообщение: что происходит сейчас, что будет дальше и что НЕ изменится до нажатия кнопки. В методологию добавлен раздел «05 Синхронизация» с правилами мастеров.
Что сделано
- Мастер из 4 шагов — страница /admin/sync/people (маршруты admin.sync.people.*): шаг 1 «Проверка данных» (площадка-источник: версия, доступность, последняя синхронизация; что сейчас на платформе — проекты, задачи, люди, направления; что изменится — новые/изменённые), шаг 2 «Синхронизация» (импорт проектов, задач и людей всех направлений), шаг 3 «Привязка к клиентам» (люди привязываются к своим проектам, pivot client_project), шаг 4 «Готово» (переход к проектам и пользователям).
- Вместо однокнопочного импорта — старый POST /admin/sync/all-people и форма с окном подтверждения убраны; главная страница /admin/sync ведёт в мастер заметным блоком «Синхронизация всех людей» с адресом площадки.
- Проактивные сообщения — каждый шаг объясняет, что сейчас происходит, что будет дальше и что НЕ изменится до нажатия кнопки («Сейчас — только проверка данных. Ничего не изменяется…», «Письма в рамках мастера не отправляются»).
- Письма в мастер не входят — импорт идёт с отложенными уведомлениями (deferNotifications): письма и уведомления по событиям задач не создаются, в мастере шага писем нет.
- Методология — добавлен раздел «05 Синхронизация» (правила работы с площадками, мастера синхронизации, структуры данных сервисов).
- Версия платформы — тестовая версия поднята до v0.75.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.74.1
24.09.2026
PATCH
Мастер синхронизации людей: без некритичного телефона в интерфейсе, со ссылкой на платформу задач и проактивными пояснениями в шагах
Шаг «Кого мы не добавим» мастера «Синхронизация людей» переработан: заголовок стал «Кто не будет добавлен», по каждой категории появилась ссылка на платформу задач (куда добавлять проекты и email, чтобы человек попал в экосистему), а в каждом шаге мастера появилось проактивное сообщение — что происходит сейчас, что будет дальше и что НЕ изменится до нажатия кнопки. Некритичное поле «Телефон» убрано из интерфейса: колонка не выводится в таблицах мастера, смена телефона больше не показывается и не считается изменением, хотя в базе телефон по-прежнему обновляется при импорте.
Что сделано
- Без «мы» и без телефона — шаг «Кого мы не добавим» переименован в «Кто не будет добавлен», колонка «Телефон» убрана из таблиц мастера (телефон в интерфейсе не используется, смена телефона не считается изменением), в тексте интерфейса нет слова «мы».
- Ссылка на платформу задач — в блоке «Кто не будет добавлен» по каждой категории («Без активных бизнес-проектов» и «Без email») появилась ссылка на платформу задач: добавьте проекты или email — и человек придёт следующей синхронизацией. Ссылка ведёт на публичный адрес платформы (task.ipd.by), а в тестовой среде — на внутренний (task.ipd.test).
- Проактивные сообщения в шагах — каждый шаг мастера показывает, что происходит сейчас, что будет дальше и что НЕ изменится до нажатия кнопки: синхронизация людей, привязка к проектам, завершение и переход к пользователям.
- Версия платформы — тестовая версия поднята до v0.74.1 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.74.0
24.09.2026
MINOR
Люди с проектами: мастер добавляет только тех, кто в активных бизнес-проектах и с email
Мастер «Синхронизация людей с платформы задач» больше не тянет всех подряд: в экосистему попадают только люди с активными бизнес-проектами и электронной почтой. Люди без проектов (личные контакты/друзья) и без email на платформе задач не добавляются и не изменяются — в шаге 1 видно отдельные блоки «Кого мы не добавим» с причинами. В таблицах появилась колонка «Проекты»: видно, какие активные проекты (и стадии) у каждого человека. Экспорт платформы задач дополнен проектами людей (ид_проектов и проекты с названием, направлением, статусом, флагом «личный» и этапом), по которым мастер решает и привязывает людей к проектам.
Что сделано
- Только люди с проектами — критерий добавления: у человека есть хотя бы один активный бизнес-проект (не личный, не завершённый). Личные проекты и завершённые экосистему не интересуют.
- Только с email — платформа работает с электронными ящиками: люди без email не добавляются. В мастере подсказка «добавьте email на платформе задач и вернитесь».
- Блок «Кого мы не добавим» — в шаге 1 две категории: «Без активных бизнес-проектов» и «Без email на платформе задач», каждая со списком людей и причиной. Без email люди с проектами видны с их проектами.
- Колонка «Проекты» — в таблицах «Новые» и «Обновления» показаны активные проекты человека (название и стадия: продажи/производство/поддержка).
- Экспорт платформы задач — в блок «люди» выгрузки добавлены ид_проектов и проекты (название, направление, статус, личный, этап). Мастер привязывает людей к проектам по реальным project_ids экспорта.
- Версия платформы — тестовая версия поднята до v0.74.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.73.0
24.09.2026
MINOR
Синхронизация людей с платформы задач: цветная сверка по ID и датам
Мастер «Экспорт людей с платформы» переименован в «Синхронизацию людей с платформы задач» — это синхронизация, а не односторонний экспорт. Шаг 1 «Проверка данных» теперь раскрашивает разницу: зелёные строки — люди будут добавлены, жёлтые — изменены (переименование/контакты), красные — смена email (критично, письма переедут на новый ящик). Каждая строка показывает ID человека на платформе задач и ID пользователя в my.ipd (сопоставление идёт по ID — имя и email могут меняться, ID стабилен), дату актуальности выгрузки платформы и дату последнего обновления записи в my.ipd.
Что сделано
- Переименование — мастер и кнопка в /admin/users называются «Синхронизация людей с платформы задач»: данных может быть и импортировано, и экспортировано, это двусторонний обмен (синхронизация).
- Цветная сверка по ID — шаг 1 сравнивает людей платформы задач с пользователями my.ipd по ID (id человека на платформе ↔ id пользователя в my.ipd через внешний ID). Зелёный — будет добавлено, жёлтый — изменено (имя/телефон/Telegram/роль), красный — смена email, что критично для истории писем.
- Даты актуальности — в шаге 1 видна дата формирования выгрузки платформы («данные платформы задач актуальны на …») и для каждой изменяемой записи — дата последнего обновления в my.ipd.
- Старые и новые значения — в таблице обновлений каждое поле показано в виде «старое → новое»: сразу видно, что именно переименуется или переехало на другой ящик.
- Версия платформы — тестовая версия поднята до v0.73.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.72.0
23.09.2026
MINOR
Экспорт людей с платформы задач в админку пользователей
В админке появился мастер «Экспорт людей»: люди с платформы задач (task.ipd.test) переносятся в пользователей my.ipd (/admin/users) за 4 шага — проверка данных (кто новый, у кого изменятся контакты), импорт/актуализация (создание недостающих, обновление имени, телефона, Telegram, роли и почты), привязка к проектам (pivot client_project) и итог. Смена email выполняется у той же записи клиента, поэтому история писем на старые ящики сохраняется, а новые письма уходят на актуальные адреса.
Что сделано
- Мастер из 4 шагов — страница /admin/people-export/wizard (кнопка «Экспорт людей с платформы» в /admin/users): шаг 1 «Проверка данных» (подключение к платформе, счётчики новых/изменённых/без изменений), шаг 2 «Импорт людей» (создано/обновлено, смена email у той же записи), шаг 3 «Привязка к проектам» (добавление в client_project), шаг 4 «Готово» (ссылка на /admin/users).
- Актуализация без потери истории — новый email (например, у Егора) обновляется у той же записи клиента по external_id человека (id клиента my.ipd): client_id не меняется, вся история писем и писем старых ящиков сохраняется, новые письма отправляются на актуальный адрес.
- Создание новых пользователей — людей с платформы, у которых нет клиента в экосистеме, мастер создаёт с ролью «Клиент», активным статусом и пустым (случайным) паролем.
- Привязка к проектам — люди связываются со своими проектами платформы по external_id проектов: добавляется строка client_project, если такой связи ещё нет (конфликтов/дублей нет).
- Письма в мастере не готовятся — шага писем нет: письма экосистемы и дальше идут на актуальные адреса, решений по рассылке мастер не принимает.
- Версия платформы — тестовая версия поднята до v0.72.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.71.5
23.09.2026
PATCH
Массовое удаление осиротевших задач + уточнение бейджа «Нет на платформе задач»
В список «Задачи с платформы задач» (/admin/tasks-platform) добавлена кнопка «Удалить все осиротевшие (N)» — разом удаляет все задачи с is_orphaned=true, которых больше нет на платформе задач. Бейдж «Нет на платформе» уточнён до «Нет на платформе задач» — теперь явно видно, что речь именно о платформе задач (task.ipd.test), а не о другой платформе.
Что сделано
- Бейдж «Нет на платформе задач» — формулировка уточнена: теперь видно, что речь именно о платформе задач (task.ipd.test), а не о другой платформе.
v0.71.4
23.09.2026
MINOR
Мастер синхронизации: живой прогресс подготовки писем
На шаге 3 мастера после нажатия «Подготовить письма» страница раньше «зависала» на всё время подготовки (до ~10 минут) без обратной связи. Теперь при подготовке виден живой прогресс: шкала 0–100 % с обновлением по ходу работы, строка «что именно готовится» (письмо N из M: клиент — проект), счётчики обработанных и ошибок и список ошибок в реальном времени. Прогресс отдаётся потоком (text/event-stream) в том же запросе, по завершении страница сама открывает шаг 4 «Письма подготовлены».
Что сделано
- Прогресс 0–100 — на шаге 3 появился оверлей с прогресс-баром и процентом; обновляется по каждому сформированному письму (поток SSE), итоговый результат читается из кэша по run-id.
- Что готовится — под шкалой подпись «Письмо N из M: клиент — проект» и статус (готово / ошибка), плюс счётчик обработанных и ошибок.
- Ошибки сразу — ошибки подготовки видны по мере появления и дублируются на финальном шаге 4.
- Автопереход — по завершении страница сама открывает шаг 4; при отключённом JS сохраняется прежний блокирующий запрос с перенаправлением.
- Версия платформы — тестовая версия поднята до v0.71.4 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.71.3
23.09.2026
PATCH
Платформа задач: задачи, скрытые от клиента (осиротевшие)
Синхронизация с платформы задач перестала показывать клиентам служебные и устаревшие задачи: задачи, у которых нет соответствия на стороне платформы или которые исключены, помечаются «осиротевшими» (is_orphaned) и скрываются из кабинета клиента, отчётов и писем. В админке напротив такой задачи виден баннер «Нет на платформе» и действия «Вернуть»/«Удалить». Счётчики прогресса проекта пересчитываются только по видимым клиенту задачам.
Что сделано
- Баннер и действия — на карточке осиротевшей задачи баннер «Нет на платформе» и кнопки «Вернуть» (restore) / «Удалить» (destroy).
- Скрыты от клиента — осиротевшие задачи исключены из кабинета клиента, отчётов прогресса и писем (is_visible_to_client=0).
- Осиротевшие при синхронизации — SyncManager помечает отсутствующие на платформе или непривязанные задачи is_orphaned=1; счётчики проекта (всего/выполнено) пересчитываются по видимым.
- Версия платформы — тестовая версия поднята до v0.71.3 (.env, .env.example, config/app.php).
v0.71.2
22.09.2026
MINOR
Задачи с платформы: мастер синхронизации из 4 шагов
Кнопка «Синхронизировать с платформы задач» на вкладке «Задачи» ведёт в мастер из 4 шагов: 1 — подключение и актуальные данные (что изменится), 2 — импорт (что заменено/изменено), 3 — выбор получателей писем об изменённых статусах задач, 4 — подготовка писем в буфер. Синхронизация выполняется только по направлениям за зону ответственности разработчика (Производство, Поддержка, сервис). Письма больше не отправляются автоматически при импорте: администратор сам решает, кому готовить, на шаге 3.
Что сделано
- Мастер из 4 шагов — страница /admin/tasks-platform/wizard: шаг 1 «Проверка данных» (подключение к платформе, что сейчас на платформе и в экосистеме, изменения: новые/изменённые задачи и проекты), шаг 2 «Импорт» (что создано/обновлено), шаг 3 «Письма клиентам» (чекбоксы кому готовить письма об изменённых статусах), шаг 4 «Готово» (письма положены в буфер).
- Только свои направления — мастер синхронизирует только направления config sync.my_directions (11 «Производство» и 19 «Поддержка, сервис»), вместо всех направлений платформы.
- Письма — отдельным шагом — при импорте из мастера письма клиентам больше не создаются автоматически: события изменений (статус задачи, задача выполнена, новая задача, выполнен шаг) сохраняются, и администратор сам отмечает получателей на шаге 3; на шаге 4 письма кладутся в буфер PreparedEmail (отправка — командой email:send-prepared).
- Быстрый импорт остался — мгновенная синхронизация всех направлений одним действием сохранена как подчинённый пункт «…» рядом с кнопкой мастера.
- Версия платформы — тестовая версия поднята до v0.71.2 (.env, .env.example, config/app.php).
v0.71.0
22.09.2026
MINOR
Задачи с платформы: мастер синхронизации из 4 шагов
Кнопка «Синхронизировать с платформы задач» на вкладке «Задачи» ведёт в мастер из 4 шагов: 1 — подключение и актуальные данные (что изменится), 2 — импорт (что заменено/изменено), 3 — выбор получателей писем об изменённых статусах задач, 4 — подготовка писем в буфер. Синхронизация выполняется только по направлениям за зону ответственности разработчика (Производство, Поддержка, сервис). Письма больше не отправляются автоматически при импорте: администратор сам решает, кому готовить, на шаге 3.
Что сделано
- Мастер из 4 шагов — страница /admin/tasks-platform/wizard: шаг 1 «Проверка данных» (подключение к платформе, что сейчас на платформе и в экосистеме, изменения: новые/изменённые задачи и проекты), шаг 2 «Импорт» (что создано/обновлено), шаг 3 «Письма клиентам» (чекбоксы кому готовить письма об изменённых статусах), шаг 4 «Готово» (письма положены в буфер).
- Только свои направления — мастер синхронизирует только направления config sync.my_directions (11 «Производство» и 19 «Поддержка, сервис»), вместо всех направлений платформы.
- Письма — отдельным шагом — при импорте из мастера письма клиентам больше не создаются автоматически: события изменений (статус задачи, задача выполнена, новая задача, выполнен шаг) сохраняются, и администратор сам отмечает получателей на шаге 3; на шаге 4 письма кладутся в буфер PreparedEmail (отправка — командой email:send-prepared).
- Быстрый импорт остался — мгновенная синхронизация всех направлений одним действием сохранена как подчинённый пункт «…» рядом с кнопкой мастера.
- Версия платформы — тестовая версия поднята до v0.71.0 (.env, .env.example, config/app.php).
v0.70.1
22.09.2026
PATCH
Месячный дайджест: месяц и год в теме письма
Письма «Итоги месяца» у одного клиента выглядели одинаково (тема «Итоги месяца — имя» без указания месяца). В тему месячного письма добавлены месяц и год: «Ваш проект — итоги за Октябрь 2026». Обновлён шаблон monthly-digest в БД (ранее рассинхронизирован с сидом) и пересчитаны темы уже подготовленных в буфере месячных писем.
Что сделано
- Тема месячного письма — в теме письма monthly-digest теперь указываются месяц и год: «Ваш проект — итоги за {{month_name}} {{month_year}}» (например «Ваш проект — итоги за Октябрь 2026»).
- Синхронизация с сидом — субъект шаблона monthly-digest в БД приведён к формату CampaignTemplateSeeder, который уже содержал месяц и год.
- Готовые письма в буфере — темы уже подготовленных месячных писем (queued/draft) пересчитаны по их дате отправки; письма одного клиента теперь различаются месяцем и годом.
- Версия платформы — тестовая версия поднята до v0.70.1 (.env, .env.example, config/app.php).
v0.70.0
21.09.2026
MINOR
Состав писем: управление на рабочем столе, корзина с восстановлением и групповые действия
Подготовленные письма теперь полностью управляются прямо с рабочего стола администратора (/admin): у каждой строки «Подготовленных писем» появилось кебаб-меню (⋯) — смотреть, редактировать, копия, удалить. Удаление стало «корзиной»: письмо перемещается в корзину и его можно восстановить, а через 30 дней оно удаляется безвозвратно автоматически. В центре буфера появились вкладка «Корзина» (с счётчиком), восстановление письма, кнопка «Удалить навсегда», групповое удаление/восстановление нескольких писем сразу (чекбоксы, как в почте).
Что сделано
- Кебаб-меню на рабочем столе — в блоке «Подготовленные письма» страницы /admin у каждой строки появилось кебаб-меню: «Смотреть», «Редактировать» (кроме отправленных/отправляемых), «Копия» и «Удалить».
- Корзина писем — в центре писем (/admin/emails) добавлена вкладка «Корзина» с счётчиком мягко удалённых писем; каждое можно «Восстановить» в буфер или «Удалить навсегда».
- Автоочистка корзины через 30 дней — команда email:purge-trashed раз в сутки безвозвратно удаляет письма, пролежавшие в корзине более 30 дней (расписание в routes/console.php).
- Групповые действия — в списке и в корзине появились чекбоксы и панель выбора: «Удалить выбранные» (в корзину), а в корзине — «Восстановить» и «Удалить навсегда» сразу для нескольких писем.
- Версия платформы — тестовая версия поднята до v0.70.0 (.env, .env.example, config/app.php).
v0.69.0
21.09.2026
MINOR
Буфер писем: кебаб-меню «Удалить» с мягким удалением (deleted_at)
В списке «Буфер писем» (/admin/emails) у каждой карточки появилось кебаб-меню (⋯) с действиями «Смотреть», «Редактировать», «Копия» и «Удалить». Удаление теперь мягкое (SoftDeletes, deleted_at), а не жёсткий DELETE: письмо исчезает из буфера и не попадёт в рассылку, но запись остаётся в истории. Копии статистики по prepared_emails в кампаниях учитывают только неудалённые письма.
Что сделано
- Кебаб-меню в списке буфера — каждая карточка /admin/emails получила кебаб-меню справа: «Смотреть», «Редактировать» (кроме отправленных/отправляемых), «Копия», разделитель и «Удалить» (с подтверждением).
- Мягкое удаление писем — PreparedEmail переведён на SoftDeletes (миграция добавляет deleted_at). destroy() больше не выполняет жёсткий DELETE: письмо помечается удалённым, скрывается из буфера и исключается из очереди отправки (email:send-prepared), но остаётся в таблице для истории.
- Защита от удаления отправляемых — удаление блокируется, пока письмо в статусе «Отправляется» (sending).
- Кампании: статистика без удалённых — агрегаты по prepared_emails в /admin/campaigns теперь фильтруют deleted_at IS NULL.
- Версия платформы — тестовая версия поднята до v0.69.0 (.env, .env.example, config/app.php).
v0.65.0
15.09.2026
MINOR
Письма: блок переписки на странице пользователя и сводка писем в колокольчике админа
Письма — главный показатель «заботы» о клиенте. На странице пользователя в админке теперь есть явный блок «Письма и переписка»: счётчики (всего, отправлено, прочитано, в очереди/ошибки) и последние письма клиента со статусами (отправлено/ошибка/в очереди) и фактом получения (прочитано/кликнул/доставлено) — чтобы видеть переписку в 1–2 клика. В колокольчике уведомлений администратора добавлена сводка исходящих писем за сутки.
Что сделано
- Блок «Письма и переписка» — на странице пользователя (/admin/users/{id}): карточки-счётчики (всего писем, отправлено, прочитано, в очереди/ошибки) и таблица последних 15 писем с типом, темой, статусом и фактом получения (прочитано/кликнул/доставлено).
- Переписка без служебных писем — в показателях учитываются только «человеческие» письма (персональные, кампании, дайджесты статуса проектов); служебные (подтверждение email, восстановление доступа) не смешиваются с перепиской.
- Ссылки-действия — из блока одним кликом: «Вся переписка →» (журнал писем с фильтром по клиенту) и «Написать письмо».
- Сводка писем в колокольчике админа — в шапке рядом с уведомлениями администратор видит «Письма за 24 часа»: всего, отправлено, прочитано, в очереди, ошибки, со ссылкой в журнал писем.
- Версия платформы — тестовая версия поднята до v0.65.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.64.0
15.09.2026
MINOR
Резервная копия v0.64.0 — полный снимок платформы с накопленными изменениями v0.61.0–v0.63.0
Создана полная резервная копия (code + db) по стандарту (Задание 208): архив кода, дамп БД my_ipd_local, README.md и changelog.md в папке v0.64.0. Версия 0.64.0 объединяет все изменения с момента последнего бекапа v0.60.2: задачи с платформы задач (v0.61.0), раздел «Актуальная информация» и производственная платформа (v0.62.0), дизайн письма «Актуальная информация» (v0.62.1), фикс автовхода, Total Commander (v0.63.0). Корневые README.md и changelog.md бэкапов обновлены.
Что сделано
- Архив кода — my-ipd_test_code_2026-09-15_v0_64_0.zip (PHP ZipArchive, исключены vendor/node_modules/storage/.git/.idea/.herd/bootstrap/cache/.env).
- Дамп БД — my-ipd_test_db_2026-09-15_v0_64_0.sql (mysqldump, my_ipd_local, --no-tablespaces --routines --triggers).
- Документация бекапа — README.md (состав, размеры, инструкция по восстановлению) и changelog.md (детальный список изменений v0.61.0–v0.64.0) в папке v0.64.0.
- Корневые файлы — обновлены корневые README.md и changelog.md (таблица версий, полная история).
- Версия платформы — поднята до v0.64.0 (.env, .env.example, config/app.php, VersionSeeder, README.md).
v0.63.0
14.09.2026
MINOR
Total Commander — единый источник правды: данные платформы (письма) в папках клиентов
Платформа выгружает переписку с клиентами в Total Commander: у каждого проекта есть свой путь в системе (tc_path), как у проектов платформы задач. В папку каждого проекта в служебную подпапку platform записывается файл «Письма.md» — все отправленные письма (журнал) и буфер подготовленных писем с датами, темой, получателем и полным видом письма. В настройках появилась вкладка «Total Commander»: видно, какие проекты имеют папку, есть ли папка на диске, сколько писем и когда была последняя выгрузка; кнопка «Выгрузить данные в Total Commander» обновляет данные во всех папках. Если платформа сломается — переписка восстанавливается из папок Total Commander.
Что сделано
- Выгрузка писем в Total Commander — сервис TotalCommanderService пишет в папку каждого проекта (tc_path) markdown-файл «Письма.md»: клиент, отправленные письма (дата, тип, кому, тема, статус, источник, полный вид) и буфер подготовленных писем (плановая дата, тип, кому, тема, статус).
- Вкладка «Total Commander» в настройках — в /admin/settings появилась вкладка: таблица проектов с путём в Total Commander, статусом папки на диске, количеством писем и буфера, датой последней выгрузки.
- Кнопка «Выгрузить данные в Total Commander» — выгружает переписку во все папки проектов одним действием; опционально точечная выгрузка по проекту (totalcmd:export {id}).
- Журнал выгрузок — даты последних выгрузок хранятся в настройках платформы и показываются на вкладке (день, месяц, год, часы, минуты).
- Артефакты — команда totalcmd:export, конфиг totalcmd.php (корень системы, служебный каталог, имя файла).
- Версия платформы — тестовая версия поднята до v0.63.0 (.env, config/app.php).
v0.62.1
14.09.2026
PATCH
Письмо «Актуальная информация»: единый дизайн экосистемы
Понедельничное письмо «Актуальная информация» переведено на общий дизайн-шаблон платформы: та же шапка «Экосистема», приветствие, карточки проектов и полноценный футер (Алексей Гапеев, реквизиты ООО «Студия ИПД», управление уведомлениями, отписка). Раньше письмо собиралось отдельным HTML (другой фон, шрифт, упрощённый футер) — теперь все письма платформы в одной экосистеме дизайна.
Что сделано
- Общий шаблон письма — создан emails/actual-info.blade.php на основе дизайна task-update (шапка «Экосистема», фон #f4f5f7, системный шрифт, карточки #f8fafc).
- Единый футер — подключён общий partial emails/partials/footer: «С уважением, Алексей Гапеев», телефоны, info@ipd.by, реквизиты ООО «Студия ИПД», ссылка на управление уведомлениями и отписка.
- Версия платформы — тестовая версия поднята до v0.62.1 (.env, config/app.php).
v0.62.0
14.09.2026
MINOR
Актуальная информация: экосистема производства и поддержки (production.ipd.test)
В платформе появился раздел «Актуальная информация» — единый взгляд на клиентов,две зоны ответственности Разработчика 2: где находится каждый проект (в производстве или на поддержке), на каком этапе конвейера, какие задачи выполняются и что планируется. Данные приходят из трёх источников экосистемы: платформа задач (task.ipd.test), производственная платформа (production.ipd.test — заказы, этапы, задачи) и локальные проекты платформы. Понедельничная рассылка «Актуальная информация» готовит письма в буфер с текущим статусом каждого проекта и планами на неделю.
Что сделано
- Раздел «Актуальная информация» — новая страница в админке: каждый заказ производства сопоставлен с проектом, показан этап конвейера, статус (производство/поддержка/готово), приоритет, срок и задачи.
- Производственная платформа подключена — третий источник экосистемы: заказы, этапы и задачи с production.ipd.test синхронизируются в платформу; сопоставление проектов авто-по имени, остальное — вручную со страницы.
- Кнопка синхронизации — «Обновить данные» выгружает актуальный срез заказов, этапов и задач; видна дата последней синхронизации.
- Понедельничная рассылка — команда подготовки писем «Актуальная информация» собирает по каждому клиенту текущий статус его проектов и планы на неделю и кладёт письма в буфер PreparedEmail.
- Версия платформы — тестовая версия поднята до v0.62.0 (.env, config/app.php).
v0.61.0
13.09.2026
MINOR
Задачи с платформы задач: отдельная вкладка с зоной ответственности разработчика
В платформе появилась отдельная вкладка «Задачи» — задачи, выгруженные с платформы задач (task.ipd.test). По умолчанию показываются задачи направлений «Производство» и «Поддержка, сервис» — тех, за которые отвечает Разработчик 2; есть переключатель на все направления. На странице видна дата и время последней синхронизации, а кнопка «Синхронизировать с платформы задач» обновляет данные. По каждой задаче доступны проект, направление, статус, приоритет, срок, исполнитель и список шагов.
Что сделано
- Вкладка «Задачи» — слева в админке появился раздел «Задачи» с задачами, синхронизированными с платформы задач task.ipd.test.
- Зона ответственности — по умолчанию показываются задачи направлений «Производство» и «Поддержка, сервис» (за них отвечает Разработчик 2); переключатель расширяет список на все направления платформы.
- Синхронизация — кнопка «Синхронизировать с платформы задач» выгружает актуальные данные; на странице отображается дата и время последней синхронизации.
- Детали задачи — по каждой задаче видно проект, направление, статус, приоритет, срок, исполнителя и шаги с прогрессом.
- Версия платформы — тестовая версия поднята до v0.61.0 (.env, config/app.php).
v0.60.2
10.09.2026
MINOR
Письма: обращение по salutation или имени, индикатор среды, журнал отправки с источником
Приветствие во всех письмах приведено к единому правилу: если у клиента задано обращение (salutation) — используется оно, иначе имя (первое слово), но не фамилия и не полное имя. В шапке появился индикатор, на каком сайте вы работаете (тестовый/продуктовый), а подготовленные письма и журнал отправки снабжены источником и понятным временем.
Что сделано
- Единое обращение — во всех письмах (task-update, task-status, client-task, project-status, support-notification) приветствие идёт через общий партиал: salutation, если задан, иначе «Здравствуйте, Имя!» (ранее часть писем использовала фамилию или полное имя).
- Обращение по умолчанию — имя — метод Client::address_name при пустом salutation возвращает имя (первое слово), а не полное имя; подстановка {{client_name}} в ежемесячных и прочих письмах больше не пишет полное ФИО.
- Индикатор среды — в шапке платформы появился бейдж «Тестовый сайт» / «Продуктовый сайт» (и «Staging»), чтобы нельзя было перепутать среду.
- Источник и время в журнале — журнал отправки (mail_logs) отмечает источник (тест/staging/продакшн) и корректное время (Europe/Minsk).
- Версия платформы — тестовая версия поднята до v0.60.2 (.env, config/app.php).
v0.49.3
01.09.2026
PATCH
Предпросмотр письма в админке показывает настоящее оформление с единым футером
Исправлен предпросмотр подготовленного письма в админке (/admin/emails/{id}): раньше панель «Предпросмотр» показывала только голое тело (body) из БД и строку-футер «Отправлено из Экосистема IPD» — без карточки Алексея Гапеева, юрданных и кнопки отписки, поэтому письмо выглядело «по-старому». Теперь страница отрендеривает настоящее письмо через ReportMail (тот же макет, что уходит клиенту, с единым футером-партиалом), и предпросмотр 1:1 соответствует реальному письму.
Что сделано
- Настоящий рендер в предпросмотре — метод show() контроллера AdminPreparedEmailController теперь строит ReportMail из данных письма и вызывает ->render(); готовый HTML передаётся в представление и показывается в панели «Предпросмотр».
- Единый футер виден в админке — в предпросмотре теперь видны карточка Алексея Гапеева, юридические данные «ООО «Студия ИПД»», «Управление уведомлениями» и рабочая кнопка «Отписаться от рассылки» (при наличии клиента).
- Fallback — если рендер по какой-то причине не удался, показывается прежний упрощённый вид (без падения страницы).
- Версия платформы — поднята до v0.49.3 (.env, config/app.php).
v0.49.2
01.09.2026
PATCH
Единый футер во всех письмах и починка рассылочных писем кампаний
Футер писем приведён к единому виду во всех письмах по эталону письма 81: карточка контакта Алексея Гапеева, юридические данные и «Управление уведомлениями». Вместо трёх разных инлайн-футеров и общего партиала теперь все письма используют один общий партиал emails/partials/footer, в который добавлена рабочая кнопка отписки. Дополнительно исправлен критический баг: рассылочное письмо кампании (CampaignMail) не отправлялось вовсе из-за несовместимой сигнатуры метода with() и отсутствующей переменной $subject.
Что сделано
- Единый футер — в письмах report, project-status, campaign инлайн-футеры заменены на общий партиал emails/partials/footer; теперь все письма (report, project-status, client-task, task-update, campaign, monthly-digest) имеют одинаковый футер: карточка Алексея Гапеева с телефонами и email, юридические данные ООО «Студия ИПД», «Управление уведомлениями» и рабочая кнопка «Отписаться от рассылки».
- Отписка в кампаниях — ссылка отписки (unsubscribeUrl) передаётся и в рассылочные письма кампаний: кнопка отписки показывается, когда письмо отправляется конкретному клиенту (sendTest / SendCampaignEmailJob), и скрывается для проверочных писем администратора.
- Починка CampaignMail — исправлен фатальный баг: метод with() в CampaignMail имел несовместимую сигнатуру with(): array вместо with($key, $value) — из-за этого класс падал при загрузке и рассылочные письма кампаний не отправлялись вовсе; метод переименован во withData() с передачей данных через Content::with().
- Версия платформы — поднята до v0.49.2 (.env, config/app.php).
Какие письма затронуты
- Проверено рендером — все 6 клиентских писем (report, project-status, client-task, task-update, campaign, verification-code) отрендерены: во всех присутствует карточка контакта, юридические данные и кнопка отписки (кроме verification-code — там отписка намеренно отсутствует).
v0.49.1
01.09.2026
PATCH
Вид письма: полноэкранный предпросмотр в редакторе, кнопка отписки и ценное вступление
Доработан интерфейс работы с письмами. В редакторе письма появился полноэкранный предпросмотр на весь экран с переключением «Просмотр / HTML», чтобы удобно смотреть и редактировать письмо. В футер писем добавлена рабочая кнопка «Отписаться от рассылки»: по ссылке клиент отключается от рассылок без входа в систему. Вступление месячного письма переписано без «мы присылаем» — теперь оно сразу несёт ценность для получателя.
Что сделано
- Полноэкранный предпросмотр — в редакторе письма (/admin/emails/{id}/edit) кнопка «Показать на весь экран» открывает письмо на весь экран с переключением «Просмотр» и «HTML», предпросмотр обновляется по мере ввода.
- Кнопка отписки — в футер письма добавлена рабочая кнопка «Отписаться от рассылки»: по публичной ссылке клиент отключается от рассылок (email_campaign_opt_out) без входа в систему.
- Ценное вступление — вступление месячного письма rewritten без «мы присылаем»: теперь сразу о ценности итога для клиента.
- Версия платформы — поднята до v0.49.1 (.env, config/app.php).
v0.49.0
31.08.2026
MINOR
Ежемесячная рассылка: единый поток, идемпотентность и письмо с ценностью
Устранено дублирование месячной рассылки: команда mail:send-monthly переведена на единый идемпотентный поток через подготовленные письма (PreparedEmail) с защитой от повторной отправки в текущем месяце. Ежемесячное письмо полностью переработано: добавлено вступление с месяцем, блок общего прогресса, списки выполненных задач и задач в работе, заметки проекта, блок о ценности и пояснение, почему клиент получил письмо.
Что сделано
- Единый поток рассылки — mail:send-monthly больше не отправляет письма напрямую в обход очереди; теперь месячная рассылка идёт через подготовленные письма PreparedEmail, как и остальные автоотправки.
- Идемпотентность — месячное письмо для клиента и проекта создаётся и отправляется ровно один раз за месяц; повторный запуск команды не создаёт дубль и не отправляет повторно.
- Защита от дублей — проверка: если письмо по проекту за текущий месяц уже отправлено или стоит в очереди, оно не отправляется повторно ни одним из путей рассылки.
- Ценность письма — месячный шаблон monthly-digest переписан: приветствие, месяц в теме письма, блок прогресса, списки «что сделано» и «над чем работаем», заметки, блок о ценности и дружелюбное закрытие.
- Общий макет письма — в письмо добавлены дата, заголовок-тема, строка статистики (прогресс, задачи, имя) и пояснение для получателя.
- Версия платформы — поднята до v0.49.0 (.env, config/app.php).
v0.48.2
31.08.2026
PATCH
Описания версий приведены в соответствие с безопасностью: без внутренних данных доступа
Проведён аудит текстов истории версий: из публичных описаний убраны и обобщены места, раскрывавшие внутренние данные доступа — логины и пароли, служебные адреса, имена внутренних токенов и ключей, точные параметры токенов и конфигураций. Методы и алгоритмы описаны профессионально, без раскрытия уязвимостей.
Что сделано
- Аудит описаний — пройдены все исторические версии (v0.1.0 … v0.48.1) на предмет раскрытия внутренних данных доступа и нюансов безопасности.
- Логины и пароли — удалены явные комбинации учётных записей и их значения из текстов публичных версий.
- Служебные адреса — внутренние почтовые адреса системы заменены на обезличенные формулировки; значения хранятся только в окружении.
- Токены и ключи — из текстов убраны имена внутренних ключей доступа, точные параметры генерации и имена секретных конфигурационных полей.
- Профессиональная подача — методы и алгоритмы (хеширование, ролевой доступ, защита API) описаны корректно и полно, но без раскрытия уязвимостей.
- Версия платформы — поднята до v0.48.2 (.env, config/app.php).
v0.48.1
31.08.2026
PATCH
История версий приведена к единому виду: чистые заголовки и кликабельные ссылки на каждую версию
Доработаны описания исторических версий: из заголовков убраны внутренние пометки «(Задание NNN)», а сам заголовок каждой версии на странице списка стал кликабельной ссылкой на её отдельную карточку. Теперь история версий читается как единый аккуратный список «что и зачем сделано».
Что сделано
- Чистые заголовки — из ~47 исторических заголовков убраны внутренние служебные пометки «(Задание NNN)», чтобы список читался как готовый changelog продукта.
- Кликабельные заголовки — на странице /versions заголовок каждой версии стал явной кликабельной ссылкой на её карточку /versions/{version}.
- Единый формат — заголовки версий приведены к единому стилю с образцом текущей публикации (без служебных слов).
- Версия платформы — поднята до v0.48.1 (.env, config/app.php).
v0.48.0
31.08.2026
MINOR
Версионность из файла перенесена в базу данных: у каждой версии своя ссылка
История версий больше не хранится в единственном markdown-файле changelog.md. Каждая версия теперь — отдельная запись в базе данных со своей собственной ссылкой, что исключает потерю истории изменений.
Что сделано
- Новая таблица versions — в базе данных добавлена таблица versions: версия, заголовок, дата, тип, аннотация, секции изменений, результат и признак текущей версии.
- Модель и сервис — добавлены модель Version и сервис VersionService, через которые все страницы читают версии из БД, а не из файла.
- Отдельная ссылка у каждой версии — у каждой версии теперь есть собственный URL вида /versions/0.48.0, по которому она открывается отдельно.
- Общие публичные страницы — список версий /versions и карточка отдельной версии /versions/{version}.
- Единый формат — страница «О платформе», админ-настройки и новые публичные страницы приведены к единому формату секций.
- Полная история — в базу засеяна вся история версий с v0.1.0 по v0.48.0 (получена с публичной страницы my.ipd.by/about).
- Версия платформы — поднята до v0.48.0 (.env, config/app.php).
v0.47.1
29.08.2026
PATCH
Вид письма: единый богатый футер для всех писем
Исправления после релиза 0.47.0.
Исправления
- Письма из буфера и автописем (ClientTaskMail, TaskUpdateMail, VerificationCodeMail) содержали короткий generic-футер «Экосистема — Пространство заботы о вашем бизнесе» вместо богатого оформления старого письма (фото, контактные данные, юрданные, ссылка «Управление уведомлениями»).
- Добавлен общий partial emails/partials/footer.blade.php с богатым футером по образцу старого письма: авто-уведомление, карточка Алексея Гапеева (фото, должность, телефоны, email, сайт), ООО «Студия ИПД», УНП 291457456, адрес и ссылка «Управление уведомлениями».
- Богатый футер подключён в emails/client-task.blade.php, emails/task-update.blade.php, emails/verification-code.blade.php и admin/prepared-emails/preview-public.blade.php — единый футер во всех письмах.
- Версия платформы поднята до v0.47.1 (.env, config/app.php).
v0.41.4
26.08.2026
PATCH
Расписание писем по дням недели
Что сделано
- Миграция `2026_08_26_025032` — `day_of_week` (0=ежемесячно, 1=Пн..7=Вс), `email_type`, `template_slug`, `time`, `recipient_filter` (JSON), `is_active`, `sort_order`
- Константы `DAY_NAMES`, `EMAIL_TYPES`
- Accessor: `day_name`, `type_label`, `recipient_label`
- Scopes: `active()`, `forDay()`
- Пн = Планы на неделю (report-monday)
- Ср = Текущий прогресс (report-wednesday)
- Пт = Итоги недели (report-friday)
- Ежемесячно = Ежемесячный дайджест (monthly-digest)
- Дайджест статуса (авто), Напоминание просроченных, Напоминание по мячу
- `index()` — недельный вид с группировкой по дням
- `store()`, `update()`, `destroy()` — CRUD
- `toggle()` — AJAX toggle active
- `reorder()` — AJAX reorder
- `GET /admin/schedule-config` → index
- `POST /admin/schedule-config` → store
- `PUT /admin/schedule-config/{id}` → update
- `DELETE /admin/schedule-config/{id}` → destroy
- `POST /admin/schedule-config/{id}/toggle` → toggle
- `POST /admin/schedule-config/reorder` → reorder
- Недельный вид: 7 карточек (Пн..Вс), в каждой — список рассылок
- Toggle включения/выключения, удаление
- Форма добавления: название, день, тип, время, шаблон, получатели
- Навигация: «Расписание» в sidebar и mobile drawer
- `APP_VERSION` изменён: `0.41.3` → `0.41.4`
v0.41.3
26.08.2026
PATCH
Персонализация писем: коррективы per-client
Что сделано
- Миграция `2026_08_26_021721` — таблица `email_overrides`: `client_id`, `email_type`, `project_id`, `subject_override`, `body_override`, `is_sent`
- `EmailOverride` модель — `findPending()`, `markSent()`, связи `client()`, `project()`
- `EmailOverrideService` — статические методы: `consume()`, `applySubject()`, `wrapBody()`, `markSent()`
- Каждый отправщик проверяет overrides перед отправкой и помечает как отправленные после:
- `GET /admin/schedule/override/{command}/{clientId}` → `scheduleOverride()` — загрузка коррективы
- `POST /admin/schedule/override/{command}/{clientId}` → `scheduleSaveOverride()` — сохранение/сброс
- Третья вкладка «Персонализация» во всех раскрытых строках расписания
- Выпадающий список получателей (с id, именем, email)
- Поля: тема письма (персональная), текст сообщения (перед стандартным контентом)
- Кнопки «Сохранить коррективу» и «Сбросить»
- Статус-индикация: загружено / сохранено / ошибка
- CSS: `.override-form`, `.override-select-row`, `.override-fields`, `.override-field`, `.override-actions`, `.override-status`
- `client_tasks` больше не содержит `client_id` — запросы переписаны через `project.client_id` и `whereHas('project', ...)`
- `APP_VERSION` изменён: `0.41.2` → `0.41.3` (`.env`, `config/app.php`)
v0.41.2
26.08.2026
PATCH
Интерактивный виджет «Предстоящие письма»
Что сделано
- `AdminController@index` — `buildScheduleItems()` теперь включает реальных получателей (до 20), команды для предпросмотра и тестовой отправки
- `AdminController::schedulePreview($command)` — возвращает HTML предпросмотра письма (GET JSON)
- `AdminController::scheduleRecipients($command)` — возвращает список получателей (GET JSON)
- `AdminController::scheduleTestSend(Request $request, $command)` — отправляет тестовое письмо на указанный email
- Приватные методы: `getActiveClientRecipients()`, `getOverdueClientRecipients()`, `getBallReminderRecipients()`, `previewProjectStatusDigest()`, `previewOverdueReminder()`, `previewReportEmail()`, `previewBallReminder()`, `wrapPreview()`, `sendTestProjectStatus()`, `sendTestOverdueReminder()`, `sendTestReport()`, `sendTestBallReminder()`
- Исправлены китайские символы "取决于「мяч」статусов" → корректный русский текст
- Кликабельные строки расписания с раскрытием деталей (onclick toggle)
- Вкладки «Получатели» и «Предпросмотр письма» в раскрытой секции
- Список получателей с аватарами, именами, email, метаданными проекта
- Если ≥20 получателей — показ «... и ещё N получателей»
- Предпросмотр письма в iframe через fetch JSON
- Кнопка «Отправить тест» с prompt для email
- `GET /admin/schedule/preview/{command}` → `AdminController::schedulePreview`
- `GET /admin/schedule/recipients/{command}` → `AdminController::scheduleRecipients`
- `POST /admin/schedule/test-send/{command}` → `AdminController::scheduleTestSend`
- `.schedule-expandable`, `.schedule-open`, `.schedule-right`, `.schedule-chevron` — раскрывающиеся строки
- `.schedule-detail`, `.schedule-detail-inner`, `.schedule-detail-tabs`, `.sdtab`, `.sdtab-panel` — вкладки деталей
- `.recipients-list`, `.recipient-row`, `.recipient-avatar`, `.recipient-info`, `.recipient-name`, `.recipient-email`, `.recipient-meta`, `.recipients-more`, `.recipients-empty` — список получателей
- `.preview-loading`, `.preview-content`, `.preview-content iframe` — предпросмотр
- `.schedule-detail-actions`, `.btn`, `.btn-sm`, `.btn-primary`, `.btn-outline` — кнопки действий
- `.spin` — анимация загрузки
- Убита мёртвая ссылка «Рассылки» в клиентском дашборде (была видна non-admin пользователям)
- Типографика: шрифт мин. 14px в элементах расписания
- `APP_VERSION` изменён: `0.41.1` → `0.41.2` (`.env`, `config/app.php`)
v0.41.1
25.08.2026
PATCH
Система email-рассылок: шаблоны, трекинг, дашборд
Что сделано
- `AdminController@index` — передаёт `$upcomingCampaigns`, `$scheduledForTomorrow`, `$scheduledForWeek` во view
- `admin/index.blade.php` — новый блок «Предстоящие письма» между подсказками и «Требуют внимания»: список запланированных кампаний с датой/временем, типом аудитории, числом получателей
- Подсветка: сегодня — красный, завтра — жёлтый, позже — серый
- Hint-блок: «Завтра запланировано рассылок: N»
- `CampaignService::buildUtmParams()` — генерирует `utm_source=ipd_platform`, `utm_medium=email`, `utm_campaign={slug}`
- `CampaignMail` — принимает `$utmSource`, `$utmMedium`, `$utmCampaign`, `$mailLog`; передаёт `$utmParams` и `$trackingPixelUrl` в Blade
- Кнопка «Открыть на платформе» теперь содержит UTM-параметры
- Миграция `2026_08_25_230000` — `tracking_token` (char 32, unique, nullable) в `mail_logs`
- `MailLog` — автогенерация `tracking_token` при создании (Str::random(32)), методы `trackingPixelUrl()` и `clickUrl()`
- `emails/campaign.blade.php` — `<img>` tracking pixel внизу письма (1x1 transparent GIF)
- `SendCampaignEmailJob` — передаёт `$mailLog` в CampaignMail для инъекции пикселя
- `emails/campaign.blade.php` — обновлён: карточка Алексея Гапеева (фото, должность, телефоны, email, сайт), footer с ООО «Студия ИПД» и УНП, ссылка «Управление уведомлениями»
- `CampaignService::buildRecipients()` — `$seenEmails` массив: один email = одно письмо, дубликаты пропускаются
- Version bump → 0.41.1
v0.41.0
25.08.2026
PATCH
Персональный ИИ-помощник для клиентов
Что сделано
- `ChatController` — расширен: принимает `user_name`, `user_email`, `user_role` из виджета, строит персональный system_prompt с контекстом пользователя (имя, роль, компания, проекты, задачи)
- `buildUserContext()` — загружает из БД проекты клиента (статус, прогресс, «мяч») и задачи, требующие внимания (с приоритетами, дедлайнами, блокирующими)
- `getSystemPrompt()` — контекстная: если пользователь авторизован — «Заботливый помощник» с инструкцией знать проекты/задачи, направлять, помогать навигацией. Если нет — общие ответы + предложение войти
- `generateFollowUpSuggestions()` — клиентоориентированные: care-сигналы (помоги, не понимаю), статус-сигналы, задачи, навигация. Для авторизованных — «Покажи проекты», «Какие задачи требуют внимания»
- Персонализированные ошибки: обращение по имени
- Layout: `@auth('client')` блок инжектит `window.__ipdChatUser` (name, email, role) в JS
- Виджет: читает `chatUser`, персонализирует приветствие («Здравствуйте, {name}!»), меняет заголовок и статус
- Запрос к API: передаёт `user_name`, `user_email`, `user_role` в payload
- Rate-limit: персонализированное сообщение для авторизованных
- Version bump → 0.41.0
v0.40.1
25.08.2026
MINOR
AI Agent для Экосистемы
Что сделано
- `ChatController` — POST `/api/chat/send` → n8n webhook → JSON response → store in chat_messages. Rate limit 8 messages/session, getConversationHistory(), generateFollowUpSuggestions(), operatorReply(), history()
- `AiAgentController` — track (digital trail), suggestions (context-aware), lead-form, lead-capture, increment-msg
- `ChatMessage` model + migration: session_id, role (user/assistant/operator), operator_name, message, page_url, page_title, ip_address, utm_*, msg_number, response_time_ms
- `ai_agent_sessions` table — digital trail, msg_count, lead_captured
- `ai_agent_leads` table — captured leads from AI chat
- `NotificationService` — Telegram + email notifications for chat messages and leads
- `config/telegram.php` — параметры подключения к Telegram-боту и приёмному чату (значения подставляются из окружения)
- `config/ai.php` — webhook_url, max_messages, enabled
- Floating chat widget (`.tg-fab`, `.tg-popup`) — dark theme, design tokens, rate limit, typing indicators
- Lead capture form (`.ai-lead-form`) — Telegram/phone/email selection, submit to `/api/ai-agent/lead`
- Follow-up suggestions — context-aware, auto-suggested after each AI reply
- Operator mode — polls `/api/chat/history` every 15s for operator messages
- Digital trail tracking — sends page visits to `/api/ai-agent/track`
- Session persistence via sessionStorage
- `POST /api/chat/send` → ChatController@send
- `GET /api/chat/history` → ChatController@history
- `POST /api/chat/operator-reply` → ChatController@operatorReply
- `POST /api/ai-agent/track` → AiAgentController@track
- `POST /api/ai-agent/suggestions` → AiAgentController@suggestions
- `POST /api/ai-agent/lead-form` → AiAgentController@leadForm
- `POST /api/ai-agent/lead` → AiAgentController@leadCapture
- `POST /api/ai-agent/increment-msg` → AiAgentController@incrementMsg
- `app/Http/Controllers/ChatController.php`
- `app/Http/Controllers/AiAgentController.php`
- `app/Models/ChatMessage.php`
- `app/Services/NotificationService.php`
- `config/telegram.php`
- `config/ai.php`
- `database/migrations/2026_08_26_000001_create_chat_messages_table.php`
- `database/migrations/2026_08_26_000002_create_ai_agent_tables.php`
- `resources/views/components/ai-chat-widget.blade.php`
- `resources/views/components/ai-lead-form.blade.php`
- `resources/css/app.css` — AI Chat Widget section (`.tg-*`, `.ai-lead-*`)
- `routes/api.php` — chat + ai-agent routes
- `.env` — TELEGRAM_*, AI_* variables
- `config/app.php` — APP_VERSION = 0.40.1
- Виджет: «Спросить у ИИ», «AI-ассистент», «онлайн», приветствие, подсказки, лимит
- Lead form: «Оставьте контакт», «Ваше имя», «Отправить», «Или напишите напрямую»
- Typing: «Думаю», «Анализирую», «Подбираю ответ», «Формулирую»
- Ошибки: «Извините, AI-ассистент временно недоступен...»
- System prompt на русском для AI-ассистента
- Подсказки на русском (hot/warm/price/case/social сигналы)
- Уведомления: «Новое сообщение в AI-чате», «Новая заявка из AI-чата»
- Создать n8n workflow для my.ipd (подключение к единому webhook-узлу для обработки чата)
- Подключить KB-файлы к workflow
v0.39.14
25.08.2026
MINOR
Внешний вид письма: предпросмотр, расписание, следующая рассылка
Что сделано
- Кнопка-глаз в каждой строке журнала почты — открывает модальное окно с предпросмотром HTML-содержимого письма
- Модальное окно: полноэкранный overlay с iframe, заголовок, ссылка «Открыть полностью», закрытие по Escape/клик вне
- Загрузка тела письма через AJAX-эндпоинт `/admin/mail/{id}/body`
- Заменён `srcdoc="{{ }}"` (проблемы с экранированием) на JS-based загрузку через fetch → srcdoc
- Заглушка для писем без сохранённого тела (старые записи до включения body storage)
- Блок «Расписание рассылок» — 4 карточки: дайджест (пн/ср/пт 8:30), просроченные (ежедневно 9:00), отчёты (пн/ср/пт 8:30), мяч (ежедневно 10:00)
- Каждая карточка: название, расписание, описание, команда
- Блок «Кому будут отправлены письма» — таблица активных клиентов с подписками: клиент, email, проект
- Показывает количество писем, которые уйдут при следующей рассылке
- `resources/views/admin/mail/index.blade.php` — модальный предпросмотр, расписание, таблица следующей рассылки
- `resources/views/admin/mail/show.blade.php` — исправлен iframe, заглушка для null body
- `app/Http/Controllers/Admin/AdminMailController.php` — метод `body()`, данные расписания/предпросмотра
- `routes/web.php` — маршрут `admin.mail.body`
v0.39.13
25.08.2026
PATCH
Email-столбец в журнале почты
Что сделано
- Добавлен столбец «Email» в таблицу журнала почты (`admin/mail/index.blade.php`)
- Столбец показывает `to_email` — конкретный email-адрес получателя каждого письма
- Решает проблему: при наличии у клиента нескольких ящиков теперь видно, на какой именно адрес было отправлено письмо
- Обновлён colspan пустого состояния (8 → 9)
- `resources/views/admin/mail/index.blade.php` — новый столбец Email в таблице journal
v0.39.12
25.08.2026
PATCH
Внутренняя перелинковка
Что сделано
- Добавлены связи `mailLogs()` в модели `Project` (HasMany) и `Client` (HasMany)
- В journal.project (проекты → индекс): ссылка на клиента в колонке «Клиент», кликабельные бейджи платформ, новая колонка «Почта» с датой последнего письма и ссылкой на журнал
- В journal.project (проекты → просмотр): ссылка на владельца в карточке деталей, кнопка «Письма» в хедере, карточка «Почта» в деталях
- В journal.platforms (платформы → просмотр): ссылки на владельцев в таблице проектов
- В journal.users (пользователи → просмотр): кнопка «Письма клиента» в хедере, ссылки на владельцев в таблице проектов
- В journal.index (дашборд админа): ссылки на клиентов в карточках проектов, title-атрибуты
- Title-атрибуты на всех перелинкованных ссылках (навигация, хедеры, детали)
- `app/Models/Project.php` — `mailLogs()` HasMany
- `app/Models/Client.php` — `mailLogs()` HasMany
- `app/Http/Controllers/Admin/AdminProjectController.php` — eager loading `mailLogs` в index/show
- `admin/projects/index.blade.php` — client link, platform links, mail column
- `admin/projects/show.blade.php` — owner link, mail button, mail detail card
- `admin/platforms/show.blade.php` — owner links
- `admin/users/show.blade.php` — mail button, owner links
- `admin/index.blade.php` — client links in project cards
v0.39.11
25.08.2026
PATCH
Цвета статусов (светофор) и видимость ссылок
Что сделано
- Завершен = **красный** (был зелёный на странице проектов,ixinв务)
- Активен = **зелёный** (работаем, всё хорошо)
- Пауза/ожидание = **жёлтый** (есть нюансы)
- Архив/неактивен = **серый** (нейтральный)
- Исправлены ВСЕ blade-файлы (15+ файлов) по всей экосистеме: admin/projects, admin/index, admin/platforms, admin/users, admin/tasks, dashboard/projects, guest, campaign views, global app.css
- Подчёркивание при наведении (underline) с плавным переходом
- Изменение цвета при hover: `#3b82f6` → `#60a5fa` (светлее)
- `cursor: pointer` на всех ссылках
- `focus-visible` outline для клавиатурной навигации (accessibility)
- Убраны inline `text-decoration:none` из 47 blade-файлов (кроме email-шаблонов)
- `.btn` ссылки не подчёркиваются (text-decoration: none !important)
- `resources/css/app.css` — стили `a`, `.btn`, `.status-completed`, `.status-active`
- `admin/projects/index.blade.php` — completed=badge-red, waiting_admin=badge-yellow
- `admin/projects/show.blade.php` — completed=badge-red, task completed=badge-red
- `admin/index.blade.php` — completed=badge-red
- `admin/platforms/show.blade.php` — completed=badge-red
- `admin/users/show.blade.php` — completed=badge-red (2 таблицы)
- `admin/users/edit.blade.php` — completed='red' в match()
- `admin/users/create.blade.php` — completed='red' в match()
- `admin/tasks/show.blade.php` — task completed=badge-red
- `dashboard/projects/index.blade.php` — completed=red
- `dashboard/projects/show.blade.php` — project+task completed=red
- `guest/project.blade.php` — completed=red
- 47 blade-файлов — удалён inline text-decoration:none
v0.39.10
25.08.2026
PATCH
Kebab-меню, ссылки на сущности, alt-тексты
Что сделано
- Кнопка «Удалить» скрыта в kebab-меню (⋯) — предотвращает случайное удаление
- Kebab-меню содержит: «Подробнее» и «Удалить» (красным)
- Меню закрывается при клике вне его
- Клик на клиента → переход на страницу клиента (`/admin/users/{id}`)
- Клик на проект → переход на страницу проекта (`/admin/projects/{id}`)
- Alt-текст (title): «Перейти к клиенту: Егор Савинский», «Перейти к проекту: Шелье»
- Тип письма: title «Фильтр: Дайджест статуса проекта»
- Тема письма: title «Открыть письмо»
v0.39.9
25.08.2026
PATCH
Ссылки на сущности + содержимое письма
Что сделано
- Клик на клиента → переход на страницу клиента (`/admin/users/{id}`)
- Клик на ID клиента → переход на страницу клиента
- Клик на проект → переход на страницу проекта (`/admin/projects/{id}`)
- Клик на ID проекта → переход на страницу проекта
- Клик на владельца → переход на страницу владельца
- Отображение содержимого письма (HTML) в iframe
- Переключатель «Показать HTML» / «Показать превью»
- Кнопки «Профиль клиента» и «Страница проекта» в быстрых ссылках
- Новая колонка `body` (longText, nullable) в таблице `mail_logs`
- Все сервисы отправки теперь сохраняют HTML тела письма при отправке:
v0.39.8
24.08.2026
PATCH
Улучшение навигации в журнале почты
Что сделано
- Клиенты, проекты, темы и типы писем стали кликабельными ссылками
- Клик на клиента → фильтр всех писем этому клиенту
- Клик на проект → фильтр всех писем проекта
- Клик на тип → фильтр всех писем этого типа
- Клик на тему → страница детализации письма
- Добавлена кнопка «Подробнее» в каждую строку журнала
- Добавлен фильтр по ID проекта в форме фильтрации
- Полная информация о письме: получатель, клиент, проект
- Визуальная воронка доставки (отправлено → доставлено → открыто → клик)
- Мета: тип, статус, Message ID, время всех событий
- Ошибка (если есть)
- Быстрые ссылки: все письма типа, клиенту, проекту
- Клиенты и проекты стали кликабельными → фильтрация в журнале
v0.39.6
24.08.2026
PATCH
Деплой на staging: Экосистема + статистика email
Что сделано
- Название «Среда» заменено на «Экосистема» во всех интерфейсных элементах
- Слоган «Цифровая среда для бизнеса» заменён на «Пространство заботы о вашем бизнесе»
- Обновлены: заголовки страниц, логотипы в сайдбаре, email-шаблоны, авторизация, гостевые страницы, настройки админки
- УНП в футере email: `193547744` → `291457456`
- Добавлен юридический адрес: Республика Беларусь, 224011, г. Брест, ул. Луцкая, 82
- `resources/views/layouts/app.blade.php` — сайдбар и мобильный drawer
- `resources/views/about.blade.php` — страница «О платформе»
- `resources/views/auth/login.blade.php` — страница входа
- `resources/views/auth/set-password.blade.php` — установка пароля
- `resources/views/auth/forgot-password.blade.php` — восстановление доступа
- `resources/views/auth/forgot-password-code.blade.php` — ввод кода
- `resources/views/guest/show.blade.php` — гостевая страница клиента
- `resources/views/guest/project.blade.php` — гостевая страница проекта
- `resources/views/emails/verification-code.blade.php` — email верификации
- `resources/views/emails/task-update.blade.php` — email обновления задачи
- `resources/views/emails/client-task.blade.php` — email задачи клиенту
- `resources/views/emails/campaign.blade.php` — email кампании
- `resources/views/emails/project-status.blade.php` — email статуса проекта
- `resources/views/emails/report.blade.php` — email отчёта
- `resources/views/admin/index.blade.php` — админ-панель
- `resources/views/admin/settings.blade.php` — настройки
- `resources/views/admin/campaigns/create.blade.php` — создание кампании
- `resources/views/admin/users/email.blade.php` — отправка email пользователю
- `app/Http/Controllers/AboutController.php` — данные «О платформе»
- `app/Services/MailSettingsService.php` — дефолты почтовых настроек
- `app/Services/ReportDispatcher.php` — дефолт футера отчёта
- `app/Services/CampaignService.php` — дефолт футера кампании
- `app/Jobs/SendCampaignEmailJob.php` — дефолт футера кампании
- `app/Http/Controllers/Admin/AdminUserController.php` — дефолт футера
- `database/seeders/EmailTemplateSeeder.php` — сидеры email-шаблонов
- `database/seeders/CampaignTemplateSeeder.php` — сидеры шаблонов кампаний
v0.39.3
24.08.2026
PATCH
Исправление email-рассылок, футер с контактами, admin email
Что сделано
- Получатель системных уведомлений переведён на актуальный служебный адрес платформы (значение хранится в настройках окружения).
- Резервный адрес напоминаний приведён к актуальному служебному адресу платформы (значение хранится в настройках окружения).
- Admin-копии дайджестов теперь приходят на реальный почтовый ящик
- Добавлен блок «Почему вы получили это письмо» — объяснение для клиента
- Добавлена карточка контактного лица: фото Алексея Гапеева, роль, телефоны, email, сайт
- Добавлены юридические данные: ООО «Студия ИПД», УНП 193547744
- Добавлена ссылка «Управление уведомлениями» → `/settings/notifications`
- Футер обновлён в обоих шаблонах: `project-status.blade.php` и `report.blade.php`
- Ссылка «Открыть проект» в `project-status.blade.php` теперь ведёт на `my.ipd.by` (prod) через route
- Версия обновлена до 0.39.3
v0.39.1
21.08.2026
PATCH
Клиентская панель инструкций, Материалы поддержки, Напоминания по мячу
Что сделано
- Инструкции проекта теперь видны клиенту на странице проекта (только с флагом `is_visible_to_client`)
- 3 типа инструкций: онлайн-встреча (ссылка), видео-запись, текст с скриншотами
- Секция «Инструкции» на клиентской странице проекта с иконками по типу
- Таймлайн событий проекта теперь виден клиенту (без CRUD — только просмотр)
- Цветные точки по датам с типами событий и описаниями
- Статус мяча виден клиенту на странице проекта (только при `ball_status !== 'none'`)
- Цветной индикатор + дата + заметка
- предупреждение для клиента когда мяч у него
- Новый JSON-блок `support_materials` в проекте — обучающие видео, презентации, PDF, документы
- CRUD для админа: вкладка «Материалы поддержки» в редактировании + секция на странице проекта
- Видно клиенту на странице проекта (с флагом `is_visible_to_client`)
- 4 типа материалов: ссылка, видео, PDF, текст
- Новая команда `project:ball-reminder` — проверяет проекты где мяч не двигается 7/14/30 дней
- Отправляет email-напоминания админу + in-app уведомления владельцу проекта
- Запускается ежедневно в 10:00 (Europe/Minsk) через планировщик
- Дублирование не допускается (трекинг порогов в памяти)
- Миграция `add_support_materials_to_projects_table` — JSON-поле `support_materials`
- Модель `Project`: +`support_materials` в fillable/casts
- `AdminProjectController`: +`cleanSupportMaterials()`, +`updateSupportMaterials()`, валидация
- `AdminProjectEventController`: события проекта — CRUD без изменений
- `MailLog`: добавлен тип `TYPE_BALL_REMINDER`
- `BallStatusReminderMail` — HTML-письмо напоминания
- Планировщик: +`project:ball-reminder` ежедневно в 10:00
- Версия: 0.39.1
v0.39.0
21.08.2026
PATCH
Инструкции, Follow-up, Календарь/События по проектам
Что сделано
- Новый JSON-блок `instructions` в проекте — список инструкций (текст, ссылки, видео)
- Каждая инструкция: заголовок, тип (текст/ссылка/видео), URL, описание, флаг «видно клиенту»
- Секция «Инструкции для клиента» на странице проекта — просмотр, добавление, редактирование
- Отдельная вкладка «Инструкции» в редактировании проекта с динамическим добавлением записей
- Валидация и очистка при сохранении (метод `cleanInstructions`)
- Новые поля проекта: `ball_status` (admin/client/none), `ball_since`, `ball_note`
- Виджет на странице проекта — индикатор текущего статуса (цветной badge + дата + заметка)
- Кнопки быстрого переключения: «У нас» / «У клиента» / «Нет»
- Отдельный метод `updateBall` в контроллере с логированием
- Новая таблица `project_events` — события проекта (старт, оплата, коммуникация, задача, встреча, пауза и др.)
- Модель `ProjectEvent` с 10 типами событий (цвет + иконка)
- CRUD для событий через `AdminProjectEventController` (store/update/destroy)
- Вертикальный таймлайн на странице проекта — цветные точки по датам
- Форма добавления события с выбором типа и описанием
- Добавлены статусы `waiting_client` (Ожидаем клиента) и `waiting_admin` (Ожидаем админа)
- Выбор новых статусов в редактировании проекта
- Цветовые бейджи в списке проектов и на странице проекта (жёлтый / красный)
- Миграции: `add_instructions_and_ball_to_projects_table`, `create_project_events_table`
- Модель `ProjectEvent` с типами, accessors, relationships
- Обновлена модель `Project`: fillable, casts, relationships, accessors
- Новые маршруты: `admin.projects.events.*`, `admin.projects.ball.update`, `admin.projects.instructions.update`
- Eager loading events в `AdminProjectController::show`
- JavaScript-функции `addInstruction()` / `addEditInstruction()` для динамических форм
v0.38.0
21.08.2026
MINOR
Персональные письма пользователям
Что сделано
- Новая страница `/admin/users/{id}/email` — форма отправки персонального письма конкретному клиенту
- Кнопка «✉ Отправить письмо» на странице пользователя (`/admin/users/{id}`)
- Выбор шаблона из `EmailTemplate` (загрузка темы, вступления, тела, футера)
- Привязка к проекту (для подстановки {{project_name}}, {{progress}} и т.д.)
- Ручное редактирование всех полей перед отправкой
- Предпросмотр письма в реальном времени (дизайн «Среда»)
- Кнопки-плейсхолдеры для быстрой вставки `{{client_name}}`, `{{project_name}}`, `{{progress}}` и т.д.
- Тип `personal` добавлен в `MailLog` — каждое отправленное письмо сохраняется в БД
- История писем клиента на странице email (вкладка «История писем»)
- Лог активности: запись `user_personal_email` при каждой отправке
- Mailable: переиспользуется `CampaignMail` (тот же HTML-шаблон «Среда»)
- Placeholder'ы подставляются на стороне сервера перед отправкой
- Предпросмотр на стороне клиента с `decodeEntities()` (исправление из v0.37.1)
- 2 маршрута: `GET /admin/users/{id}/email` + `POST /admin/users/{id}/email`
v0.37.1
20.08.2026
MINOR
Кликабельные табы wizard + предпросмотр
Что сделано
- Табы шагов (Аудитория, Шаблон, Редактор, ...) теперь кликабельны и обновляют URL hash (`#step1`..`#step7`)
- При загрузке страницы — автоматический переход на шаг из URL hash (например, `admin/campaigns/create#step5` открывает шаг «Тест»)
- Обработка `hashchange` — кнопка «Назад» браузера корректно переключает шаги
- `history.pushState` — URL обновляется без перезагрузки страницы
- Предпросмотр теперь показывает полный дизайн письма «Среда»: шапка с логотипом, тема, вступление, тело с подставленными плейсхолдерами, кнопка «Открыть на платформе», футер
- Плейсхолдеры подставляются с тестовыми данными: Иван Петров, ivan@test.by, ООО Тест, Тестовый проект, 65%, 12/8 задач
- Вынесено в общую функцию `replacePlaceholders()` для переиспользования
- XSS-безопасность: `{{chr(123)}}` в Blade заменено на `@{{` синтаксис (строки 159, 179, 215)
- JS-regex с `{{ }}` экранированы через `\{\{` (строки 386-393) для предотвращения ошибок компиляции Blade
- `sumThrottle2` — добавлена проверка на null перед присвоением
v0.37.0
19.08.2026
PATCH
Email-маркетинг платформа
Что сделано
- Модель `Campaign` — статусы (draft/scheduled/sending/completed/cancelled/failed), типы аудитории (all/active/project/custom), статистика
- Модель `CampaignRecipient` — отслеживание каждого получателя (pending/sent/delivered/failed/unsubscribed/bounced)
- Миграции: `campaigns`, `campaign_recipients`, `clients.email_campaign_opt_out`
- `SendCampaignEmailJob` — ShouldQueue job, tries=3, backoff=60
- Каждое письмо = отдельный job с delay(throttle_seconds)
- Автоматический переход к следующему после отправки
- Очередь на database driver
- `buildRecipients()` — формирование списка по фильтру
- `getAudienceStats()` — подсчёт аудитории
- `renderBody()` / `renderSubject()` — подстановка переменных
- `sendTest()` — тестовая отправка
- `startCampaign()` — запуск (создание recipients + dispatch jobs)
- `cancelCampaign()` — отмена (остановка + пометка pending как failed)
- `updateStats()` — пересчёт счётчиков
- `scheduledCampaigns()` — обработка запланированных кампаний
- `CampaignMail` — универсальный Mailable для кампаний
- View: `emails/campaign.blade.php` — дизайн «Среда»
- Список кампаний с фильтрацией и статистикой
- Детальная страница: прогресс-бар, статистика, список получателей
- Предпросмотр письма в отдельном окне
- Кнопки: Запустить / Отменить / Удалить
- Ссылка «Кампании» в почте и в сайдбаре
- `welcome-client` — Приветствие клиента (system)
- `monthly-digest` — Ежемесячный дайджест (report)
- `GET /admin/campaigns` — список
- `GET /admin/campaigns/create` — wizard
- `POST /admin/campaigns` — создание
- `GET /admin/campaigns/{id}` — детали
- `PUT /admin/campaigns/{id}` — редактирование
- `DELETE /admin/campaigns/{id}` — удаление
- `GET /admin/campaigns/{id}/preview` — предпросмотр
- `POST /admin/campaigns/{id}/send` — запуск
- `POST /admin/campaigns/{id}/cancel` — отмена
- `POST /admin/campaigns/{id}/test` — тест
- `POST /admin/campaigns/audience` — AJAX аудитория
v0.36.0
19.08.2026
MINOR
Шаблоны email-отчётов
Что сделано
- Таблица `email_templates` (slug, name, subject, intro, body, footer, type, is_active)
- Модель `EmailTemplate` с типами: report, system, promo
- `report-monday` — Понедельник: планы на неделю (прогресс, запланированные задачи)
- `report-wednesday` — Среда: текущий прогресс (выполненные и в работе задачи)
- `report-friday` — Пятница: итоги недели (общий прогресс, выполненные задачи, заметки)
- Профессиональный HTML-дизайн с логотипом «Среда»
- Кнопка «Открыть на платформе»
- Прогресс-бар с процентами
- Цветовая кодировка задач: синий (активные), жёлтые (в работе), зелёные (выполненные)
- Footer с названием компании
- `ReportMail` — универсальный Mailable для отчётов
- Поддержка переменных: {{client_name}}, {{project_name}}, {{progress}}, {{tasks_list}}, {{completed_list}}, {{in_progress_list}}, {{notes_list}}
- `ReportDispatcher` — сервис отправки отчётов
- `sendReport()` — отправка клиенту по шаблону
- `sendTest()` — отправка тестового письма на любой email
- Данные берутся из модели Project (задачи, прогресс, заметки)
- Три вкладки: Журнал | Шаблоны | Отправить
- Журнал: статистика, фильтрация, логи писем
- Шаблоны: список шаблонов, предпросмотр, редактирование темы и тела
- Отправить: тестовое письмо (на любой email) + ручная отправка клиенту (выбор клиента → проекта → шаблона)
- Динамическая загрузка проектов клиента через JSON
- GET `/admin/mail` — журнал + шаблоны + отправка (с `?tab=`)
- POST `/admin/mail/test` — тестовое письмо
- POST `/admin/mail/manual` — ручная отправка клиенту
- GET `/admin/mail/template/{id}/preview` — предпросмотр шаблона
- PUT `/admin/mail/template/{id}` — обновление шаблона
- GET `/admin/users/{id}/json` — JSON с проектами клиента
v0.35.5
19.08.2026
MINOR
Название «Среда»
Что сделано
- Название платформы: «Среда»
- Подзаголовок: «Цифровая среда для бизнеса»
- Верхний левый угол сайдбара: заголовок + подзаголовок вместо «IPD v0.35.4»
- Мобильный drawer: «Среда» вместо «IPD»
- Сайдбар (десктоп): «Среда / Цифровая среда для бизнеса»
- Мобильный drawer: «Среда»
- Страницы авторизации: вход, восстановление доступа, установка пароля, ввод кода
- Страница «О платформе»: заголовок, описание, footer
- Гостевые страницы: client/show, client/project
- Email-шаблоны: verification-code, task-update, project-status, client-task
- Админ-панель: заголовок дашборда, настройки (site_name, mail_from_name, subjects, footer)
- MailSettingsService: все дефолты обновлены
- AboutController: название в данных
v0.35.4
19.08.2026
PATCH
Тариф «Цифровой операционный партнёр»
Что сделано
- Стоимость: 36 000 BYN/год (3 000 BYN/мес), договор на 12 месяцев
- 6 направлений: маркетинг, CRM, платформа, AI, аналитика, поддержка
- Время реакции: до 2 часов
- Поддержка: персональное сопровождение
- Цвет: #f97316 (оранжевый), иконка: handshake
- Доступен после прохождения «Экосистема роста»
- Цифровая архитектура — стал «Хит» (is_popular=true), бейдж «Хит» на карточке
- Базовый запуск — скрыт с сайта (is_gated=true, is_public=false)
- Стратегическое партнёрство — теперь доступно после «Цифровой операционный партнёр»
- Level 0 (Free) → Цифровая архитектура (вместо Базового запуска)
- Level 1 → Экосистема роста
- Level 2 → Цифровой операционный партнёр
- Level 3 → Стратегическое партнёрство
- Level 4 → топ
- SVG-иконка handshake для нового тарифа
- Бейдж «Хит» на карточке популярного тарифа (accent цвет)
v0.35.3
19.08.2026
PATCH
Исправление видимости в тёмной теме
Что сделано
- В базе данных: `#6b7280` → `#9ca3af` (светлый серый, читаем на тёмном фоне)
- TariffSeeder: дефолтный цвет Free обновлён до `#9ca3af`
- Все использования `{{ $tariff->color }}` как `color:` CSS автоматически исправлены (KPI, админ-дашборд, проекты, подписки)
- `.sub-status.free` цвет текста: `#6b7280` → `#9ca3af`
- Непрозрачность фона бейджа: `0.15` → `0.2`
- `.badge-gray` цвет текста: `#6b7280` → `#9ca3af`
- Непрозрачность фона: `0.15` → `0.2`
v0.35.2
19.08.2026
PATCH
Заявки на тарифы и предоплата
Что сделано
- Создана таблица `client_tariff_requests` (client_id, tariff_id, status, client_message, admin_note)
- Статусы: pending, approved, rejected
- Связи: client(), tariff()
- Атрибуты: status_label, status_color
- Кнопка «Отправить заявку» вместо «Подключить»
- Форма с сообщением для администратора (необязательное)
- Заявка создаёт статус pending
- Баннер «Заявка ожидает решения администратора»
- Запрет на дублирование заявок на один тариф
- Предыдущиеpending-заявки отклоняются при новой
- Контроллер `AdminTariffRequestController` (index, approve, reject)
- Виджет `admin/tariff-requests/index.blade.php`
- Кнопки «Одобрить» / «Отклонить» с подтверждением
- Одобрение = создание подписки + обновление finance
- Отклонение = смена статуса
- Маршруты: admin/tariff-requests, .../approve, .../reject
- Ссылка в сайдбаре (десктоп + мобильное меню)
- Обновлены описания: «Договор на N месяцев» вместо «рассрочка»
- Предоплата 50% от стоимости
- Базовый запуск: 3000 BYN, предоплата 1500 BYN, 3 мес
- Цифровая архитектура: 6000 BYN, предоплата 3000 BYN, 6 мес
- Экосистема роста: 12000 BYN, предоплата 6000 BYN, 12 мес
- Итеративная поставка: 6000 BYN, этапная поставка
- Заменены все эмодзи на SVG-иконки (rocket, layers, trending, star, repeat, shield)
- Чеклисты, бейджи, баннеры — всё на SVG
- Предоплата отображается под ценой
- Форма заявки прямо на карточке тарифа
- Убраны ссылки на «Подключить» (режим subscribe)
v0.35.1
19.08.2026
PATCH
Тариф Free
Что сделано
- Добавлено поле `is_free` (boolean, default false) в таблицу `tariffs`
- Создан тариф «IPD Free» (id=0, slug=ipd-free, level=0)
- Тариф не публичный (is_public=false) — нельзя подключить вручную
- Бесплатный: base_price=0, hourly_rate=0
- Описание: «Вы находитесь в системе IPD. Поддержка доступна по мере освобождения ресурсов.»
- Особенности: доступ к платформе, хранение проекта, базовая поддержка, возможность升级
- Добавлен `is_free` в fillable/casts
- Добавлен метод `isFree()` — возвращает true для тарифа Free
- Добавлен метод `isOnFreeTariff()` — проверяет, находится ли клиент на Free
- Добавлен метод `getEffectiveTariff()` — возвращает текущий тариф (платный или Free)
- `subscriptions:expire` — переводит клиентов с истёкшими подписками на Free
- `clients:ensure-tariffs` — устанавливает Free для всех клиентов без тарифа
- Обновлён `index()` — передаёт freeTariff и isOnFree в виджет
- Тарифы для подписки: исключён Free (только публичные, не-free)
- Новый блок «IPD Free» — показывает текущий бесплатный тариф с описанием и особенностями
- Бейдж «Текущий тариф: IPD Free» для клиентов без платной подписки
- Подсказки для升级 к платным тарифам
- Добавлена колонка «Тариф» в список пользователей
- Показывает название платного тарифа или «Free» для бесплатных
- Все существующие клиенты переведены на тариф Free (7 клиентов)
- Единственный платный клиент (Егор Савинский) остался на «Итеративная инкрементная поставка»
v0.35.0
19.08.2026
PATCH
Система тарифов и задач по уровням
Что сделано
- Добавлено поле `level` (unsigned tiny integer, default 0) в таблицу `tariffs`
- Добавлено поле `tariff_level` (unsigned tiny integer, default 0) в таблицу `client_task_templates`
- Базовый запуск: level 0, 3000 BYN, 3 мес (1000/мес)
- Цифровая архитектура: level 1, 6000 BYN, 6 мес (1000/мес)
- Экосистема роста: level 2, 12000 BYN, 12 мес (1000/мес)
- Стратегическое партнёрство: level 3, 15000 BYN, VIP (гейтинг)
- Итеративная инкрементная поставка: level 1, 6000 BYN, 5 этапов
- `ClientTariffController::subscribe()` — создаёт подписку, деактивирует предыдущую, обновляет finance
- POST-роут `tariff/{tariff}/subscribe`
- Валидация: проверка публичности, доступности, дубликатов
- ActivityLogger логирует подключение
- Шаблоны задач (`ClientTaskTemplate`) имеют поле `tariff_level` (0–3)
- Level 0: базовые задачи (ТЗ, договор, оплата, материалы, проверка) — доступны всем
- Level 1: аналитика, контент-план, SEO-отчёт
- Level 2: стратегия продвижения, стратегическая сессия
- Level 3: персональная дорожная карта
- Клиент видит только задачи с `tariff_level <= его уровень`
- `DashboardController::index()` — фильтрует clientTasks по tariff_level клиента
- `ProjectController::show()` — фильтрует clientTasks по tariff_level
- `ProjectTokenController::show()` — фильтрует clientTasks по tariff_level (гостевой доступ)
- Задачи без шаблона (`template_id = null`) доступны всегда
- Добавлен `level` в fillable/casts
- Добавлен `Tariff::levelLabel(int $level)` — возвращает название уровня
- Добавлен `Tariff::getClientLevel(Client $client)` — определяет уровень клиента по активной подписке
- Добавлен `tariff_level` в fillable/casts
- Показывает все публичные тарифы (не только апгрейды)
- Текущий тариф выделен зелёным с бейджем «Ваш тариф»
- Кнопка «Подключить» — POST-форма с CSRF
- Текущий тариф — кнопка «Текущий тариф» (disabled)
- Недоступные — замок с причиной
Техническое
- `app.css`: добавлен блок «Kebab menu (универсальный)» (~80 строк)
- `app.blade.php`: глобальный `toggleKebab` + click-outside + Escape handler
- `APP_VERSION` изменён: `0.34.2` → `0.34.3`
- 142 тестов, 580 ассертов — все пройдены.
Что исправлено
- Добавлены унифицированные CSS-классы: `.kebab-wrap`, `.kebab-btn`, `.kebab-menu`, `.kebab-item`, `.kebab-sep`
- Ранее стили были размазаны по инлайн-блокам `<style>` в каждом blade-файле
- Теперь kebab-меню работает везде: карточки проектов, админка, страница проекта
- `toggleKebab(btn)` — глобальная, определена в layout (ранее дублировалась в каждом файле)
- Закрытие по клику вне меню — глобальный обработчик
- Закрытие по Escape — глобальный обработчик
- `dashboard/index.blade.php` — удалены инлайн kebab-стили и JS
- `admin/index.blade.php` — удалены инлайн kebab-стили и JS
- `admin/projects/index.blade.php` — удалены инлайн kebab-стили и JS
- `admin/projects/show.blade.php` — удалены инлайн kebab-стили и JS, `.kebab` → `.kebab-wrap`
v0.34.2
19.08.2026
MINOR
Мобильное меню
Что изменено
- Нижняя панель навигации (bottom nav) — 5 элементов: Главная, Проекты, Финансы, Уведомления, Ещё
- Боковое выдвижное меню (drawer) — открывается по кнопке «Ещё»
- Drawer содержит все пункты сайдбара + раздел «Аккаунт» (Профиль, Уведомления настр., Безопасность, Инструкция, Поддержка, Выйти)
- Тёмный оверлей при открытом drawer, закрытие по свайпу/клику
- Адаптивный topbar на мобильном: скрыто имя, уменьшены отступы
- User dropdown на мобильном: позиционирование внизу экрана
- Уведомления dropdown: адаптивная ширина
- Новые компоненты: `.mobile-bottomnav`, `.mbn-item`, `.mobile-overlay`, `.mobile-drawer`, `.mdrawer-*`
- Padding-bottom на контенте для пространства над bottom nav
- `openMobileDrawer()` / `closeMobileDrawer()` — toggle drawer с анимацией
- Закрытие по клику на оверлей или кнопку ✕
- `document.body.style.overflow` блокируется при открытом drawer
Техническое
- `app.blade.php`: добавлен HTML для bottom nav и drawer (скрыты на desktop через CSS)
- `app.css`: стили для мобильных компонентов
- `APP_VERSION` изменён: `0.34.1` → `0.34.2`
- 142 тестов, 580 ассертов — все пройдены.
v0.34.1
19.08.2026
PATCH
Исправления KPI и тарифов
Что исправлено
- Бобер Боб (Егор Савинский): `tariff_id` исправлен на 1 (Базовый запуск)
- Подписка: `tariff_id=1`, `total_amount=1200`, `payments_made=600`
- Подсвечивается тариф клиента (зелёная рамка), а не «Популярный»
- Блок «Детали подписки»: стоимость тарифа, оплачено, остаток
- Блок «Что входит в ваш тариф»: список фич текущего тарифа
- Блок «Повысить тариф»: только тарифы ВЫШЕ текущего
- Убрана подсветка «Популярный» — теперь это «Ваш тариф»
- Показывает реальные данные: тариф, оплачено, остаток
- Привязана к `client_finances` и `client_subscriptions`
- Показывает дату начала подписки
Техническое
- `ClientTariffController`: передаёт `finance`, отмечает `is_current`
- `KpiController`: передаёт `finance`, `subscription`
- Вью тарифов: фильтрация только повышающих тарифов
- `APP_VERSION` изменён: `0.34.0` → `0.34.1`
- 142 тестов, 580 ассертов — все пройдены.
v0.34.0
19.08.2026
PATCH
Показатели KPI
Что изменено
- 22 показателя эффективности, сгруппированных по 8 категориям
- Каждый KPI привязан к уровню тарифа (0-4)
- Визуальные чекбоксы: доступные — зелёные, недоступные — серые с пометкой
- Трафик: мониторинг посещаемости
- SEO: позиции в поисковиках
- Финансы: управление продажами, ROI
- Коммуникация: отчёты, созвоны, менеджер
- Аналитика: конверсия, A/B тесты, прогнозы
- ROI: окупаемость вложений
- SLA: время реагирования
- Проекты: задачи, совместные проекты
- Уровень 0 (без тарифа): ежемесячный отчёт, базовая коммуникация
- Уровень 1 (Базовый запуск): трафик, SEO, еженедельные отчёты
- Уровень 2 (Цифровая архитектура): аналитика, A/B тесты, стратегия
- Уровень 3 (Экосистема роста): ROI, прогнозирование, персональный менеджер
- Уровень 4 (Партнёрство): совместные проекты, выход на рынки
- Блок «Хотите больше возможностей?» с ссылкой на следующий тариф
- Недоступные KPI показаны с пометкой «Требуется тариф ≥ N»
Техническое
- Миграции `kpi_indicators`, `client_kpis`
- Модели `KpiIndicator`, `ClientKpi`
- Контроллер `KpiController`
- View `dashboard/kpi/index.blade.php`
- Seeder `KpiIndicatorSeeder` — 22 индикатора
- Route `GET /kpi`
- Sidebar раздел «Системное»: KPI → Тариф
- `APP_VERSION` изменён: `0.33.0` → `0.34.0`
- 142 тестов, 580 ассертов — все пройдены.
v0.33.0
18.08.2026
MINOR
Админ-панель уведомлений
Что изменено
- 7 типов уведомлений с глобальными переключателями
- Включение/отключение по каналам: Email, Telegram, В приложении
- Настройки действуют как «по умолчанию» — пользователь может отключить
- Список всех активных клиентов с их персональными настройками
- Редактирование настроек для каждого пользователя
- Видно, какие типы отключены системой, а какие — пользователем
- Форма для отправки тестового уведомления конкретному пользователю
- Выбор типа, канала, темы и текста
- Автоматическая проверка: система → пользователь → отправка
- Логирование в `notification_sends` с причиной пропуска
- Статистика: всего, отправлено, доставлено, ошибок, пропущено
- Таблица с фильтрацией по дате, пользователю, типу, каналу, статусу
- Причины пропусков: `user_disabled`, `invalid_email`, etc.
- Публичная страница — отключает ВСЕ уведомления для пользователя
- Ссылка внизу писем для соответствия GDPR/CAN-SPAM
Техническое
- Миграции `system_notification_settings`, `notification_sends`
- Модели `SystemNotificationSetting`, `NotificationSend`
- Контроллер `AdminNotificationController` (8 методов)
- Views `admin/notifications/{index,clients,client-settings,report,unsubscribe}.blade.php`
- Seeder `SystemNotificationSettingSeeder` — 7 типов по умолчанию
- Routes 8 маршрутов + `/unsubscribe/{token}`
- `APP_VERSION` изменён: `0.32.0` → `0.33.0`
- 142 тестов, 580 ассертов — все пройдены.
v0.32.0
18.08.2026
MINOR
Настройки профиля и уведомлений
Что изменено
- Редактирование имени, телефона, компании
- Смена пароля (с проверкой текущего)
- Просмотр информации об аккаунте (роль, верификация каналов)
- Три канала: В приложении, Email, Telegram
- 7 типов уведомлений: статус проекта, назначение задачи, выполнение задачи, новое сообщение, финансы, системные объявления, еженедельный отчёт
- Каждый тип включается/выключается отдельно для каждого канала
- Автоматическое создание записей при первом входе
- В меню пользователя ( top-right ) добавлены: «Профиль», «Уведомления», «Безопасность»
- Кнопка «Настройки» (админ) удалена из меню клиента
- Все изменения профиля записываются в `settings_history`
- Смена пароля логируется
Техническое
- Миграция `create_client_notification_preferences_table` — таблица настроек уведомлений
- Модель `ClientNotificationPreference` — настройки по каналам
- Контроллер `ProfileController` — профиль, уведомления, пароль
- Views `settings/profile.blade.php`, `settings/notifications.blade.php`
- Routes `GET/PUT /settings/profile`, `GET/PUT /settings/notifications`, `PUT /settings/profile/password`
- `APP_VERSION` изменён: `0.31.1` → `0.32.0` (`.env`, `.env.example`, `config/app.php`).
- 142 тестов, 580 ассертов — все пройдены.
v0.31.1
18.08.2026
MINOR
Финансовые данные из Total Commander
Что изменено
- Данные извлечены из README проектов в Total Commander (`L:\Система\02 Клиенты\...`)
- Заполнены `client_finances` для 4 клиентов:
- Исправлен `client_id=18` для проекта «Медтехника Гродно» (был `null`)
Техническое
- Seeder `ClientFinanceSeeder` — заполнение данными из README
- Миграция данных через `updateOrInsert` (без дублей)
- `APP_VERSION` изменён: `0.31.0` → `0.31.1` (`.env`, `.env.example`, `config/app.php`).
- 142 тестов, 580 ассертов — все пройдены.
v0.31.0
18.08.2026
PATCH
Сайдбар клиента, Финансы
Что изменено
- «Панель управления» переименована в «Рабочий стол» (без группы)
- Раздел **Основное**: Проекты, Финансы, Платформы, Сообщения
- Раздел **Системное**: Тариф
- Статистика: баланс, внесено всего, предоплата, следующий платёж
- Блок «Договор»: дата первого контакта, заключение, начало/окончание действия, текущий этап
- Блок «Оплаты»: предоплата, дата предоплаты, внесено всего, баланс, следующий платёж
- Автоматическое создание записи `ClientFinance` при первом входе
Техническое
- Модель `ClientFinance` + миграция `client_finances`
- Контроллер `Dashboard\FinanceController@index`
- Маршрут `GET /finance` → `client.finance.index`
- Связь `Client::finance()` → `HasOne(ClientFinance)`
- `APP_VERSION` изменён: `0.30.0` → `0.31.0` (`.env`, `.env.example`, `config/app.php`).
- 142 тестов, 580 ассертов — все пройдены.
v0.30.0
18.08.2026
MINOR
Рабочий стол клиента: светофор, задачи, активность
Что добавлено
- Зелёный (`light-green`) — все задачи клиента выполнены
- Жёлтый (`light-yellow`) — есть ожидающие задачи
- Красный (`light-red`) — есть просроченные или блокирующие задачи
- Серый (`light-grey`) — нет задач клиента
- Каждая карточка проекта показывает два прогресс-бара: прогресс разработчика (синий) и прогресс клиента (зелёный)
- Отдельный счётчик задач для каждой стороны
- До 5 невыполненных задач с бейджами статуса (Ожидает / На подтверждении / Просрочено)
- Блокирующие задачи выделены красным + пометка «блокирует»
- Предупреждения о блокирующих и просроченных задачах сверху
- Всего задач / Выполнено / Ожидают / Прогресс в процентах
- Отдельный progress-bar для клиента
- Последние 10 действий (входы, просмотры страниц, выполнение задач)
- Цветовые точки: зелёный = вход, синий = просмотр, фиолетовый = выполнение
- Автоматически логирует все GET-запросы клиентов (просмотры страниц)
- Пропускает маршрут `client.dashboard` (отслеживается отдельно в контроллере)
- Типы: `login`, `page_view`, `task_view`, `task_complete`, `task_start`
- Привязка к клиенту и проекту
- Хранит IP, user_agent, метаданные
Изменения
- Заголовок: «Панель управления» → «Рабочий стол»
- Секция «Проекты (Производство)» → «Проекты»
- Секция «Статистика» → «Другие проекты»
- Тарифная секция перенесена в конец
Файлы
- `app/Models/ClientActivity.php` — модель активности клиента
- `app/Http/Middleware/TrackClientActivity.php` — middleware логирования просмотров
- `database/migrations/2026_08_18_111219_create_client_activities_table.php` — миграция
- `app/Http/Controllers/Dashboard/DashboardController.php` — переработан: задачи, светофор, активность
- `app/Http/Controllers/Auth/ClientAuthController.php` — добавлено логирование входа в `ClientActivity`
- `app/Http/Controllers/Dashboard/ClientTaskController.php` — добавлено логирование выполнения задач
- `bootstrap/app.php` — `TrackClientActivity` добавлен в web middleware
- `resources/views/dashboard/index.blade.php` — полностью переработан в «Рабочий стол»
- `tests/Feature/ClientDesktopTest.php` — 10 тестов (25 assertions)
- `tests/Feature/DashboardProjectsSplitFeatureTest.php` — обновлены под новые заголовки
v0.29.1
18.08.2026
MINOR
Исправление dashboard/projects: видимость проектов при impersonate
Баг: dashboard показывал все проекты при входе под пользователем
- Зачем: при impersonate (вход под пользователем) `/dashboard` показывал **все** проекты из внешнего API (13 штук), хотя у пользователя есть только 1 проект. Страница `/dashboard/projects` показывала «Нет проектов».
- Корень: `DashboardController` и `ProjectController` использовали `$client->projects()` (BelongsToMany через pivot `client_project`), не учитывая владение проектами (`client_id`). Если у клиента нет записей в pivot — список пуст. А `$apiProjects` загружал **все** проекты из API без фильтрации по пользователю.
- Исправление:
- `APP_VERSION` изменён: `0.29.0` → `0.29.1` (`.env`, `.env.example`, `config/app.php`).
v0.29.0
18.08.2026
PATCH
Вход под пользователем: Impersonate
Новое
- Кнопка «Войти как» на страницах `/admin/users` (список) и `/admin/users/{id}` (детали) — рядом с «Просмотр» и «Редактировать». Показывается только для не-admin и активных пользователей.
- Реализована функция «Войти как»: администратор временно просматривает платформу от имени клиента с явной пометкой в интерфейсе и полным журналом таких входов; использование функции для других администраторов заблокировано.
- Жёлтый баннер в кабинете клиента при impersonate: «Вы вошли как {имя} — видите платформу глазами пользователя. ← Вернуться к админу».
- Кнопка «Вернуться к админу» POST `/stop-impersonate`, восстанавливает сессию админа и перенаправляет на список пользователей.
- Логирование: все входы через impersonate записываются в `activity_log` (`user_impersonate`).
Файлы
- `app/Http/Controllers/Admin/AdminUserController.php` — `impersonate()`, `stopImpersonate()`
- `routes/web.php` — `POST /admin/users/{id}/impersonate`, `POST /stop-impersonate`
- `resources/views/admin/users/index.blade.php` — кнопка «Войти как»
- `resources/views/admin/users/show.blade.php` — кнопка «Войти как»
- `resources/views/layouts/app.blade.php` — жёлтый баннер impersonate
Автотесты
- `tests/Feature/AdminImpersonateTest.php` (10 тестов, 23 assertions): вход под клиентом; запрет входа под другим админом; запрет входа под неактивным; возврат к админу; сохранение admin_id в сессии; stopImpersonate без admin_id; кнопка видна для клиентов; кнопка скрыта для админов; баннер при impersonate; баннер не показывается при обычном входе.
- `APP_VERSION` изменён: `0.28.6` → `0.29.0` (`.env`, `.env.example`, `config/app.php`).
v0.28.6
18.08.2026
MINOR
Дайджест статуса проектов и уведомления клиентам
Автоматическая рассылка дайджестов клиентам
- Зачем: клиенты не в курсе текущего статуса проекта — что сделано разработчиком, что ожидается от клиента (документы, подписи, ответы). Нужна автоматическая система, которая показывает прогресс «как диаграмма Ганта» и напоминает о просроченных задачах.
- Новая модель email: `ProjectStatusMail` (`app/Mail/ProjectStatusMail.php`) — дайджест статуса проекта с прогресс-баром, списками задач (выполнено / в работе / ожидаем от клиента / просрочено), этапами проекта.
- Email-шаблон: `resources/views/emails/project-status.blade.php` — HTML-письмо с цветовыми секциями: зелёный (выполнено), синий (в работе), жёлтый (ожидаем от клиента), красный (просрочено).
- Сервис: `ProjectStatusNotificationService` (`app/Services/ProjectStatusNotificationService.php`) — вычисляет статус проекта (группировка задач), отправляет дайджест клиенту + копию служебному адресу администратора (адрес из окружения), создаёт in-app уведомление.
- Artisan-команда: `project:status-notify` — отправляет дайджесты по всем активным проектам.
- Расписание: пн/ср/пт в 8:30 (Europe/Minsk) — полный дайджест; ежедневно в 9:00 — напоминания по просроченным задачам клиента.
- Напоминания по просроченным задачам: `ClientTaskNotificationService::remind()` — ранее реализованный метод, который нигде не вызывался. Теперь подключён в расписании (ежедневно 9:00).
- Лог почты: `TYPE_PROJECT_STATUS = 'project_status'` в `MailLog` (дайджест статуса проекта).
- Тип уведомления: `project_status` в `ClientNotification` — in-app уведомления с иконкой `clipboard`.
Синхронизация и актуализация проекта 40 (Бобер Боб)
- Запущена синхронизация с `task.ipd.test/project/15` — данные обновлены (прогресс 50% → 84%, новая задача «Ответ от Егора по поводу теста»).
- Заполнены milestones: старт 17.07.2026, подготовка 17.07–05.08, производство с 05.08 (в работе).
- Добавлено описание проекта.
- `APP_VERSION` изменён: `0.28.5` → `0.28.6` (`.env`).
Автотесты
- `tests/Feature/ProjectStatusNotifyTest.php` (9 тестов, 31 assertion): группировка задач по статусам; отправка дайджеста с MailLog記錄; in-app уведомление с правильным заголовком; обработка только активных проектов; dry-run; overdue-only; тип лога; исключение done/awaiting_confirmation задач из pending; проверка milestones.
v0.28.5
12.08.2026
PATCH
Этапы и даты проекта
Даты старта и этапов вместо «даты создания записи»
- Зачем: на гостевой странице проекта «Каменецкое благочиние» показывалось «Проект ведётся с **24.07.2026**» — это `created_at` (когда запись завели в системе), а не реальная дата старта работ (январь). Клиент не понимал, откуда эта дата. Теперь по проекту можно задать реальные даты: старт, три этапа и срок службы — они показываются клиенту как наглядный график.
- Новое поле `projects.milestones` (миграция `2026_08_12_150000_add_milestones_to_projects_table.php`): JSON `{start_date, stages{preparation|production|support}, lifespan_years, note}`.
- Три этапа (названия и пояснения заданы в модели, `Project::MILESTONE_STAGES`):
- Админка (`/admin/projects/{id}/edit`): вкладка **«Этапы проекта»** — дата старта проекта, у каждого этапа даты «с» и «по» + пояснение для клиента, срок службы платформы в годах (по умолчанию 10) и общее примечание по срокам.
- График этапов (общий партиал `resources/views/partials/project-milestones.blade.php`): горизонтальная диаграмма-линия с сегментами этапов (пропорционально длительности, от старта до конца сопровождения), подпись «Завершение сопровождения: дата», легенда этапов с датами и пояснениями, примечание о сроке службы (~10 лет).
- Показ клиенту: гостовая страница по токену (секция «Этапы проекта») и личный кабинет. Вместо «Проект ведётся с {created_at}» при заполненных датах показывается «Проект стартовал {дата}»; для проектов без дат — прежняя логика (ничего не ломается).
- Страница проекта в админке: блок «Этапы проекта (даты)» с графиком и ссылкой на редактирование.
- Экспорт/импорт JSON: `milestones` включены в выгрузку и загрузку проекта (для ИИ и переноса данных).
- Данные проекта «Каменецкое благочиние» (id 37): проставлены реальные даты — старт 15.01.2026, этапы аналитика/производство/поддержка, срок службы 10 лет.
- `APP_VERSION` изменён: `0.28.4` → `0.28.5` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/ProjectMilestonesFeatureTest.php` (8 тестов): сохранение этапов из формы редактирования; пустые этапы отбрасываются и срок службы по умолчанию 10 лет; блок этапов на странице проекта в админке; кабинет клиента показывает «Старт проекта» и график; гостовая страница показывает «Проект стартовал» вместо «Проект ведётся с»; fallback на `created_at` для проектов без дат; экспорт включает `milestones`; вкладка «Этапы проекта» в форме редактирования.
v0.28.4
12.08.2026
PATCH
Токен и email: подтверждение уходит владельцу проекта
Почему в гостевой ссылке показывался email администратора
- Зачем: на гостевой странице проекта «Каменецкое благочиние» для подтверждения email показывался адрес администратора, который создал гостевую ссылку, а не владельца проекта. Владелец проекта — Отец Николай (`cerkv_kamen@mail.ru`).
- Корень: гостевая ссылка привязывалась к аккаунту того, кто её создал, а не к владельцу проекта. Поэтому подтверждение email уходило администратору, а не настоящему владельцу.
- Исправление (код):
- Данные: существующий токен проекта «Каменецкое благочиние» (id 76) перепривязан с админа (client 1) на владельца Отец Николай (client 23).
- Понятность админки: подсказка у списка «Пользователи (доступ)» теперь объясняет, что это список **всех клиентов платформы** (из всех проектов), а доступ — только у отмеченных галочкой (по умолчанию только у владельца). Так понятно, почему «несколько доступов» и что это не ошибка.
- `APP_VERSION` изменён: `0.28.3` → `0.28.4` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/GuestTokenEmailFeatureTest.php` (5 тестов): гостевая страница токена, привязанного к админу, показывает email владельца; код подтверждения уходит на email владельца; подтверждение по коду помечает владельца; генерация токена админом из кабинета привязывает токен к владельцу; генерация клиентом привязывает к клиенту.
v0.28.3
12.08.2026
PATCH
Favicon и установка как приложения
Иконки сайта и веб-приложение
- Зачем: платформа была «без иконки, без приложения» — в браузере сложно определить, что это за сайт, а `public/favicon.ico` был пустым файлом (0 байт). Теперь сайт узнаваем: есть маленькая и большая иконки во вкладке, и его можно установить как приложение (Android/iOS/desktop) с подписью.
- Набор иконок в `public/` (бренд IPD: синий квадрат `#3b82f6` со скруглением и белыми вертикальными полосами — как логотип в шапке):
- Веб-манифест `public/site.webmanifest` установка как PWA: название приложения **«IPD»** (short_name, подпись на рабочем столе/в списке приложений), `name` «IPD Platform — управление проектами», `display: standalone` (полноэкранное окно без браузера), `theme_color: #3b82f6`, `background_color: #0f172a`, `start_url/scope: /`.
- Meta-теги в `<head>` всех страниц (общий partial `resources/views/partials/favicons.blade.php`, подключён в `layouts/app.blade.php`, `layouts/guest.blade.php`, `welcome.blade.php`):
- `APP_VERSION` изменён: `0.28.2` → `0.28.3` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/FaviconFeatureTest.php`: мета-теги favicon/manifest присутствуют на страницах кабинета и гостевой странице; файлы иконок и манифеста существуют в `public/` и не пустые; манифест корректно валиден как JSON с нужными ключами (name, short_name, display, icons 192/512).
v0.28.2
11.08.2026
PATCH
«Поля проекта»: ссылки и информация вместо копирования версий
Поля проекта со ссылками вместо копирования текста версий
- Зачем: Алексей ведёт версии проектов у себя (например, открытая история версий `/platform-version/` и `/about` на bober-bob.ipd.by). Копировать готовый текст в поле changelog — «бесполезная работа». Теперь по каждому проекту можно завести **поля = подпись + ссылка/текст**, и клиенту/ИИ отдаётся ссылка на исходник, а не копия.
- Новое поле `projects.fields` (миграция `2026_08_11_140000_add_fields_to_projects_table.php`): JSON-массив `{label, value, type: link|text, is_visible_to_client}` — разные поля под разные сайты, проекты, площадки.
- Админка (`/admin/projects/{id}/edit`): вкладка **«Поля проекта»** (заменила «Версии проекта»). Кнопки-пресеты в один клик добавляют готовое поле: «История версий», «Сайт проекта», «Платформа», «Ссылка», «Заметка (текст)» — остаётся вписать только ссылку. Строки добавляются/удаляются, у каждой — подпись, значение, тип и чекбокс «видно клиенту».
- Автопоказ клиенту без копирования: заполненные поля автоматически видны клиенту **без логина** — на гостевой странице (секция «Ссылки и информация», ссылки-чипы) и в личном кабинете (страница проекта). Скрытые от клиента поля не показываются.
- Для ИИ: поля попадают в экспорт JSON проекта (`fields`) и в импорт — ИИ получает ссылку на исходник (например, на историю версий) и сам её читает, копировать ничего не нужно.
- Страница проекта в админке: блок «Поля проекта (ссылки и информация)» со всеми полями (скрытые помечены) и ссылкой на редактирование.
- Текстовое поле `changelog` (Задание 94) сохранено в БД, но вводить его больше не нужно — вместо него поля со ссылками.
- `APP_VERSION` изменён: `0.28.1` → `0.28.2` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/ProjectFieldsFeatureTest.php`: сохранение полей из формы редактирования (пресеты/произвольные), отображение на странице проекта в админке, в личном кабинете клиента и на гостевой странице (без логина), скрытие полей, помеченных «скрыто», пустой список → секции нет, экспорт включает `fields`, наличие пресетов в форме.
v0.28.1
11.08.2026
PATCH
«История версий проекта»: Changelog по каждому проекту
История версий (Changelog) у каждого проекта
- Новое поле `projects.changelog` (миграция `2026_08_11_130000_add_changelog_to_projects_table.php`): история версий конкретного проекта в формате markdown (версия, дата, «что сделано» и «зачем» — как в `changelog.md` платформы).
- Админка (`/admin/projects/{id}/edit`): новая вкладка **«Версии проекта»** — текстовое поле с подсказкой формата и примером. Сохраняется кнопкой «Сохранить» вместе с остальными полями.
- Страница проекта (`/admin/projects/{id}`): блок «История версий (Changelog)» с отрисованным markdown и ссылкой на редактирование.
- Личный кабинет клиента: секция **«История версий»** на странице проекта — что сделано и зачем.
- Гостевая страница (по ссылке, без логина): секция **«История версий — что сделано»** — главная ценность доступна клиенту без входа в кабинет (аналогично `https://xn----7sbbdliaae3aaodrirjkg5m4a.xn--90ais/platform-version/` и `https://bober-bob.ipd.by/about`).
- Экспорт/импорт JSON проекта теперь включает поле `changelog`.
- Описание ведётся Алексеем по мере работы и может использоваться для ИИ-отчётов по клиентам (задачи/план/диаграмма Ганта + история версий).
- `APP_VERSION` изменён: `0.28.0` → `0.28.1` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/ProjectChangelogFeatureTest.php`: сохранение changelog из формы редактирования проекта, отображение на странице проекта в админке, в личном кабинете клиента и на гостевой странице (без логина), рендер markdown и скрытие при пустом поле.
v0.28.0
11.08.2026
PATCH
«Актуальные данные по проекту»: синхронизация с платформой задач по кнопке
Точечная синхронизация проекта с платформы задач
- Страница проекта (`/admin/projects/{id}`): новый блок «Синхронизация с платформой задач» — ссылка на проект task-платформы (`task.ipd.test/project/{external_id}`), бейдж «Данные обновлены: дата/время» и кнопка **«Получить данные по проекту»**. Нажатие тянет с платформы задач актуальные данные конкретного проекта: описание, статус, прогресс, задачи (включая завершённые — полная история), шаги.
- Список проектов (`/admin/projects`): кнопка **«Получить данные по всем проектам»** — точечно синхронизирует каждый проект, привязанный к платформе задач (заполнен `external_id`), не создавая лишних проектов. В kebab-меню проекта добавлен пункт «Синхронизировать».
- Новый метод `SyncManager::executeProject()` фильтрует данные экспорта по `external_id` проекта, запрашивает `include_completed=1` (чтобы клиент видел историю — что сделано и когда), импортирует направление/проект/задачи/шаги и обновляет `last_synced_at`, `tasks_total`, `tasks_completed` проекта. `SyncManager::execute()` вынесен в общий `runImport()`.
- `importTasks`: задачи, уже привязанные к локальному проекту по `external_id`, обновляются даже когда в экспорте нет `project_name` (завершённые/архивные задачи) — их статус и шаги приводятся к актуальным. Новые задачи без проекта по-прежнему пропускаются.
- `PlatformRegistry::fetchData(..., ['include_completed' => true])` добавляет к экспорту `?include_completed=1`.
- Логи: синхронизация проекта пишется в историю проекта (`ActivityLog` с `project_id`).
- `APP_VERSION` изменён: `0.27.0` → `0.28.0` (`.env`, `.env.example`).
Автотесты
- `tests/Feature/ProjectTaskPlatformSyncFeatureTest.php`: точечная синхронизация проекта по кнопке (Http::fake — платформа задач), обновление прогресса и `last_synced_at`, импорт активных и завершённых задач, история для клиента (завершённые задачи не теряются), массовая синхронизация «по всем проектам».
v0.27.0
10.08.2026
MINOR
«Что нужно от клиента»: голосовые/заметки проекта и контрольные точки
«Мои данные по проекту» — голосовые и заметки Алексея (админка + клиент)
- Новая сущность `project_notes` (миграция `2026_08_10_100000_create_project_notes_table.php`): запись с типом (`voice` — голосовое, `text` — заметка, `report` — итоговый отчёт), заголовком, текстом/расшифровкой (markdown), прикреплённым аудио-файлом (название, размер, MIME) и флагом видимости клиенту.
- Админка (страница проекта): секция «Мои данные по проекту» — добавить голосовое (загрузка файла MP3/M4A/WebM до 20 МБ, диск `private`), заметку или отчёт, редактирование, переключение видимости «видно клиенту/скрыто», скачивание аудио, удаление. Дата записи фиксируется автоматически.
- Итоговый markdown-отчёт: кнопка «Сформировать итоговый отчёт» собирает все видимые записи проекта в один markdown-отчёт (название проекта, статус, прогресс, ссылка на ИИ-проект `ai_url`, все записи с датами и текстом/аудио) и сохраняет его как запись-отчёт, видимую клиенту.
- Клиентский кабинет и гостевая страница: секция «Мои данные по проекту» — список видимых записей с датами, отрисованным markdown (`Str::markdown`) и ссылкой на прослушивание/скачивание голосового (маршруты `client.projects.notes.audio`, `guest.project.notes.audio`). Скрытые записи клиенту не показываются.
- Уведомления: при добавлении записи с чекбоксом «Уведомить клиентов» создаётся уведомление в личном кабинете (тип `project_note`, сервис `ProjectNoteNotificationService`).
- Модель `ProjectNote` , контроллеры `Admin\AdminProjectNoteController` (store/update/toggleVisibility/destroy/downloadAudio/generateReport) и `Dashboard\ProjectNoteController` (скачивание аудио с проверкой доступа).
«Задачи для клиента» — контрольные точки с ожиданиями от заказчика
- Доп. поля `client_tasks`: `category` (категория ожидания: документ, оплата, материалы, проверка, возврат, доступ, другое), `template_id` (из какого шаблона создана), `is_blocking` (блокирует проект — «проект остановлен в ожидании клиента») и `client_response` (комментарий клиента при отметке «выполнено»).
- Новая сущность `client_task_templates` (миграция `2026_08_10_100001_create_client_task_templates_table.php`) — шаблоны-заготовки контрольных точек: название, категория, описание «что нужно от клиента», срок по умолчанию в днях, порядок, активность.
- Сид `ClientTaskTemplateSeeder` 7 типовых контрольных точек разработки: согласовать ТЗ, подписать договор, внести предоплату, предоставить материалы, проверить работу и согласовать, возврат исходников и доступов, проверить доступ к кабинету.
- Админка: на странице проекта — блок «Применить шаблон» (выбор шаблона + свой срок + уведомление), в форме создания задачи — категория, шаблон, чекбокс «Блокирует проект»; управление шаблонами (CRUD) на `/admin/client-task-templates`. В таблице задач бейджи: категория, «Блокирует», «Просрочено», ответ клиента.
- Клиентский кабинет: бейдж «Блокирует проект» и «Просрочено», комментарий при отметке «Отметить выполненной». Гостевая страница: секция «Что нужно от вас» с категорией, сроками и блокирующими задачами.
- Уведомления и напоминания: в `ClientTaskNotificationService` добавлен метод `remind()` для напоминаний (просрочено / приближается срок / блокирует) — уведомление в кабинет + письмо; исправлена передача `$action` в `createNotification()`.
- Новое поле `projects.ai_url` ссылка на ИИ-проект (форма редактирования проекта, включается в итоговый отчёт).
Автотесты (14 новых, всего 75/75)
- `tests/Feature/ProjectNoteFeatureTest.php` (8 тестов): добавление заметки, скрытие от клиента, рендер видимых/скрытых, уведомление клиенту, генерация отчёта, удаление, запрет скачивания аудио клиенту без доступа.
- `tests/Feature/ClientTaskTemplateFeatureTest.php` (6 тестов): наличие сида шаблонов, создание шаблона, применение шаблона (со сроком из шаблона и со своим сроком), создание блокирующей задачи с категорией, бейдж «Блокирует проект» у клиента, отметка задачи с комментарием.
- `APP_VERSION` изменён: `0.26.1` → `0.27.0` (`.env`, `.env.example`).
v0.26.1
10.08.2026
MINOR
Очистка проектов после синхронизации
Исправление: на `/admin/projects` отображались «не те» проекты
- Причина: полная синхронизация 07.08.2026 с task-платформы была выполнена без фильтра «Только производство» (флаг `production_only` не был активен на момент запуска). В БД my.ipd попали все 22 проекта экспорта, включая не производственные: AmoCRM, «Встреча Агроусадьба "Угляны"», «Ремонт в квартире», «CRM + сайт + AI-консультант + техническая поддержка», VipLabel, PWA, «Медтехника Гродно — IID», «Поездка в Могилев», сайты *.ipd.by и дубль «Mysteria — Монтаж видео». При сортировке по умолчанию `updated_at desc` они попадали в начало списка и путали Алексея.
- Исправление: удалены 17 лишних (не производственных) проектов вместе со связанными задачами/шагами/токенами (каскад, как в `AdminProjectController::destroy()`). В системе остались только 6 производственных проектов: Mysteria, Каменецкое благочиние, Медтехника Гродно, Алексей Гапеев, Бобер Боб, Mysteria — Монтаж видео (направление «Производство», external_id=11).
- Профилактика: фильтр «Только производство» в коде (`SyncManager::filterProduction()`, `config/sync.php`, чекбокс на `/admin/sync`) включён по умолчанию — при следующих синхронизациях лишние проекты импортироваться не будут.
- Бэкап перед очисткой: `my_ipd_backup_20260810_1112.sql` (дамп полной БД до удаления).
- `APP_VERSION` изменён: `0.26.0` → `0.26.1` (`.env`, `.env.example`).
v0.26.0
09.08.2026
PATCH
Производство и статистика на панели управления; Системные подсказки на панели управления
Исправление: стабильный запуск полного набора тестов (`php artisan test` — 62/62)
- Причина массовых падений: `phpunit.xml` использует `sqlite :memory:`. Часть тестов использовала трейт `DatabaseTransactions`, который получает пустую базу (схема создаётся только `RefreshDatabase`-тестами) — отсюда «no such table» при изолированном или переупорядоченном запуске (в т.ч. `ApiProjectApiTest`: 2 теста «no such table: api_keys»).
- Исправление: тесты переведены на `RefreshDatabase` (со `$this->seed()` там, где нужны сидированные данные): `ApiProjectApiTest`, `ApiKeyTest`, `DesignSystemRenderTest`, `ManualTasksFeatureTest`, `ProjectOwnerRolesFeatureTest`, `ProjectSyncTest`, `TempAuthDiagTest`.
- Исправлены устаревшие/ошибочные тесты:
- Результат: `php artisan test` — 62 passed, 0 failed, 0 errors (273 assertions).
Разделение проектов на панели управления: «Производство» и «Статистика»
- Главный раздел «Проекты (Производство)»: на панели управления (`/dashboard`) теперь основными показываются только активные проекты направления «Производство» (направление id=16, внешний id=11). Локально это Mysteria, Каменецкое благочиние, Алексей Гапеев.
- Раздел «Статистика»: все остальные проекты (другие направления и паузы) вынесены в отдельный раздел ниже — они не засоряют главную ленту, но остаются полностью доступными.
- Кнопка «Показать завершённые проекты (N)»: в разделе «Статистика» кнопка раскрывает архив завершённых проектов (статус `completed`) со значком «Завершён». Ничего не удаляется — все проекты остаются в системе и доступны через карточки.
- Контроллер `Dashboard\DashboardController`: метод `splitProjects()` разделяет проекты на три группы — производственные активные, остальные и завершённые (определение направления «Производство» по `external_id = 11` или названию). Для админа список проектов больше не ограничен 20 — показываются все, чтобы раздел «Статистика» был полным.
- Автотест `tests/Feature/DashboardProjectsSplitFeatureTest.php` (2 теста): разделение для админа (все проекты) и для клиента (собственные проекты), наличие секций и кнопки архива.
- `APP_VERSION` изменён: `0.25.2` → `0.26.0` (`.env`, `.env.example`).
Исправление: русские названия проектов отображались «кракозябрами»
- Причина: при создании дампа локальной БД использовался PowerShell-канал (`| Out-File` / `>`), который перекодировал вывод mysqldump через CP866 — UTF-8 байты ломались (двойная кодировка). Русские строки (проекты, задачи, клиенты) на staging (`my_ipd_dev_app`) и prod (`my_ipd_app`) оказались искажены.
- Исправление: дамп создан через `cmd /c` (как в инструкции `03_Резервное_копирование_тестового_сайта.md`): `mysqldump --default-character-set=utf8mb4 --single-transaction --routines --triggers`. Обе БД пересозданы из корректного дампа локальной `my_ipd_local` — данные на staging и prod снова в правильном UTF-8.
- `APP_VERSION` изменён: `0.26.0` → `0.25.2` (`.env`, `.env.example`).
Подсказки на панели управления
- Новая секция «Системные подсказки» на панели управления (`/admin`): система сама подсказывает, что требует внимания и куда перейти.
- Почта не настроена: если SMTP-сервер не подключён (пустой хост или отправитель) — подсказка «Почта не настроена» с переходом на вкладку «Почта» в настройках. После заполнения настроек SMTP подсказка исчезает.
- Синхронизация: если синхронизация ещё ни разу не выполнялась — подсказка «Синхронизация ещё не выполнялась» с переходом на страницу синхронизации; если последняя синхронизация была более 7 дней назад — предупреждение «Данные могли устареть».
- Что добавилось на последней синхронизации: из журнала активности разбирается статистика последнего импорта и показывается подсказка «Что добавилось на последней синхронизации» (сколько проектов/задач/шагов/клиентов добавлено и изменено).
- Задачи ждут подтверждения: если у клиентов есть задачи в статусе «Ожидает подтверждения» — подсказка с их количеством и переходом к проектам.
- Клиенты не подтвердили email: если есть клиенты без подтверждённого email — подсказка с переходом к списку клиентов.
- Сервис `AdminDashboardHintsService` формирует подсказки (уровень: danger/warning/success/info, текст, ссылка-действие).
- Автотест `tests/Feature/AdminDashboardHintsFeatureTest.php` (9 тестов): каждая подсказка, скрытие при решённой проблеме и рендер секции на дашборде.
- `APP_VERSION` изменён: `0.25.1` → `0.26.0` (`.env`, `.env.example`).
v0.25.1
07.08.2026
MINOR
Статус «ожидает подтверждения» для задач клиента
Подтверждение выполненной клиентом задачи администратором
- Новый статус `awaiting_confirmation` («Ожидает подтверждения») для задач клиента (`client_tasks`). Когда клиент нажимает «Отметить выполненной», задача теперь не становится сразу «Выполнена», а переходит в статус «Ожидает подтверждения» с фиксацией даты и автора отметки.
- Админ-интерфейс: на странице проекта у задачи в статусе «Ожидает подтверждения» появляются кнопки «Подтвердить» (задача становится «Выполнена», данные об отметке клиента сохраняются) и «Вернуть» (возврат в работу, отметка снимается). В форме редактирования задачи добавлен третий статус.
- Клиентский кабинет: статус «Ожидает подтверждения» отображается клиенту с пояснением, что задача ожидает подтверждения администратором; кнопка повторной отметки скрыта до подтверждения.
- Уведомления: при подтверждении администратором клиентам проекта создаётся уведомление «Задача подтверждена администратором» и отправляется письмо (ClientTaskMail, журналируется в MailLog).
- Статусная колонка расширена до 30 символов (миграция) — значение `awaiting_confirmation` длиннее 20 символов.
- Автотест `tests/Feature/ClientTaskConfirmationFeatureTest.php` (8 тестов): полный цикл отметка → ожидание → подтверждение/возврат, включая рендер страниц.
- `APP_VERSION` изменён: `0.25.0` → `0.25.1` (`.env`, `.env.example`).
v0.25.0
06.08.2026
PATCH
Синхронизация производства и уведомления клиентов
Синхронизация с task-платформой: производство и шаги (Задание 83)
- Нормализация экспорта: внешний экспорт `/api/export/full` отдаёт русскоязычные ключи (`ид_задачи`, `название`, `шаги` и т.д.). Добавлен `TaskExportNormalizer`, который приводит данные к единому формату — импорт снова работает корректно.
- Импорт шагов: задачи теперь привозят шаги (`steps`) с признаком выполнения. Прогресс задачи и проекта считается по шагам и отображается клиенту («что сделано»).
- Дедлайны и статусы: синхронизируются `дедлайн`, описание и статусы задач/проектов (активная → активна, в_работе → в работе, выполнена → выполнена и т.д.).
- Только производство: по умолчанию синхронизируются только проекты и задачи направления «Производство» (направление 11) — у них заполнено поле «проект». Переключатель на странице синхронизации.
- Аналитика синхронизации: после выполнения показывается, что добавлено и что изменено (по сущностям и по полям «было → стало»), в том числе по шагам.
- Видимость клиенту: производственные задачи автоматически помечаются видимыми клиенту — клиент видит стадию производства, шаги, сроки и время обновления.
Уведомления клиентам о смене статусов (Задание 83)
- При синхронизации отслеживаются изменения: добавлена задача, задача выполнена, изменён статус, выполнен шаг.
- Клиенту создаются уведомления в кабинете (ClientNotification) и отправляется письмо на почту (TaskUpdateMail, журналируется в MailLog).
Задачи для клиента (обязательства клиента) (Задание 83)
- Новая сущность **«Задачи для клиента»** (`client_tasks`): администратор создаёт задачи, которые должен выполнить клиент (прислать материалы, подписать договор, предоставить доступ и т.п.) — название, описание, срок, приоритет и внутренняя заметка.
- Внутренняя заметка (`admin_note`) видна только администратору — клиент её не видит (рабочие заметки о клиенте остаются закрытыми).
- Админ-интерфейс: на странице проекта (`/admin/projects/{id}`) — раздел «Задачи для клиента»: создание, редактирование, отметка «выполнено/вернуть в работу», удаление.
- Клиентский кабинет: на странице проекта — раздел «Задачи для вас»: срок, описание, пометка «просрочено», кнопка «Отметить выполненной». Выполнение клиентом фиксируется в журнале.
- Уведомления: при создании/изменении задачи для клиента (если администратор отметил «уведомить») клиентам проекта создаётся уведомление в кабинете (ClientNotification) и отправляется письмо (ClientTaskMail, журналируется в MailLog).
- `APP_VERSION` изменён: `0.24.5` → `0.25.0` (`.env`, `.env.example`).
v0.24.5
06.08.2026
MINOR
Исправлено: тариф на странице проекта не сохранялся
Исправление: тариф не сохранялся при нажатии «Сохранить» (Задание 81)
- Причина: на странице редактирования проекта (`/admin/projects/{id}/edit`) селект «Тариф» находился в отдельной форме «Оформить подписку по проекту», а кнопка «Сохранить» внизу страницы отправляла только основную форму проекта. Выбранный тариф никуда не передавался — система писала «сохранено», а по факту тариф не создавался.
- Исправление: выбор тарифа (клиент, тариф, дата начала, окончание, дедлайн, оплата, комментарий) теперь находится внутри основной формы проекта и сохраняется обычной кнопкой «Сохранить». Если по проекту уже есть активная подписка — она обновляется, если нет — создаётся.
- История подписок вынесена на отдельную вкладку «История подписок» — там статусы и удаление подписок работают как раньше.
- Страница просмотра проекта (`/admin/projects/{id}`) теперь показывает тариф: в карточках добавлены «Тариф» (цветной бейдж), «Оплата», «Окончание (договор)» и «Дедлайн».
- `APP_VERSION` поднят: `0.24.4` → `0.24.5` (`.env`, `.env.example`).
v0.24.4
06.08.2026
PATCH
Тариф и статус проекта в списке и на дашборде
Админ: тарифные колонки в списке проектов (Задание 81)
- Список проектов (`/admin/projects`): добавлены колонки **«Тариф»**, **«Оплата»**, **«Подключён»**, **«Окончание»**, **«Дедлайн»** — теперь сразу видно, у кого какой тариф, когда подключили, когда заканчивается оплата и срок выполнения. Колонки входят в «Настройка полей» (можно скрывать/показывать), по умолчанию видны тариф, оплата, окончание и дедлайн.
- Индикация в ячейках: тариф — цветной бейдж по цвету тарифа; оплата — «Оплачено»/«Не оплачено»; окончание — красный бейдж, если оплата истекла; дедлайн — жёлтый, если до срока ≤ 7 дней, красный — если просрочен.
- Подписки (`subscriptions.tariff`) подгружаются в списке проектов, добавлен аксессор `Project::active_subscription` (активная подписка, последняя по дате старта).
Админ: индикаторы на дашборде (`/admin/dashboard`)
- Секция «Требуют внимания» проекты с проблемами сгруппированы сверху — не внесена предоплата, оплата по тарифу истекла, дедлайн просрочен или близко, в проекте нет задач, задачи не обновлялись давно, синхронизация устарела. Красные бейджи — критично, жёлтые — предупреждение. Если проблем нет, выводится «Все проекты в порядке».
- Карточки проектов обогащены: бейдж тарифа (цвет тарифа), статус оплаты, «до {дата}» окончания оплаты, дедлайн, а в футере — счётчик проблем/предупреждений или «OK».
- `APP_VERSION` поднят: `0.24.3` → `0.24.4` (`.env`, `.env.example`).
v0.24.3
06.08.2026
PATCH
Расположение кнопок на странице проекта
Usability: кнопки на странице редактирования проекта (Задание 80)
- «Сохранить» теперь в самом низу страницы на всех вкладках, включая «Тариф»: кнопка вынесена за пределы формы проекта (связь через `form="project-form"`) и стоит последней. Раньше на вкладке «Тариф» она оказывалась выше содержимого, а внизу была «Опасная зона» — пользователь машинально тянулся вниз и мог случайно удалить проект.
- «Отмена» перенесена в верхний правый угол карточки (рядом с заголовком) — продублированная кнопка внизу удалена.
- «Удалить проект» убрана со страницы редактирования (блок «Опасная зона» удалён) — она больше не висит под каждым табом.
- Kebab-меню на карточке проекта (`/admin/projects/{id}`): в правом верхнем углу шапки добавлена кнопка «⋮», по нажатию — пункт «Удалить проект» с подтверждением. Удаление доступно только оттуда.
- `APP_VERSION` поднят: `0.24.2` → `0.24.3` (`.env`, `.env.example`).
v0.24.2
06.08.2026
PATCH
Починено сохранение тарифа на странице проекта + дедлайн
Исправление: не сохранялась подписка по проекту (Задание 79)
- Причина: вкладка «Тариф» на странице редактирования проекта находилась внутри основной формы сохранения проекта — браузер игнорирует вложенные `<form>`, и кнопка «Оформить подписку по проекту» отправляла форму обновления проекта без полей тарифа. Данные не сохранялись.
- Исправление: панель «Тариф» (форма оформления и таблица подписок) вынесена из формы проекта — теперь обе формы работают независимо, подписка по проекту сохраняется корректно.
- Поле «Дедлайн» (`deadline`) у подписки: в форме тарифа проекта вместо «Следующий платёж» теперь поле «Дедлайн (срок выполнения)» — к какому сроку выполнить задание / первую итерацию (по методологии Iterative Incremental Delivery). Поле добавлено и в форму на карточке пользователя.
- Отображение дедлайна: колонка «Дедлайн» в таблицах подписок (вкладка «Тариф» проекта и карточка пользователя), а клиенту на странице «Тариф» (`/tariff`) показывается: тариф, факт внесения предоплаты (сумма) и дедлайн выполнения.
- `APP_VERSION` поднят: `0.24.1` → `0.24.2` (`.env`, `.env.example`).
v0.24.1
06.08.2026
PATCH
Тариф на странице проекта
Тариф на странице редактирования проекта (Задание 78)
- Админ: вкладка «Тариф» на странице редактирования проекта (`/admin/projects/{id}/edit`): можно сразу отметить тариф по проекту — выбрать клиента (по умолчанию владелец проекта), тариф, даты начала/окончания, следующий платёж, оплачено и комментарий админа. Подписка привязывается к проекту и клиенту.
- Список подписок по проекту на той же вкладке: тариф, клиент, статус, даты, платёж, оплата; смена статуса и удаление подписки возвращают обратно на страницу проекта.
- Клиент видит тариф: оформленная по проекту подписка попадает в личный кабинет клиента (раздел «Тариф» на `/tariff`) и в карточку пользователя в админке.
- Связь `Project::subscriptions()` добавлена в модель.
- `APP_VERSION` поднят: `0.24.0` → `0.24.1` (`.env`, `.env.example`).
v0.24.0
05.08.2026
PATCH
Тарифы и подписки
Тарифы и подписки (Задание 77)
- Админ: раздел «Тарифы» (`/admin/tariffs`): полный CRUD тарифов — название, slug, описание, преимущества, базовая цена, рассрочка (месяцев × платёж), часовая ставка, время реакции, уровень поддержки, цвет, иконка, порядок, флаги (публичный/популярный/VIP), пререквизит для VIP-тарифа. Карточка тарифа в списке показывает цену, рассрочку, часовую ставку, статус, число подписок.
- 5 тарифов из калькулятора перенесены и засеяны (`TariffSeeder`): Базовый запуск (3 000 BYN), Цифровая архитектура (6 000 BYN), Экосистема роста (12 000 BYN), Стратегическое партнёрство (15 000 BYN, VIP — доступен после тарифа 3), Итеративная инкрементная поставка (6 000 BYN).
- Админ: блок «Тариф и подписка» в карточке пользователя (`/admin/users/{id}`): оформление подписки клиенту (тариф, проект, даты начала/окончания, следующий платёж, оплачено, комментарий), таблица подписок со сменой статуса (активна/отменена/завершена/истекла) и удалением.
- Таблица `client_subscriptions` client_id, tariff_id, project_id, статус, оплачено, даты, ежемесячный платёж, сумма, число платежей, комментарий админа.
- Клиент: страница «Тариф» (`/tariff`): карточки публичных тарифов с ценой, рассрочкой, преимуществами, временем реакции и уровнем поддержки; VIP-тариф показывается закрытым с указанием пререквизита; вверху — текущая активная подписка клиента (тариф и дата следующего платежа).
- Меню: у клиента — пункт «Тариф», у админа — «Тарифы».
- `APP_VERSION` поднят: `0.23.0` → `0.24.0` (`.env`, `.env.example`).
v0.23.0
05.08.2026
MINOR
Восстановление доступа и воронка писем
Восстановление доступа (Задание 76)
- Кнопка «Забыли пароль?» на странице входа (`/login`): открывается форма восстановления доступа, где достаточно ввести email — на него отправляется 5-значный код.
- Форма восстановления (`/login/forgot`): ввод email → отправка кода → ввод кода → вход в личный кабинет без пароля. Код действителен 5 минут, повторную отправку можно сделать через 60 секунд.
- Письма с кодом восстановления (`password_reset`): шаблон письма с крупным кодом, названием проекта, кнопкой «Перейти на страницу проекта». Отправляется через SMTP-настройки платформы (вкладка «Почта» в настройках) или в лог в режиме разработки.
- Воронка писем (`mail_logs`): каждая отправка фиксируется: `pending → sent/failed → delivered (принято сервером) → opened (пиксель-трекер) → clicked (переход по кнопке) → verified (ввод кода) → authed (вход в кабинет)`.
- Пиксель-трекер открытия (`/p/{token}/mail-open/{logId}` — отдаёт 1×1 GIF) и **трекер кликов** (`/p/{token}/mail-click/{logId}` с перенаправлением на страницу проекта или вход).
- Админ: раздел «Почта» (`/admin/mail`): статистика воронки (отправлено / доставлено / открыто / клики / подтверждено / входы / ошибки), список всех писем с фильтрами по типу и статусу, кнопка «Отправить тестовое письмо», удаление записей.
- Клиент: раздел «Сообщения» (`/mail`): список всех писем, отправленных клиенту платформой, с типом, темой, статусом и статистикой.
- Уведомления в кабинете: при подтверждении email и при восстановлении доступа клиенту приходит уведомление в личный кабинет.
- Подтверждение email из личного кабинета: если email не подтверждён, на дашборде показывается баннер «Подтвердите ваш email» с кнопкой отправки кода и формой ввода — подтвердить почту можно, не переходя по ссылке из письма (`/settings/verify-email/send`, `/settings/verify-email/verify`).
- Настройки → вкладка «Почта»: SMTP-сервер (хост, порт, логин, пароль, шифрование), отправитель, темы писем, вступительный текст и подпись.
- `APP_VERSION` поднят: `0.22.2` → `0.23.0` (`.env`, `.env.example`, `config/app.php`).
v0.22.2
05.08.2026
MINOR
Токены доступа сразу после ссылки для клиента
Расположение токенов на странице проекта (Задание 75)
- Блок «Токены доступа» перемещён вверх теперь он идёт сразу после «Ссылка для клиента» и перед списком задач. Раньше токены были в самом низу страницы после 517 задач — приходилось долго скроллить. Теперь ссылка для клиента и генерация токенов рядом, без прокрутки.
- Логика Алексея: «работаем с токенами — они должны быть рядышком». Блок токенов (создание/генерация, таблица токенов) сохранён без изменений, изменён только порядок секций.
- `APP_VERSION` поднят: `0.22.1` → `0.22.2` (`.env`, `.env.example`, `config/app.php`).
v0.22.1
05.08.2026
PATCH
Kebab menu в списке проектов и колонка «Платформы»
Kebab menu и настройка полей (Задание 74)
- Kebab menu (три точки) в списке проектов (`/admin/projects`): вместо явных кнопок «Просмотр / Редактировать / Удалить» теперь одна компактная кнопка «⋮» с выпадающим меню. Внутри меню: Просмотр, Редактировать, История, Экспорт, а **Удалить — в самом низу отдельным красным пунктом** (защита от случайного нажатия). Кнопка удаления больше не висит рядом с часто используемым «Редактировать».
- Колонка «Платформы» в настройках полей: в выпадающем меню «Настройка полей» появился пункт «Платформы» — при включении показывается колонка, где видно, каким платформам принадлежит каждый проект (бейджи с иконками платформ; для `production.ipd.by` — пометка «(скоро)»). По умолчанию колонка скрыта, как «Пользователи»/«Токен».
- Поведение меню: открывается по клику, закрывается по клику вне меню, по Escape и при открытии другого кебаб-меню.
- `APP_VERSION` поднят: `0.22.0` → `0.22.1` (`.env`, `.env.example`, `config/app.php`).
v0.22.0
05.08.2026
PATCH
Платформы: единый центр экосистемы IPD
Платформы (Задание 73)
- Новый раздел «Платформы» для клиентов (`/platforms`): единый центр экосистемы IPD, из которого клиент по своему аккаунту (email) переходит на другие платформы без повторной авторизации — аналог экосистемы Google.
- Платформы привязываются к пользователям и к проектам: доступ к платформе клиент получает, если платформа привязана к его аккаунту напрямую или к его проектам (через владение или связь).
- SSO-переход без повторной авторизации: для перехода создаётся одноразовый короткоживущий токен, привязанный к аккаунту клиента; клиент бесшовно перенаправляется на целевую платформу. Токены хранятся в отдельной таблице и имеют ограниченный срок действия.
- Платформы:
- Новые таблицы: `platforms` (name, slug, url, description, icon, color, is_active, is_client_accessible, sort_order), `client_platform` (привязка к клиентам), `project_platform` (привязка к проектам), `platform_sso_tokens` (SSO-токены).
- Администрирование платформ (`/admin/platforms*`): CRUD платформ, привязка клиентов и проектов, статусы «Доступна клиентам» / «Активируется позже» / «Неактивна», сортировка, иконки и цвета. Все действия логируются в `activity_log` (`platform_created`, `platform_updated`, `platform_deleted`, `platform_enter`).
- Интеграция с пользователями: на странице пользователя (`/admin/users/{id}`) секция «Платформы» с таблицей привязок; в форме редактирования — чекбоксы доступных платформ.
- Интеграция с проектами: на странице проекта (`/admin/projects/{id}`) секция «Платформы» с бейджами статуса; в форме редактирования — таб «Платформы» с чекбоксами.
- Сайдбар: пункт «Платформы» в клиентской навигации и «Платформы (Админ)» в администрировании.
- `APP_VERSION` поднят: `0.21.2` → `0.22.0` (`.env`, `.env.example`, `config/app.php`).
v0.21.2
05.08.2026
MINOR
Проекты у пользователя: кнопка редактирования и управление проектами
Проекты у пользователя (Задание 72)
- Кнопка «Редактировать» на странице пользователя (`/admin/users/{id}`): теперь в шапке рядом с именем и внизу страницы есть кнопки «Редактировать» и «Безопасность», которые ведут на форму редактирования и настройки безопасности. Раньше кнопки редактирования на странице пользователя не было.
- Управление проектами на странице пользователя: в таблицах «Проекты (владеет)» и «Проекты (доступ через связи)» у каждого проекта появились кнопки «Редактировать» (переход к редактированию проекта) и «Убрать» (снятие проекта с пользователя).
- Снятие владения проектом в форме редактирования: добавлена секция «Проекты (владеет)» с чекбоксами — можно снять отметку с проекта, чтобы передать его в «без владельца» (при сохранении `client_id` проекта становится `null`, действие логируется в `activity_log`).
- `APP_VERSION` поднят: `0.21.1` → `0.21.2` (`.env`, `.env.example`, `config/app.php`).
v0.21.1
05.08.2026
PATCH
Исправление редактирования пользователя, toast-уведомления
Исправление редактирования пользователя (Задание 71)
- Исправлен критический баг формы редактирования (`/admin/users/{id}/edit`): вложенные `<form>` (Подтвердить email/Telegram/телефон) внутри главной формы ломали HTML-структуру — браузер закрывал главную форму на первой вложенной, из-за чего чекбоксы «Активен» и «Проекты» и кнопка «Сохранить» оказывались вне формы и **сохранение не отправлялось**. Проверки перенесены в отдельные формы (`form`-атрибут кнопок), главная форма снова корректна.
- Toast-уведомления: при любых действиях (сохранение, подтверждение email, ошибки) в правом верхнем углу появляется всплывающее сообщение (зелёное — успех, красное — ошибка, синее — инфо), исчезает через ~3,5 сек. Добавлены также стили для `alert-error` и `alert-info` (показ ошибок в верхней части контента).
- `APP_VERSION` поднят: `0.21.0` → `0.21.1` (`.env`, `.env.example`, `config/app.php`).
v0.21.0
05.08.2026
PATCH
Редактирование пользователя и логирование действий
Редактирование пользователя (Задание 70)
- Исправлено сохранение имени/фамилии при редактировании пользователя (`/admin/users/{id}/edit`): изменения полей name, email, company, phone, telegram, role, is_active, пароль сохраняются и отображаются на странице пользователя.
- Логирование изменений пользователя: при каждом обновлении в `activity_log` записывается действие `user_updated` с деталями изменённых полей (старое значение → новое), например: `name: «Егор» → «Егор Савинский»`.
- Логирование всех действий администрирования пользователей: создание (`user_created`), удаление (`user_deleted`), подтверждение email/telegram/телефона (`user_email_verified` и др.), настройки безопасности (`user_security_updated`). В каждом логе — кто (администратор), когда, IP и детали.
- Новый пункт меню «Логи» в разделе «Администрирование» сайдбара → страница `/admin/logs` (журнал действий по всей системе, с фильтром по действию и клиенту, пагинация).
- `APP_VERSION` поднят: `0.20.4` → `0.21.0` (`.env`, `.env.example`, `config/app.php`).
v0.20.4
04.08.2026
MINOR
Восстановление пользователей и полный переимпорт данных
Восстановление данных (Задание 68)
- Критическая находка: приложение VDS my-dev.ipd.by реально использует БД **`my_ipd_dev_app`** (v0.20.x), а не `my_ipd_dev` (v0.14.x), из которой данные были импортированы в v0.20.3.
- Пользователи восстановлены: **9 клиентов** импортировано из актуальной БД (Администратор, Иван Петров, Тестовый Пользователь, Демо Клиент, **Егор** (real client, egor@jetpeel.club, +7 926 899 4992), Алексей, Анна Петрова, Иван Сидоров, Тестовый Клиент).
- Привязки и токены: импортированы `client_project` (Егор → Бобер Боб), **4 client_tokens** (включая bbOiPf…, views 7), 5 `client_auth_known_accounts`.
- Полный переимпорт из `my_ipd_dev_app`: 14 направлений, 13 проектов, **639 задач** (330 completed / 244 in_progress / 65 planned), **259 steps** — заменили неполные данные v0.20.3 (11/14/247).
- Исправлены данные v0.20.3: проекты task.ipd.local / ipd.local / OSPanel / DOS заменены на реальные (Дизайн-проект, Mysteria ×2, Медтехника Гродно ×2, Бобер Боб и др.); `pending` → `planned`; пересчитаны tasks_count / tasks_total / tasks_completed / progress_pct.
- `APP_VERSION` поднят: `0.20.3` → `0.20.4` (`.env`, `.env.example`, `config/app.php`).
Настройка полей проекта (Задание 69)
- Новая колонка «Тотал командер» в списке `/admin/projects`: показывает наличие `tc_path` (иконка папки + имя последней папки пути, tooltip с полным путём) и доступна в выпадающем меню «Настройка полей».
- Новая колонка «Редактирован» (`updated_at`): видна по умолчанию, доступна в «Настройка полей», добавлена в сортировку.
- Сортировка по умолчанию — по дате редактирования (убыванию): последний редактируемый проект всегда сверху (`sort_by=updated_at`).
- Сохранение видимости колонок: в localStorage — новые колонки сохраняют своё состояние как и остальные.
v0.20.3
04.08.2026
PATCH
Восстановление данных с VDS
Восстановление данных
- Восстановлены реальные данные с VDS my-dev.ipd.by (v0.14.3) в локальную БД my_ipd_local (v0.20.3)
- 11 направлений импортировано (Продажи, Производство, Развитие, Личное, AmoCRM, Маркетинг, Поддержка/сервис, Сайты, Автоматизация, Обучение, Финансы)
- 14 проектов импортировано (Mysteria, Каменецкое, Медтехника Гродно, Алексей Гапеев, Бобер Боб, АгроБетон, AmoCRM, и др.)
- 247 задач импортировано с привязкой к проектам и направлениям
- Транслитерация статусов русские → английские (активно→active, завершена→completed и др.)
- VDS-специфичные поля сохранены в JSON meta (risks, ai_note, horizon, completed_date, start_date)
- ВАЖНО: в v0.20.4 данные этого раздела заменены на полный переимпорт из актуальной БД `my_ipd_dev_app` (этот раздел исторический)
Исправления
- Исправлен инцидент TaskApiService.php на VDS (Undefined array key "status")
v0.20.0
03.08.2026
PATCH
Страница «О платформе»: полный редизайн по образцу
Страница «О платформе» (Задание 64)
- Страница `/about` переписана полностью по образцу `admin-platform-heart.ipd.test/about` (платформа Admin Platform Heart, ООО «Студия ИПД»). Сохранена вся логика и архитектура образца, дизайн пересобран под фирменные токены IPD (синий акцент, светлая/тёмная тема, единый CSS).
- Hero-блок: логотип IPD (белая плитка с тремя растущими барами — фирменный мотив), бейдж «Система управления проектами», версия платформы, заголовок, подзаголовок и описание.
- Что это за платформа: сетка из шести ячеек с SVG-иконками (направления и проекты, задачи с шагами, гостевые ссылки, документы, синхронизация, админ-панель).
- Информация о системе: платформа + версия, PHP, стек (Laravel + Vite/Tailwind), БД, авторизация, сервер, среда.
- Кто развивает: блок ООО «Студия ИПД» с официальным юрлицом, сертификаты партнёра amoCRM (amoSTART, 31 июля 2026) с лайтбоксом для просмотра и поясняющей заметкой.
- Контакты: фото Алексея Гапеева (`public/img/admin/aleksey-gapeev.jpg`), Telegram-бот, личный Telegram, два телефона, email — кликабельные ссылки (t.me/tel/mailto).
- История версий: парсер changelog.md расширен — теперь показываются не только версия/дата/заголовок, но и разделы (`###`) с пунктами (`- **Жирное** текст`) и бейджи типа релиза (MAJOR/MINOR/PATCH).
- Логика/архитектура взята из образца: новый контроллер `app/Http/Controllers/AboutController.php` (парсер changelog + данные platform/company/contacts/certificates/system), маршрут `/about` переведён на контроллер.
- Стили в `app.css`: страница оформлена токенами дизайн-системы (hero с градиентом и декоративными radial-градиентами, карточки, сетки, info-строки, бейджи версий, лайтбокс). Без инлайн-`<style>` (требование теста `assertDontSee('</style>')`).
- Сертификаты скопированы в `public/assets/certificates/` (4 webp: disp + full).
- Проверено в headless Chrome (Playwright): hero с логотипом и версией, 6 ячеек, 7 info-строк, 2 сертификата с открытием лайтбокса, 5 контактов, история версий (45 записей с секциями), кнопки ссылок; без ошибок консоли; светлая и тёмная темы.
- `APP_VERSION` поднят: `0.19.4` → `0.20.0` (`.env`, `.env.example`, `config/app.php`).
- Тест `DesignSystemRenderTest::test_version_shows_0_19_4` → `test_version_shows_0_20_0`.
v0.19.4
03.08.2026
MINOR
Дизайн в зависимости от URL: z-index футера и смещение кебаба
Причина (Задание 63): две независимые проблемы, обе «привязаны» к страницам первого уровня
- 1) `.version-footer` с `z-index: 1000` перекрывал контент на dashboard. Плашка версии (`partials/version-footer.blade.php`) — `position: fixed; bottom: 12px; left: 12px; z-index: 1000`. На страницах, где она есть (`/dashboard`), фиксированный элемент с z-index 1000 ложился поверх карточек/меню; при отключении z-index визуально всё приходило в норму (подтвердил Алексей, тестируя в DevTools). **Исправлено:** `z-index: 1000` убран из `.version-footer`.
- 2) Инлайн-стили карточного кебаба перетекали на topbar-кебаб `#kebabWrap` → кнопка ⋮ смещалась на ~10–12px вниз. В `dashboard/index.blade.php` и `admin/index.blade.php` карточки проектов используют `.kebab-wrap { position: absolute; top: 12px; right: 12px; }` и `.kebab-btn { padding: 4px 6px; font-size: 18px; line-height: 1; }` (специфичность 0,1,0). Эти же классы совпадали с topbar-кебабом (`#kebabWrap`, `#kebabBtn`), который лежит в лэйауте **на всех app-страницах**. Правило `body.app-layout #kebabWrap { position: relative }` из `app.css` (специфичность 0,2,1) перебивало `position: absolute` (что хорошо — topbar-кебаб должен быть relative), но **не перебивало** `top: 12px` и `padding/font-size/line-height` у `.kebab-btn`, потому что в `app.css` эти свойства для topbar-кебаба не заданы вовсе. Итог: на `/dashboard` и `/admin` (страницы первого уровня, где есть карточные кебабы) topbar-кнопка ⋮ опускалась на 12px и получала лишний padding. На `/admin/sync`, `/admin/projects` и других глубоких страницах инлайн-блока с `.kebab-wrap` нет — смещения нет. Отсюда и «дизайн зависит от URL».
- Исправление: инлайн-стили карточного кебаба заскоуплены под `.pr-card` в обоих файлах (`.pr-card .kebab-wrap`, `.pr-card .kebab-btn`, `.pr-card .kebab-menu`, ...). Теперь они влияют только на кебабы внутри карточек и не трогают topbar `#kebabWrap`/`#kebabBtn`.
- Проверено в headless Chrome (Playwright): на `/dashboard` и `/admin` topbar-кнопка ⋮ выровнена по центру с колокольчиком и user-menu (cy=37, до фикса 49 — смещение устранено); карточный кебаб по-прежнему `position: absolute; top: 12px`; `version-footer` `z-index: auto`; ошибок консоли нет. `/admin/sync`, `/admin/projects` — без изменений (как и было).
- `APP_VERSION` поднят: `0.19.3` → `0.19.4` (`.env`, `.env.example`, `config/app.php`).
- Тест `DesignSystemRenderTest::test_version_shows_0_19_3` → `test_version_shows_0_19_4`.
v0.19.3
03.08.2026
PATCH
Исправление «слетевшего» дизайна на страницах первого уровня
Причина: конфликт CSS-специфичности у kebab-кнопок (Задание 62)
- Симптом: на страницах первого уровня вложенности (`/dashboard`, `/admin`) «слетал» дизайн карточек: кнопка-«кебаб» (⋮) оказывалась в левом верхнем углу карточки как квадратная кнопка 36×36 с рамкой и заливкой, сдвигая контент вниз. На страницах 2–3-й глубины (`/admin/settings`, `/admin/projects`, ...) визуально всё было в порядке.
- Гипотеза о кривых путях опровергнута: все `<link>`/`<script>`-ассеты абсолютные (`https://my.ipd.test/build/assets/...`), одинаковые на всех страницах, независимо от глубины URL. Относительных `src`/`href`/`action`/`url()` в HTML и CSS нет.
- Настоящая причина — специфичность селекторов: для kebab-меню в шапке (`#kebabWrap`) в `resources/css/app.css` были общие правила `body.app-layout .kebab-wrap { position: relative }` и `body.app-layout .kebab-btn { width: 36px; ... border: 1px solid ... }` (специфичность 0,2,1). Эти же классы использовали карточки проектов на `/dashboard` и `/admin` со своими инлайн-стилями `.kebab-wrap { position: absolute; top: 12px; right: 12px }` и `.kebab-btn { background: none; border: none }` (специфичность 0,1,0). Правила из `app.css` выигрывали по специфичности и «ломали» карточный кебаб.
- Исправление: стили topbar-кебаба в `resources/css/app.css` заскоуплены на `#kebabWrap` (`body.app-layout #kebabWrap ...`) — карточный кебаб теперь получает свои инлайн-стили: `position: absolute; top: 12px; right: 12px`, прозрачная кнопка без рамки, меню раскрывается в правом верхнем углу карточки.
- Проверено в headless Chrome (Playwright): на `/dashboard` и `/admin` `.pr-card .kebab-wrap` имеет `position: absolute`, кнопка прозрачная (30×29px, без рамки), позиция в правом верхнем углу карточки; topbar `#kebabWrap` по-прежнему `position: relative` с кнопкой 36×36; меню открывается по клику; ошибок консоли нет. `/admin/settings` не содержит карточных кебабов — поэтому там дизайн и не «слетал».
- `APP_VERSION` поднят: `0.19.2` → `0.19.3` (`.env`, `.env.example`, `config/app.php`).
- Тест `DesignSystemRenderTest::test_version_shows_0_19_2` → `test_version_shows_0_19_3`.
v0.19.2
03.08.2026
PATCH
Сайт на HTTPS
HTTPS для локального сайта (Задание 61)
- `my.ipd.test` переведён на HTTPS: `herd secure my.ipd.test` сгенерировал TLS-сертификат (валиден до 03.08.2027), подписанный локальным CA «Laravel Valet CA Self Signed» (доверен в хранилище Windows). Созданы `Certificates/my.ipd.test.crt|key` и nginx-конфиг `Nginx/my.ipd.test.conf`.
- HTTP → HTTPS: запросы на `http://my.ipd.test` автоматически получают **301 → `https://my.ipd.test`** (nginx). Сайт открывается только по HTTPS.
- SAN сертификата: `my.ipd.test` + `*.my.ipd.test`.
- `APP_URL` обновлён: `http://my.ipd.test` → `https://my.ipd.test` (`.env`, `.env.example`) — все генерируемые ссылки, редиректы и формы теперь ведут на HTTPS.
- `APP_VERSION` поднят: `0.19.0` → `0.19.2` (`.env`, `.env.example`, `config/app.php`).
- Тест `DesignSystemRenderTest::test_version_shows_0_19_0` → `test_version_shows_0_19_2`.
- Существующие сессии и «запомненный» вход сохранены: конфигурация куки не изменилась, авторизация пользователей не была сброшена при переходе на HTTPS.
v0.19.0
02.08.2026
PATCH
Экосистема дизайна
Дизайн-токены и единый CSS (Задание 59)
- Весь CSS вынесен из `<style>`-блоков в `resources/css/app.css` и подключён через Vite (`@vite`). Больше не нужно править стили в 30 view-файлах: токены, типографика, sidebar, dropdowns, формы, бейджи — в одном месте.
- Дизайн-токены унифицированы: оба неймспейса переменных (`--card-bg`/`--muted` для app и `--bg-card`/`--text-muted`/`--success` для guest) определены вместе в тёмной и светлой темах. Добавлены недостающие токены (`--bg-secondary`, `--radius-md/lg`, `--info`, `--tag-bg`), которые раньше не были определены и не работали.
- Стили app- и guest-лэйаутов разграничены через классы `body.app-layout` и `body.guest-layout` — конфликт `.topbar`/`.main`/`.body` между лэйаутами исключён.
Авто-тема (light/dark/auto)
- `prefers-color-scheme`: если пользователь не выбирал тему явно, сайт следует системной теме; при смене темы ОС сайт переключается на лету. Явный выбор в localStorage по-прежнему в приоритете.
- Защита от «вспышки» темы: тема выставляется инлайн-скриптом в `<head>` до рендера, плюс CSS-фолбэк `html:not([data-theme])` для выключенного JS.
- Логика темы и мобильного меню переехала из лэйаутов в `resources/js/app.js` (единая точка).
Blade-компоненты форм
- Созданы **`x-input`, `x-textarea`, `x-select`, `x-button`, `x-checkbox`, `x-badge`** — единый API для форм: label + «*», подсказка, ошибка валидации, значение через `old()`.
- Формы переведены на компоненты (admin users/projects/tasks — create и edit): локальные `<style>`-блоки удалены, стили взяты из глобального CSS. Новые страницы смогут собирать формы из компонентов без дублирования стилей.
- Единый стандарт полей: высота 40px, focus-подсветка, стрелка у `select`, `color-scheme` под тему (календарь/скроллбар), `textarea` от 96px с `resize: vertical`.
Прочее
- Публичные страницы переведены на guest-лэйаут: вход (`/login`), установка пароля, «О платформе», гостевая сводка `/s/{token}` больше не standalone-страницы со своими `<style>`-блоками — у них единая шапка, авто-тема, футер и глобальный CSS.
- `APP_VERSION` поднят: `0.18.2` → `0.19.0` (`.env`, `.env.example`, `config/app.php`).
- Создана документация **`DESIGN_SYSTEM.md`** (токены, лэйауты, компоненты, чеклист для агентов, доступность AA).
v0.18.2
02.08.2026
MINOR
Дизайн: читаемость, контраст, поля ввода
Поля ввода (Задание 58)
- Textarea «Описание» на вкладке API key стала современной: вместо дефолтного «квадратика 170×40» — высота от 96px, ширина до 560px, скруглённые углы, `resize: vertical`, focus-подсветка (акцентная рамка + мягкое свечение). Один стандарт применён ко всем `textarea` в настройках и формах (admin projects/tasks/users).
- Подсказки и метки полей подняты с 11px до 14px.
Типографика и контраст (рекомендации Google Material Design)
- Минимум 14px для основного текста по всему сайту: убраны шрифты 9–13px у заголовков таблиц, меток, описаний, подписей, журналов, уведомлений, футеров. 13px оставлены только для мелких бейджей/кода.
- Контраст текста повышен: серые оттенки заменены на более тёмные в светлой теме (`#64748b` → `#475569` для secondary-текста), захардкоженные цвета (`#94a3b8`, `#cbd5e1`, `#eab308`, `#60a5fa` и др.) заменены на переменные темы (`--muted`, `--text-secondary`, `--warning`, `--accent`, `--purple`, `--green`, `--red`).
Сайдбар и темы
- Светлая тема — чёрный текст в боковой панели: введены переменные `--sidebar-text`, `--sidebar-text-hover`, `--sidebar-text-active` (тёмная: `#cbd5e1`/`#f8fafc`/`#fff`; светлая: `#1e293b`/`#0f172a`/`#0f172a`). Активное меню в светлой теме теперь чёрное (раньше только синяя подсветка).
- При переключении темы цвет текста реально меняется (раньше в обеих темах sidebar был один и тот же серый).
- Пройдены страницы: dashboard, проекты (dashboard/admin), пользователи, задачи, синхронизация, логи, настройки, API keys, токены, вкладка безопасности, guest-проект, вход, установка пароля, «О платформе», пагинация.
v0.18.1
02.08.2026
PATCH
API key: права доступа, платформы, история обмена
Вкладка «API key» как настоящая вкладка (Задание 57)
- Вкладка «API key» теперь выглядит как остальные вкладки Настроек: панель табов (Основные / Безопасность / Интеграции / API key / Версии системы) видна всегда, а контент API-ключей обёрнут в тот же контейнер, что и другие вкладки. Раньше на вкладке «API key» табы вообще не показывались — она выглядела как отдельная страница.
- Стили приведены в порядок: добавлены бейджи (статусы/права/платформы), кнопки `btn-sm`/`btn-accent`/`btn-red` — раньше классы были, а стилей для них в Настройках не было.
Права доступа (скоупы) для ключей
- Новое поле `abilities` (JSON) в таблице `api_keys`: ключ теперь выдаётся с конкретными правами — `projects:read` (чтение проектов), `projects:write` (редактирование), `sync:run` (запуск синхронизации). В форме создания — чекбоксы с описанием каждого права, минимум одно.
- Права ключа проверяются в единой точке middleware: запрос без соответствующего права отклоняется с кодом 403 и указанием нужного права. Устаревшие ключи без явно заданных прав проходят конвертацию в актуальную модель прав при первом обращении.
Платформа и описание ключа
- Новое поле `platform` к какой платформе/агенту выдан ключ (n8n, ИИ-агент, внешняя платформа и т.д.), с подсказками через datalist. Отображается бейджем в списке и попадает в журнал.
- Новое поле `description` произвольная заметка к ключу, показывается в списке и на странице ключа.
История обмена данными
- Страница ключа (`/admin/api-keys/{id}`, кнопка «История» в списке): полная информация о ключе + таблица обращений — время, запрос (`GET /api/projects…`), результат (код 200/403/…), IP, User-Agent. Видно, как идёт обмен данными между платформами.
- В лог добавляется `api_key_id` (новая колонка в `activity_log`): обращения привязаны к конкретному ключу; при удалении ключа история сохраняется (FK `SET NULL`).
- Запросы с запретом прав логируются отдельно (`api_key_forbidden`) — видно, кто пытался делать то, что ему не разрешено.
v0.18.0
01.08.2026
PATCH
Создание API key
Раздел «Интеграции» → «API key» (Задание 56)
- Новая вкладка «API key» в разделе «Интеграции» (Настройки → «API key»), где пользователь создаёт именованные API-ключи с названием проекта/платформы/агента. Система сама генерирует секретный ключ, показывая его один раз после создания.
- Таблица `api_keys`: `client_id` (владелец ключа), `name` (название), `token` (SHA-256 хеш секрета — сырой ключ в БД не хранится), `prefix` (видимый идентификатор для списка), `is_active`, `last_used_at`, timestamps.
- Ключ генерируется системой как криптостойкая случайная строка; в базе данных хранится только его необратимый хеш. Сырой ключ показывается один раз сразу после создания и в БД не сохраняется.
- Управление ключами: активация/отзыв (без удаления истории), полное удаление; все действия пишутся в журнал активности (`api_key_created`, `api_key_revoked`, `api_key_activated`, `api_key_deleted`).
- Доступ к API защищается ключами: система проверяет переданный ключ, отклоняет отозванные и неактивные, ведёт журнал обращений с привязкой к конкретному ключу — видно, какой пользователь что загружает и выгружает.
- Журнал обращений по ключам: на вкладке «API key» показываются последние 20 событий по ключам (кто/что/когда).
v0.17.0
01.08.2026
MINOR
Управление проектом, Total Commander, API и синхронизация
Редактирование проекта в табах (Задание 55)
- Страница редактирования переведена на вкладки (tabs) на всю ширину экрана вместо длинного списка на 640px: «Основное», «Клиенты и доступ», «Сайты проекта», «Total Commander / ИИ», «Данные». Поля распределены по категориям — открыл проект, сразу видишь, что часто редактируешь, а статичные данные (пользователи) спрятаны во вкладку, чтобы случайно не менять.
- Основное открывается по умолчанию: название, статус и большое поле описания на всю ширину (220px высоты, resize) — удобно дописывать описание и добавлять логотип компании.
- Вкладка «Данные»: External ID, источник, даты создания/обновления/последней синхронизации (только чтение) и переключатель «Проект активен».
Total Commander / ИИ (поле «искусственный интеллект»)
- Новое поле `tc_path` (строка) в таблице `projects` — путь к папке проекта в Total Commander (первоисточник правды). Пример для Mysteria: `L:\Система\02 Клиенты\03 Активная работа\Mysteria\`.
- Новое поле `meta` (JSON) с флагом `meta.ai_ready` — «Доступен ИИ-агентам (есть первоисточник)». Если включён, по проекту можно давать задания ИИ-агентам: они сканируют папку Total Commander и обновляют данные.
- Новое поле `last_synced_at` (дата) — время последней синхронизации проекта; теперь проставляется автоматически при каждой синхронизации из папки.
- Проект **Mysteria (ID 34):** заполнен `tc_path` = `L:\Система\02 Клиенты\03 Активная работа\Mysteria\`, `ai_ready = true`.
- Страница проекта: блок «Total Commander / источник правды» с путём, индикатором «README.md найден/не найден» (+ дата изменения файла), бейджем «Доступен ИИ-агентам» и датой синхронизации.
Синхронизация из папки Total Commander (первоисточник правды)
- Новый сервис синхронизации: читает `README.md` из папки, указанной в `tc_path`, и обновляет поля проекта: название (первая часть заголовка `#`), описание (цитата-блок под заголовком, кроме метаданных `**Ключ:**`), статус (строка `**Статус проекта:**`, маппинг эмодзи/слов), сайты (ссылки из таблиц README — пропускаются соцсети и нерабочие 🔴).
- Кнопка «Синхронизировать сейчас» на странице проекта — мгновенная синхронизация одного проекта.
- Artisan-команда `php artisan project:sync` синхронизация всех проектов с указанным `tc_path` (вывод: что обновлено); `--id=34` — только один проект.
- Идемпотентность: повторная синхронизация без изменений в README не трогает данные (только обновляет `last_synced_at`).
- Синхронизация фиксируется в журнале изменений (action `project_synced`).
API для ИИ-агентов (json + токен)
- Новый API (`/api/projects`, защищён передаваемым ключом доступа из окружения): система может подключиться к платформе, получить и подправить данные проекта — то, что нужно n8n и ИИ-агентам.
- `GET /api/projects` — список проектов (id, name, status, tc_path, ai_ready, links).
- `GET /api/projects/{id}` — полный экспорт проекта (схема `ipd.project.export.v1`).
- `PUT /api/projects/{id}` — обновление полей из JSON (name, description, status, external_id, tc_path, source, links, meta, is_active) с валидацией; логируется `project_updated_api`.
- `POST /api/projects/{id}/sync` — триггер синхронизации из папки Total Commander.
- Без токена или с неверным токеном — `401 Unauthorized`; несуществующий проект — `404`.
Экспорт / импорт JSON (экосистема синхронизации)
- Экспорт: кнопка «Экспорт JSON» на странице проекта скачивает полный JSON проекта (схема `ipd.project.export.v1`): название, описание, статус, external_id, tc_path, links, meta, владелец, пользователи, даты. Общий сборщик payload вынесен в сервис `ProjectExporter` (используется экспортом и API).
- Импорт: форма на странице проекта загружает JSON (выгруженный экспортом) и обновляет название, описание, статус, сайты, Total Commander, external_id, source. Валидация корректности JSON и допустимости статуса.
- Импорт фиксируется в журнале изменений (action `project_imported`).
Техническое
- `APP_VERSION`: `0.16.1` → `0.17.0` (`.env` и `config/app.php`).
- Миграция `2026_08_01_110001_add_tc_and_meta_to_projects_table.php` (`tc_path`, `last_synced_at`, `meta`).
- Маршруты: `GET /admin/projects/{id}/export`, `POST /admin/projects/{id}/import`, `POST /admin/projects/{id}/sync`, API `GET/PUT/POST /api/projects...`.
- Сервисы: `app/Services/ProjectExporter.php`, `app/Services/ProjectSyncService.php`; команда `app/Console/Commands/SyncProjects.php`; middleware `app/Http/Middleware/ApiToken.php`.
v0.16.1
01.08.2026
MINOR
Имена 2: доверие и ссылки на сайты
Блок доверия на гостевой странице (Задание 54)
- Имя с фамилией: «Алексей ведёт ваш проект» → «Здравствуйте! Я Алексей Гапеев — веду ваш проект». ФИО администратора обновлено в БД (name = «Алексей Гапеев»).
- Фотография: на гостевой странице появилась фотография Алексея (`public/img/admin/aleksey-gapeev.jpg`), размещённая рядом с приветствием — страница стала более доброжелательной и доверительной.
- Контакты по клику: по нажатию на имя «Алексей Гапеев» раскрывается блок «Связаться со мной» с Telegram-ботом (`@Aleksey_Gapeev_AI_bot`), двумя телефонами (+375 29 576-39-28, +375 29 698-83-70) и email (info@ipd.by). Контакты кликабельны (t.me, tel:, mailto:).
- Контактные данные администратора (телефон, Telegram, публичный email) хранятся в настройках платформы и отображаются на публичных страницах для связи с клиентами.
Ссылки на сайты проекта
- Новое поле `links` (JSON) в таблице `projects`: у каждого проекта теперь может быть несколько ссылок на сайты — пара «название + URL».
- Админка: поле «Сайты проекта» в форме редактирования проекта (динамический список, кнопка «+ Добавить сайт») и вывод сайтов в виде чипов на странице проекта.
- Гостевая страница: под приветствием выводятся чипы со ссылками на сайты проекта (открываются в новой вкладке).
- Проект Mysteria (ID 34): заполнены 4 сайта — mysteria.ipd.by, osteopathy.mysteria.ipd.by, my.mysteria.ipd.by, adm.mysteria.ipd.by (источник правды: `L:\Система\02 Клиенты\03 Активная работа\Mysteria\README.md`). Описание обновлено на осмысленное из README (вместо «Тестовая среда dev.mysteria.ipd.by. Контентная итерация (121 файл).»).
Техническое
- `APP_VERSION`: `0.16.0` → `0.16.1` (`.env` и `config/app.php`).
- Миграция `2026_08_01_100001_add_links_to_projects_table.php`.
v0.16.0
31.07.2026
PATCH
Имена: гостевая страница проекта
Гостевая страница проекта /p/{token} (Задание 53)
- Доброжелательное приветствие: вместо «Клиент приглашает вас ознакомиться с проектом» страница начинается с «Ваш личный проект» и блока «Алексей ведёт ваш проект. Ниже — что уже сделано и какие задачи сейчас в работе». Гость понимает, что это его проект, а ведёт его команда IPD.
- Имя администратора: учётная запись администратора переименована с «Администратор» на «Алексей» — единое личное имя в админке, логах и приветствиях.
- Заметные даты: в шапке страницы — «Проект ведётся с {дата}» и статус проекта; у каждой задачи — «создана {дата}» и срок; подписи укрупнены (13px вместо 11–12px) для читабельности.
- Email-подтверждение перенесено в конец страницы и переработано в доброжелательный CTA: блок «Хотите быть в курсе ещё удобнее?», три шага (подтвердить email → войти в кабинет → настроить уведомления), кнопка «Подтвердить email — отправить код». Раньше форма с кодом была вверху и выглядела как требование персональных данных.
- Мелкие серые тексты (8–11px «серым на сером») укрупнены и переведены на читаемые цвета: подписи сроков, сообщений, вложений, токена, подвал страницы.
- Новый подвал страницы: «IPD Platform — ведение проектов для вашего бизнеса» + пояснение, что сервис ведёт этот проект (задачи, сроки, файлы, переписка).
v0.15.3
31.07.2026
MINOR
Роли: администратор и клиент
Администратор и клиент (Задание 52)
- Разделение ролей: администратор больше не может быть владельцем проекта или участником. Администратор управляет платформой и мониторит проекты, владелец и участники — всегда клиенты (роль `client`/`test`).
- Владелец проекта стал необязательным: при создании/редактировании проекта можно оставить «Без владельца» и назначить позже. Поле `client_id` теперь nullable в валидации и формах.
- Управление на странице проекта: таблица «Пользователи» показывает владельца (бейдж «Владелец») и участников (бейдж «Участник»), с кнопками «Сделать владельцем», «Снять владельца», «+ Пользователь», «Уд.». Имя каждого пользователя — ссылка на его карточку.
- Исключение администратора из выборов: в формах проекта (владелец, пользователи) и в форме добавления пользователя на странице проекта роль «Администратор» больше не появляется.
- Проект «Mysteria» (ID 34): владелец изменён с администратора на клиента Владимира; администратор больше не фигурирует как владелец проекта.
- Исправлен переход из кабинета клиента: карточки проектов на странице «Мои проекты» вели на админский маршрут `admin.projects.show` (недоступный клиенту) — теперь ведут на `client.projects.show`.
v0.15.2
31.07.2026
PATCH
Символы шагов → SVG-иконки
Дизайн, управление проектами (Задание 51)
- Спецсимволы шагов задач заменены на SVG-иконки: на странице проекта в админке, на странице задачи (кнопки переключения), в личном кабинете клиента и в настройках безопасности.
- Невыполненный шаг — иконка «кружок» `<x-icon name="circle">`, выполненный — «галочка» `<x-icon name="check">`; иконки векторные, наследуют цвет (зелёный для выполненных, вторичный для невыполненных).
- Галочки верификации каналов связи (Email/Telegram/Телефон) на странице проекта заменены с `✓`/`✗` на `<x-icon name="check">` (зелёная) и `<x-icon name="x">` (красная); бейдж «Все каналы верифицированы 3/3» тоже использует иконку.
- Размеры иконок: шаги 16px (админ-страница проекта) и 14px (кнопки/карточки).
v0.15.1
31.07.2026
PATCH
Дизайн форм задач + русификация
Дизайн форм задач (Задание 50)
- Формы создания и редактирования задачи развёрнуты на всю ширину экрана (убран лимит `max-width: 640px`): компактные поля (название, статус, источник, приоритет, срок, получена) — сеткой в 3 колонки; «Что нужно от нас» / «Что нужно от клиента» — в 2 колонки; описание и первая запись — во всю ширину. Правка create автоматически подтянулась и в edit.
- Приоритет теперь выбирается списком 0–10 («Без приоритета» … «10») вместо нативного числового поля `type="number"` (старое «маленькое окошко» высотой ~10px).
- Иконка календаря у полей даты в тёмной теме стала белой — нативным полям добавлен `color-scheme: dark` (в светлой теме — `color-scheme: light`, иконка остаётся тёмной).
- Кнопка «Выберите файл» переделана в современный стиль: пунктирная кнопка с иконкой загрузки, рядом отображается имя выбранного файла (JS). Тот же стиль применён к загрузке файла на странице задачи. Мультизагрузка не менялась — работает как раньше.
- Дата добавления при редактировании: в шапке формы редактирования задачи показываются «Добавлена: дд.мм.гггг чч:мм · Обновлена: дд.мм.гггг чч:мм» (по-русски).
- Общие улучшения полей: высота 42px, подсветка при фокусе (`box-shadow`), акцентные чекбоксы (`accent-color`), выпадающие списки с кастомной стрелкой, адаптивность (на узких экранах колонки схлопываются в одну).
Русификация интерфейса
- Аксессоры статусов: `status_label` у `Task` (Запланирована/Активна/В работе/На паузе/Выполнена) и `Project` (Активен/Завершён/На паузе), `role_label` у `Client` (Администратор/Клиент/Тестовый).
- Сырые английские статусы заменены на русские подписи по всему сайту: админ-дашборд, страницы и списки проектов, страницы пользователей (проекты владельца и по связям), гостевая страница проекта `/p/{token}`, личный кабинет клиента (список и детали проекта), страница задачи.
- Роли пользователей в админке («актив»/`active` → роли): Администратор/Клиент/Тестовый.
- Мелочи: бренд «IPD Platform» → «IPD» в шапке, подвале и `<title>` гостевого макета; «device:» → «устройство:» на странице безопасности; подпись «Email» → «Электронная почта» на странице входа.
v0.15.0
31.07.2026
PATCH
История проектов + Задачи, добавленные вручную
Задачи, добавленные вручную (Задание 49)
- Миграция `2026_07_31_100001_add_task_sources_and_messages.php`:
- Модели: `TaskMessage`, `TaskAttachment`, а также связи и аксессоры в `Task` (`messages`, `visibleMessages`, `attachments`, `visibleAttachments`, `is_manual`, `source_label`), `visibleTasks` в `Project`.
- Администрирование (`AdminTaskController`):
- Синхронизация (`SyncManager`): задачи из платформы создаются/обновляются только при `source=synced`; ручные задачи защищены от перезаписи (пропускаются с `skipped`). Импортированным задачам проставляется `source=synced` и `sync_origin`. Исправлена запись несуществующего поля `progress`.
- Клиентские представления:
- Кнопки «Копировать» ссылок проекта: устойчивый fallback для `http://my.ipd.test` (без secure context) через `execCommand`; при неудаче — окно-подсказка с готовой ссылкой для ручного копирования.
- Срок действия ссылок: истёкшие токены явно помечаются (красный бейдж «Истекла» с датой) в админке и личном кабинете; в кабинете добавлена кнопка «Удалить» для истёкших/отозванных ссылок (`DELETE dashboard/projects/{id}/token/{tokenId}`).
- Создание задачи сразу с первой записью и файлом: в форме задачи — текст записи (звонок/встреча/заметка/письмо/чат), видимость записи, файл (до 20 МБ) и его видимость.
- Синхронизация: задачи без совпавшего локального проекта больше не создаются «висящими» без проекта (пропускаются с `skipped` — данные не теряются); в `sync_origin` записывается версия платформы (`Платформа · версия X`) и обновляется при каждой синхронизации.
- Миграция `2026_07_31_110000_add_task_work_fields.php`: у задач новые поля `need_from_us` («что нужно от нас»), `need_from_client` («что нужно от клиента») и флаг `protected_from_sync` (защита от перезаписи импортом). Защищённые задачи пропускаются синхронизацией (`skipped`), как и ручные.
- Клиентские страницы: задача показывает блоки «Нам нужно» / «Нужно от вас»; источник показывается нейтрально (Звонок/Встреча/Заказ/От клиента) без внутреннего ярлыка «Из платформы».
Добавлено
- `app/Services/ActivityLogger.php` единый сервис записи активности (`client_id`, `project_id`, `action`, `details`, `ip`, `user_agent`) с защитой от ошибок журналирования.
- Логирование действий администратора:
- Страница истории проекта маршрут `GET /admin/projects/{id}/history`, метод `AdminProjectController@history`, вьюха `resources/views/admin/projects/history.blade.php` (пагинация по 50 записей, колонки: дата, пользователь, действие, детали, IP).
- Секция «Последние изменения» на странице проекта — последние 10 записей активности со ссылкой «Вся история →».
Исправлено
- `app/Services/Sync/SyncManager.php` — `logSync` писал в несуществующие колонки (`entity_type`, `entity_id`, `entity_name`, `old_value`, `new_value`), которые отбрасывались массовым назначением. Теперь пишет в `project_id` (null) и `details` (JSON со статистикой и ошибкой).
- `importTasks` не писал несуществующую колонку `progress` (массовое назначение падало при синхронизации задач); поле удалено из `config/sync.php` (`entity_types.tasks.fields`).
v0.14.3
29.07.2026
MINOR
Страница проектов: сортировка, источник, настройка полей; Автоматическая синхронизация по расписанию
Добавлено
- Сортировка по колонкам клик по заголовку таблицы (ID, Название, Клиент, Статус, Прогресс, Задач, Источник, Создан) сортирует по возрастанию/убыванию. Активная сортировка отмечена стрелкой ▲/▼.
- Колонка «Источник» показывает, откуда проект: «Локальный» (создан вручную) или «Синхронизирован» (импортирован с другой площадки). Цветовые метки: зелёный / фиолетовый.
- Кнопка «Настройка полей» выпадающий список с чекбоксами для скрытия/показа колонок. Выбор сохраняется в localStorage между сессиями.
- Переданы `sort_by`, `sort_dir`, `per_page` через `appends()` в пагинацию — параметры сохраняются при перелистывании страниц.
- `withCount('tasks')` — оптимизация для колонки «Задач» (без загрузки всей коллекции).
Изменено
- `AdminProjectController@index` — поддержка `sort_by` (id, name, client, status, progress_pct, tasks_count, source, created_at) и `sort_dir` (asc/desc). Сортировка по клиенту через подзапрос `ORDER BY (SELECT name FROM clients WHERE ...)`.
- `resources/views/admin/projects/index.blade.php` — полная переработка: динамические заголовки, чекбоксы колонок, скрипт localStorage, иконка глаза в кнопке.
Добавлено
- `app/Console/Commands/SyncRun.php` — Artisan command `sync:run`
- `routes/console.php` — `Schedule::command('sync:run')->hourly()->withoutOverlapping()`
- `config/logging.php` — канал `sync` (daily, 30 дней хранения)
v0.14.2
29.07.2026
PATCH
Пагинация: русский язык, SVG-стрелки, выбор количества
Изменено
- `resources/views/vendor/pagination/bootstrap-5.blade.php` — полная переработка: русский текст, SVG-иконки (`<x-icon name="arrow-right">`) вместо HTML-сущностей, компактный размер (32px), hover-эффекты, селектор количества записей на страницу
- `app/Providers/AppServiceProvider.php` — зарегистрирован `Paginator::defaultView()` для использования кастомного шаблона
- `app/Http/Controllers/Admin/AdminProjectController.php` — `paginate(20)` → `paginate(min($perPage, 100))` с поддержкой `?per_page=` из URL
- `lang/ru/pagination.php` — создан файл русских переводов для пагинации
- `resources/views/admin/projects/index.blade.php` — удалён старый CSS пагинации (стили теперь в шаблоне)
Исправлено
- `config/sync.php` не подгружался из-за кэша конфигурации — `php artisan config:cache`
- `resources/views/admin/sync/index.blade.php` — `route("admin.sync.preview", "")` падал с `UrlGenerationException` из-за пустого параметра `{platform}`. Заменено на `url("/admin/sync/preview")`
- `app/Services/Sync/PlatformRegistry.php` — HTTP timeout для discovery уменьшен с 10s до 3s, чтобы страница не зависала при недоступных площадках
v0.13.4
29.07.2026
MINOR
SVG-иконки, типографика, контрастность
Added
- Компонент `<x-icon>` (`resources/views/components/icon.blade.php`) — 14 SVG-иконок: eye, lock, link, arrow-right, kebab, refresh, trash, x, check, clipboard, circle, check-circle, mail, edit, users, settings
- CSS-переменная `--text-secondary` (#cbd5e1 dark / #334155 light) для читаемого вторичного текста
- CSS-переменные размеров `--fs-lg`, `--fs-xl`, `--fs-2xl`
Changed
- Все HTML-emoji (👁, 🔒, 🔗, ⋮, ↻, 🗑, ✕, →) заменены на `<x-icon>`
- Все Unicode-emoji (✓, ✗, 🟢, 🟡, ⚪, 🔗, 📋, ✅, ○, ⚠, 📅) заменены на `<x-icon>`
- Размер шрифта 11px → 12px во всех badge, label, hint, meta, footer
- Размер шрифта 10px → 11px в таблицах (даты, метки)
- Размер шрифта 9px → 11px в sidebar role-badge
- Цвет текста `#64748b` и `#94a3b8` → `var(--text-secondary)` и `var(--muted)`
- `th` заголовки цвет с `var(--muted)` на `var(--text-secondary)` для контраста
- Ссылки с dashboard все ведут на `/admin/projects/{id}`, старый `/dashboard/projects/{id}` редиректит админа
- Версия bump 0.13.3 → 0.13.4
Files Changed
- `resources/views/components/icon.blade.php` — новый компонент
- `resources/views/layouts/app.blade.php` — CSS-переменные, 9px/10px → 11px
- `resources/views/dashboard/index.blade.php` — SVG + font-size 12px
- `resources/views/admin/index.blade.php` — SVG + font-size 12px
- `resources/views/admin/projects/show.blade.php` — SVG + font-size 12px
- `resources/views/admin/projects/index.blade.php` — SVG + font-size 12px
- `resources/views/admin/tokens/index.blade.php` — SVG + font-size 12px
- `resources/views/dashboard/projects/show.blade.php` — font-size 12px
- `resources/views/dashboard/projects/index.blade.php` — SVG
- `resources/views/guest/project.blade.php` — SVG
- `resources/views/about.blade.php` — SVG
- `resources/views/settings/security.blade.php` — SVG
- `resources/views/admin/users/security.blade.php` — SVG
- `config/app.php` — version 0.13.4
- `.env` — version 0.13.4
v0.13.3
29.07.2026
PATCH
Dashboard: kebab-меню, пользователи, токены на карточках проектов
Added
- Карточки проектов на dashboard переработаны: показывают владельца/направление, прогресс-бар, количество задач
- Блок пользователей на каждой карточке — список привязанных пользователей (до 3, остальные — «+N ещё»), статус email (✓/✗), дата последнего входа
- Блок токенов статус: «Токен активен» (с датой истечения), «Токен истёк/отозван», «Токен не создан»
- Kebab-меню на каждой карточке: Просмотр проекта, Админка проекта, Сгенерировать токен
- Кликабельность вся карточка ведёт на страницу проекта
- API-проекты показаны как fallback, если нет локальных проектов
Changed
- Версия платформы: 0.13.2 → 0.13.3
- `resources/views/dashboard/index.blade.php` — полная переработка: kebab-меню, пользователи, токены, кликабельность
- `app/Http/Controllers/Dashboard/DashboardController.php` — для админа: все локальные проекты с clients+tokens; для клиента: привязанные проекты
v0.13.2
29.07.2026
PATCH
Админ-панель: проекты на dashboard, пароль в открытом виде, улучшение UX привязки проектов
Added
- Админ dashboard блок «Проекты (последние 10)» с карточками: название (кликабельное), владелец, статус, направление, прогресс-бар, дата создания
- Kebab-меню на каждой карточке проекта: Просмотр, Редактировать, Токены
- Пароль в открытом виде поля пароля на формах создания и редактирования пользователя теперь `type="text"` (видимый) с кнопкой «Скрыть/Показать»
- Информативные чекбоксы проектов при привязке проектов к пользователю теперь отображается: владелец, статус (бейдж), направление; подсветка выбранных проектов
- Клиент dashboard проект стал кликабельным (ссылка на страницу проекта), если привязан к локальному проекту
Changed
- Версия платформы: 0.13.1 → 0.13.2
- `resources/views/admin/users/edit.blade.php` — projects checklist: +владелец, +статус, +направление; password: type=text + toggle
- `resources/views/admin/users/create.blade.php` — +поле пароля (type=text), projects checklist: +владелец, +статус, +направление
- `resources/views/admin/index.blade.php` — +блок проектов с карточками и kebab-меню
- `resources/views/dashboard/index.blade.php` — проект стал кликабельным (если есть локальный)
- `app/Http/Controllers/Admin/AdminController.php` — +проекты в view data
- `app/Http/Controllers/Dashboard/DashboardController.php` — +externalIdMap для ссылок на локальные проекты
v0.13.1
29.07.2026
PATCH
Исправление: токен проекта показывал email администратора вместо клиента
Fixed
- Токен проекта 40 («Бобер Боб») `client_id` изменён с 1 (admin) на 5 (Егор). Теперь гостевая страница показывает email egor@jetpeel.club
- Приветствие на гостевой странице убрано слово «Администратор», теперь просто «Егор приглашает вас ознакомиться с проектом»
Changed
- Версия платформы: 0.13.0 → 0.13.1
- `resources/views/guest/project.blade.php` — welcome-card: убрано фиксированное «Администратор», теперь динамически подставляет имя владельца проекта
v0.13.0
29.07.2026
PATCH
Гостевая страница проекта: редизайн, email-верификация, темы
Added
- Гостевой layout `layouts/guest.blade.php`: CSS-переменные (dark/light), переключатель темы (localStorage), мобильное меню (hamburger), навигация (Проект, О платформе, Войти), footer с версией
- Email-верификация на гостевой странице: сервер генерирует короткий код и отправляет его на email клиента (через SMTP); код подтверждается на странице через отдельные маршруты отправки и проверки.
- EmailVerificationController sendCode (генерация + сохранение + логгирование кода), verifyCode (проверка + установка email_verified_at + очистка кода)
- Миграция 100006 колонки `email_verification_code` (varchar 10) и `email_verification_code_sent_at` (timestamp) в таблицу clients
- Дизайн токен-страницы welcome-card с приветствием от администратора, email-секция с индикацией статуса, прогресс-бар проекта, задачи с дедлайнами и процентами, индикация просроченных задач, действия после верификации (установить пароль / войти)
Changed
- Версия платформы: 0.12.0 → 0.13.0
- `resources/views/guest/project.blade.php` — полный редизайн: использует layouts.guest, улучшенная типографика, email-верификация, прогресс-карточка, таски с датами/дедлайнами, mobile-first responsive
- `app/Http/Controllers/Guest/ProjectTokenController.php` — передаёт emailVerified, hasPassword, project.direction, tokenModel для отображения
- `app/Models/Client.php` — fillable: +email_verification_code, email_verification_code_sent_at; casts: +email_verification_code_sent_at
- `routes/web.php` — +2 маршрута: send-code, verify-code
Files
- `database/migrations/2026_07_29_100006_add_email_verification_to_clients_table.php` — новый
- `resources/views/layouts/guest.blade.php` — новый гостевой layout
- `app/Http/Controllers/Guest/EmailVerificationController.php` — новый контроллер верификации
- `resources/views/guest/project.blade.php` — переработан
- `app/Http/Controllers/Guest/ProjectTokenController.php` — обновлён
- `routes/web.php` — +2 маршрута
v0.12.0
29.07.2026
MINOR
Passwordless registration, интеграции и UX
Added
- Passwordless registration: пароль стал необязательным при создании; при его отсутствии формируется защищённая ссылка-приглашение, по которой пользователь сам задаст пароль. Форма создания пользователя: поле пароля заменено подсказкой «Оставьте пустым — пользователь сам задаст пароль»
- SetPasswordController GET /set-password/{token} + POST /set-password/{token}: показывается форма, где пользователь вводит пароль → после установки автоматический вход в dashboard. Сообщение: «Администратор уже зарегистрировал вас на платформе IPD»
- Проверка passwordless в login если password = null, выводится ошибка «Вы ещё не установили пароль. Используйте ссылку-приглашение»
- Колонка «Пользователи» на /admin/projects AdminProjectController index: withCount('clients'). Таблица: новая колонка «Пользователи» с счётчиком и склонением (пользователь/пользователя/пользователей)
- Индикатор каналов на странице проекта новая колонка «Каналы»: красный бейдж «0 каналов» (нет верифицированных), жёлтый «N/3» (частично), зелёный «✓ 3/3» (все)
- Кнопка «Отправить подтверждение» email POST /admin/users/{id}/send-email-verification: устанавливает email_verified_at, метод-заглушка для будущей отправки письма
- Фундамент настройки Telegram-бота в разделе «Интеграции»: параметры подключения бота и привязка чата к клиенту с кодом верификации.
- Фундамент SMS Gateway в разделе «Интеграции»: параметры подключения шлюза, имя отправителя и код верификации телефона клиента с меткой времени.
- Модель Client дополнена полями верификации (телефон, Telegram) и флагом установки пароля, добавлен вспомогательный метод.
Changed
- Версия платформы: 0.11.0 → 0.12.0
- `app/Http/Controllers/Admin/AdminUserController.php` — store: password nullable, при отсутствии генерируется verification_token, email_verified_at не ставится. Добавлен sendEmailVerification()
- `app/Http/Controllers/Admin/AdminProjectController.php` — index: withCount('clients')
- `app/Http/Controllers/Admin/SettingsController.php` — добавлен таб «Интеграции» с настройками подключения Telegram и SMS.
- `app/Http/Controllers/Auth/ClientAuthController.php` — login: проверка что password не null, иначе сообщение о ссылке-приглашении
- `resources/views/admin/users/create.blade.php` — поле пароля заменено на подсказку
- `resources/views/admin/projects/index.blade.php` — колонка «Пользователи» с clients_count
- `resources/views/admin/projects/show.blade.php` — колонка «Каналы» с индикатором верификации
- `resources/views/admin/users/show.blade.php` — кнопка «Отправить» для email
- `resources/views/admin/settings.blade.php` — новый таб «Интеграции» с полями Telegram и SMS
- `routes/web.php` — 3 новых маршрута: set-password (GET+POST), send-email-verification
- `config/app.php` — APP_VERSION: 0.11.0 → 0.12.0
Files
- `database/migrations/2026_07_29_100004_add_passwordless_fields_to_clients_table.php` — password nullable, verification_token, password_set_at
- `database/migrations/2026_07_29_100005_add_verification_codes_to_clients_table.php` — поля кодов верификации (Telegram, телефон).
- `app/Http/Controllers/Auth/SetPasswordController.php` — установка пароля по токену
- `resources/views/auth/set-password.blade.php` — форма установки пароля
v0.11.0
29.07.2026
MINOR
Связывание проектов и пользователей; Связывание проектов и пользователей, каналы связи
Added
- Привязка пользователей к проекту в create/edit проекта добавлен чеклист `clients[]` с синхронизацией через sync в store/update
- AdminProjectController attachClient, detachClient с логированием в activity_log
- Каналы связи миграция 100003: колонки telegram, telegram_verified_at, phone_verified_at в clients
- Каналы связи UI на /admin/users/{id}/show — таблица «Каналы связи» с индикацией и кнопками; на /admin/users/{id}/edit — поле Telegram + кнопки подтверждения
- Индикация на странице проекта в таблице пользователей: зелёный ✓ / красный ✗ для каждого канала
- Client модель fillable: telegram, telegram_verified_at, phone_verified_at; casts: datetime для verified_at. Helpers: isEmailVerified, isTelegramVerified, isPhoneVerified, verifiedChannelsCount
- Routes verify-email, verify-telegram, verify-phone, attach-client, detach-client
Changed
- Версия платформы: 0.10.1 → 0.11.0
- AdminProjectController.show/edit — загрузка clients; create/edit views — чеклист
- AdminUserController.show — секция «Каналы связи»; edit — поле Telegram + кнопки верификации
- routes/web.php — 5 новых маршрутов
- config/app.php — APP_VERSION: 0.10.1 → 0.11.0
Added
- Управление пользователями проекта на странице `/admin/projects/{id}` новая секция «Пользователи» с таблицей (имя, email, Telegram, телефон, роль, статус верификации), кнопкой «+ Пользователь» (выбор из списка) и «Удалить» для каждого
- Выбор пользователей при создании/редактировании проекта чеклист пользователей в формах create/edit проекта (соответствует аналогичному чеклисту проектов у пользователя)
- AdminProjectController: attachClient/detachClient два новых метода для добавления/удаления пользователей проекта, с логированием в activity_log
- Каналы связи (Client) колонки `telegram`, `telegram_verified_at`, `phone_verified_at` в таблице `clients`
- Методы Client model `isEmailVerified()`, `isTelegramVerified()`, `isPhoneVerified()`, `verifiedChannelsCount()`
- Каналы связи на странице пользователя `/admin/users/{id}`: новая секция «Каналы связи» с таблицей (Email, Telegram, телефон) и кнопками «Подтвердить»/«Снять» для каждого канала
- Редактирование каналов связи в `/admin/users/{id}/edit` добавлено поле Telegram и кнопки подтверждения каналов
- AdminUserController: verifyEmail/verifyTelegram/verifyPhone три новых метода для переключения статуса верификации
- Routes 5 новых: verify-email, verify-telegram, verify-phone, attach-client, detach-client
Changed
- Версия платформы: 0.10.1 → 0.11.0
- `app/Http/Controllers/Admin/AdminProjectController.php` — store/update теперь синхронизируют `clients` pivot; добавлены attachClient/detachClient
- `app/Http/Controllers/Admin/AdminUserController.php` — store/update принимают telegram; добавлены verifyEmail/verifyTelegram/verifyPhone
- `app/Models/Client.php` — добавлены `telegram`, `telegram_verified_at`, `phone_verified_at` в fillable + casts; добавлены 4 helper-метода для верификации
- `resources/views/admin/projects/show.blade.php` — новая секция «Пользователи» с таблицей и кнопкой «+ Пользователь»
- `resources/views/admin/projects/edit.blade.php` — чеклист пользователей с выбором доступа
- `resources/views/admin/projects/create.blade.php` — чеклист пользователей при создании
- `resources/views/admin/users/show.blade.php` — новая секция «Каналы связи» с таблицей
- `resources/views/admin/users/edit.blade.php` — поле Telegram + кнопки подтверждения каналов
- `resources/views/admin/users/create.blade.php` — поле Telegram
- `routes/web.php` — 5 новых маршрутов
Files
- `database/migrations/2026_07_29_100003_add_communication_channels_to_clients_table.php` — новый
v0.10.1
29.07.2026
MINOR
Исправление выхода; Исправление: кнопка «Выйти» и тестирование мультиаккаунтов
Fixed
- Механизм выхода полностью очищает состояние сессии на устройстве, гарантируя, что пользователь не будет автоматически восстановлен при повторном входе
- guest:client middleware убран с /login — login page показывает блок «уже вошёл» для аутентифицированных пользователей
- Миграция 100002 исправлен duplicate auto_login в up()
Fixed
- Механизм выхода очищает состояние «запомненного устройства», предотвращая автоматическое восстановление сессии после явного выхода
- Миграция 100001 удалено дублирование колонки `auto_login` (колонка добавляется только в миграции 100002)
Changed
- Версия платформы: 0.10.0 → 0.10.1
- `app/Http/Controllers/Auth/ClientAuthController.php` — `logout()` удаляет known_account перед выходом
- `database/migrations/2026_07_29_100001_create_client_auth_known_accounts_table.php` — убран лишний `Schema::table('clients', ...)` с добавлением `auto_login`
- `app/Models/Client.php` — добавлен `knownAccounts()` HasMany relation для удобства
v0.10.0
29.07.2026
PATCH
Мультиаккаунты («Вход как в Google»)
Added
- Мультиаккаунты (Google-style) login.blade.php переработан: при входе отображаются известные аккаунты (аватар, имя, email) → клик для быстрого переключения → «+ Другой аккаунт» → форма email/пароль. «× Выйти из всех аккаунтов» на странице логина и в /settings/security
- Таблица `client_auth_known_accounts` хранит «запомненные» устройства (привязку аккаунта к устройству) с уникальными индексами для корректного переключения между аккаунтами
- Реализован флаг «Запомнить устройство» на форме входа: при включении устройство считается доверенным, и сессия сохраняется между визитами.
- Устройству клиента присваивается анонимный долговременный идентификатор, который связывает «запомненные» аккаунты на этом устройстве
- ClientAuthController методы getDeviceId(), saveKnownAccount(), switchAccount(), forgetAccount(), logoutAll(), knownAccounts() (API), securitySettings(), toggleAutoLogin()
- Реализован безопасный механизм «запомненного устройства»: для доверенных устройств пользователя сессия восстанавливается автоматически, повторный ввод пароля не требуется. Middleware зарегистрирован в глобальной цепочке обработки запросов.
- Dropdown профиля — «Сменить пользователя» в app.blade.php profile dropdown секция с загруженными через /api/auth/known-accounts аккаунтами + кнопка «×» забыть аккаунт для каждого
- Админ-страница безопасности `/admin/users/{id}/security/`: управление «запоминанием устройства», принудительное завершение сессий, просмотр и очистка известных устройств
- Страница `/settings/security`: карточка учётной записи (имя, email, роль, последний вход), переключатель «запомнить устройство», список известных устройств с кнопкой удаления, кнопка «Выйти из всех аккаунтов»
- Routes POST /login/switch/{id}, POST /login/forget/{id}, POST /login/logout-all, GET /settings/security, POST /settings/security/toggle-auto-login, GET /api/auth/known-accounts, GET+POST /admin/users/{id}/security
Changed
- Версия платформы: 0.9.0 → 0.10.0
- `app/Http/Controllers/Auth/ClientAuthController.php` — полный рефакторинг: добавлены мультиаккаунт-методы и управление запоминанием устройств
- `resources/views/auth/login.blade.php` — переработан: два состояния (список аккаунтов / форма входа), анимация переключения, кнопка «Выйти из всех»
- `resources/views/layouts/app.blade.php` — добавлена секция «Сменить пользователя» в user dropdown + JavaScript fetch known-accounts + forget buttons
- `routes/web.php` — добавлены 8 новых маршрутов для мультиаккаунтов, безопасности и админ-безопасности
- `resources/views/admin/users/edit.blade.php` — добавлена кнопка «Безопасность» в actions
- `config/app.php` — `APP_VERSION` по умолчанию: `0.9.0` → `0.10.0`
- `.env` — `APP_VERSION=0.9.0` → `0.10.0`
- `bootstrap/app.php` — зарегистрирован AutoLogin middleware в web-группе
Files
- `database/migrations/2026_07_29_100001_create_client_auth_known_accounts_table.php` — создание таблицы known_accounts
- `database/migrations/2026_07_29_100002_add_auto_login_to_clients_table.php` — добавление колонки auto_login
- `app/Models/ClientAuthKnownAccount.php` — модель для known_accounts
- `app/Http/Middleware/AutoLogin.php` — middleware автоматического входа
- `app/Http/Controllers/Auth/ClientAuthController.php` — рефакторинг с мультиаккаунтами
- `app/Http/Controllers/Admin/AdminUserController.php` — добавлены editSecurity/updateSecurity
- `resources/views/auth/login.blade.php` — переработан с account chooser
- `resources/views/settings/security.blade.php` — новая страница безопасности
- `resources/views/admin/users/security.blade.php` — новая админ-страница безопасности
- `resources/views/layouts/app.blade.php` — секция смены пользователя + forget buttons
- `resources/views/admin/users/edit.blade.php` — кнопка «Безопасность»
- `routes/web.php` — новые маршруты
- `bootstrap/app.php` — регистрация AutoLogin middleware
v0.9.0
28.07.2026
MINOR
Верхнее правое меню: уведомления, профиль, kebab-меню, тема
Added
- Колокольчик уведомлений в topbar-right: SVG колокольчик, красный badge с количеством непрочитанных, dropdown при клике (список уведомлений, «Пометить все прочитанными», ссылка «Все → /notifications»)
- Система уведомлений таблица `client_notifications` (type, title, description, url, read_at), модель `ClientNotification`, контроллер `NotificationController` (index, apiList, markRead, markAllRead), 3 API-эндпоинта (GET /api/notifications, POST /api/notifications/read, POST /api/notifications/read-all)
- Страница уведомлений `/notifications`: список карточек с иконками по типу (warning/error/success/info), unread-индикатор, «Пометить все прочитанными», пустое состояние
- Dropdown профиля аватар с именем (pill-кнопка), при клике: header (аватар-xl, имя, email), «Настройки», переключатель «Тёмная тема» (switch), «Выйти»
- Kebab-меню (⋮) трёхточечная кнопка рядом с уведомлениями: «Иструкция» → /about, «Поддержка» → mailto:support@ipd.by, «Выйти»
- Переключатель темы в dropdown профиля: switch с label «Тёмная тема», сохраняет выбор в localStorage, применяет CSS-переменные [data-theme="light|dark"] на <html>, работает на всех страницах (layout)
- Light theme CSS-переменные `[data-theme="light"]` overrides: bg #f1f5f9, card-bg #fff, border #e2e8f0, text #0f172a, muted #475569, accent #2563eb, green #16a34a, red #dc2626
Changed
- Версия платформы: 0.8.0 → 0.9.0
- `resources/views/layouts/app.blade.php` — полная переработка topbar-right: роль-тейг и кнопка «Выти» заменены на notifications bell + kebab menu + user dropdown. Добавлены CSS для: .notif-wrap, .notif-bell, .notif-badge, .notif-dropdown, .notif-item, .user-dd, .user-dd__trigger, .user-dd__dropdown, .kebab-wrap, .kebab-btn, .kebab-dropdown. Добавлены JS: toggle dropdowns, loadNotifications (fetch /api/notifications), markNotifReadDD, markAllNotifRead, updateNotifBadge, toggleLayoutTheme
- `routes/web.php` — добавлены 4 роута: GET /notifications, GET /api/notifications, POST /api/notifications/read, POST /api/notifications/read-all
- `config/app.php` — `APP_VERSION` по умолчанию: `0.8.0` → `0.9.0`
- `.env` — `APP_VERSION=0.8.0` → `0.9.0`
Files
- `database/migrations/2026_07_28_100001_create_client_notifications_table.php` — новый
- `app/Models/ClientNotification.php` — новый
- `app/Http/Controllers/Dashboard/NotificationController.php` — новый
- `resources/views/notifications.blade.php` — новый
- `resources/views/layouts/app.blade.php` — обновлён (topbar-right, CSS, JS, theme)
Migration
- `2026_07_28_100001_create_client_notifications_table.php` — таблица client_notifications
v0.8.0
25.07.2026
MINOR
Публичная страница «О платформе», переработка авторизации
Added
- Страница «О платформе» новая страница `/about` (публичная, без авторизации): gradient-шапка с логотипом, карточка «О платформе» (версия, среда, время, статус), описание возможностей, ссылки (вход, история версий), блок последних версий из changelog.md, кнопка «Войти в систему»
- Переработанная страница входа новый дизайн: inline SVG логотип, заголовок «IPD Platform», подзаголовок «Управление проектами», описание назначения, ссылка «Войти в систему» → /about, версия с ссылкой на /about
- Переключатель темы кнопка в правом верхнем углу (☀️/🌙) на страницах входа и /about, сохраняет выбор в localStorage, работает без JavaScript-фреймворков
- Route GET /about публичный маршрут без middleware авторизации, парсит changelog.md для отображения истории версий
Changed
- Версия платформы: 0.7.3 → 0.8.0
- `resources/views/auth/login.blade.php` — полная переработка дизайна: добавлены логотип SVG, описание, theme switcher, ссылка на /about
- `config/app.php` — `APP_VERSION` по умолчанию: `0.7.3` → `0.8.0`
- `.env` — `APP_VERSION=0.7.3` → `0.8.0`
Files
- `resources/views/about.blade.php` — новый файл: публичная страница «О платформе»
- `resources/views/auth/login.blade.php` — переработка дизайна
- `routes/web.php` — добавлен route `GET /about`
- `config/app.php` — обновлена версия
v0.7.3
25.07.2026
MINOR
Токены: UX-улучшения, доступность, часовой пояс
Added
- Dropdown срока токена вместо числового поля теперь выпадающий список: 24ч, 48ч (по умолчанию), 72ч, 1 неделя, 1 месяц
- Кнопка «Копировать» рядом с URL гостевой ссылки, копирует полный URL в буфер обмена с визуальной обратной связью (изменение текста и цвета на 2 сек)
- Индикатор токенов на странице проектов в таблице `/admin/projects` добавлена колонка «Токен»: зелёный бейдж с количеством активных токенов, жёлтый если токены неактивны, серое тире если нет токенов
Changed
- Версия платформы: 0.7.2 → 0.7.3
- Часовой пояс `config/app.php`: `UTC` → `Europe/Minsk` для корректного отображения дат/времени без ручной конвертации
- Контрастность текста CSS-переменная `--muted` изменена с `#64748b` (контраст ~2.7:1) на `#94a3b8` (контраст ~5.1:1, WCAG AA)
- Размер мелкого текста `--fs-xs` увеличен с 12px до 13px для лучшей читаемости
- `AdminProjectController@index` — добавлен eager-load `tokens` для индикатора токенов
- `admin/projects/show.blade.php` — обе формы создания токена используют dropdown вместо number input
v0.7.2
25.07.2026
PATCH
Токены: аналитика, логирование, центр управления
Added
- Аналитика токенов новая страница `/admin/tokens` с дашбордом: общая статистика (всего, активных, истёкших, отозванных, просмотров), конверсия по проектам (прогресс-бары), таблица всех токенов сгруппированная по проектам, история последних действий
- Токены в sidebar новый пункт «Токены» (иконка ключа) в боковом меню администратора между «Проекты (Админ)» и «Настройки»
- Логирование действий с токенами все операции (создание, отзыв, обновление, удаление) записываются в `activity_log` с указанием проекта, замаскированного токена, IP-адреса и user-agent
- AdminTokenController контроллер для страницы аналитики токенов
- Аналитика по проектам для каждого проекта отображается количество токенов, просмотров и процент активных токенов (конверсия)
Changed
- Версия платформы: 0.7.1 → 0.7.2
- `AdminProjectController` — методы `generateToken`, `revokeToken`, `regenerateToken`, `destroyToken` теперь записывают логи в `activity_log`
- `routes/web.php` — добавлен 1 роут: `GET /admin/tokens`
- Sidebar — добавлена ссылка «Токены» (`/admin/tokens`)
v0.7.1
25.07.2026
PATCH
Безопасность токенов: защищённые ссылки, трекинг просмотров
Added
- Безопасная генерация токенов: токены теперь создаются как криптостойкие случайные строки повышенной длины вместо простых числовых значений.
- Трекинг просмотров новые поля `views_count` и `last_used_at` в таблице `client_tokens` для отслеживания активности ссылок
- Управление токенами из админки на странице проекта (`/admin/projects/{id}`): создание, обновление (regenerate), отзыв, удаление токенов
- Форма создания токена метка, срок действия (часы), выбор клиента
- Маскировка токенов в таблице отображается маска `xxxx...xxxx` вместо полного токена
- Регенерация существующих токенов автоматическая миграция заменяет числовые/короткие токены на безопасные 64-символьные
- Статус токенов три состояния: Активен (зелёный), Истёк (жёлтый), Отозван (красный)
- Индикаторы статуса визуальные бейджи для каждого токена в таблице
Changed
- Версия платформы: 0.7.0 → 0.7.1
- `ClientToken` модель — добавлены поля `views_count`, `last_used_at` в fillable/casts; методы `isExpired()`, `regenerate()`, `incrementViews()`, `generateSecureToken()`, accessor `masked_token`
- `AdminProjectController` — добавлены методы: `generateToken()`, `revokeToken()`, `regenerateToken()`, `destroyToken()`
- `ProjectTokenController@guest` — теперь вызывает `incrementViews()` при каждом открытии гостевой страницы
- `routes/web.php` — добавлены 4 роута для управления токенами
Migration
- `2026_07_25_100001_add_security_fields_to_client_tokens_table.php` — добавление `views_count`, `last_used_at`
- `2026_07_25_100002_regenerate_insecure_tokens.php` — замена числовых/коротких токенов на безопасные
v0.7.0
24.07.2026
PATCH
Синхронизация данных: импорт из task.ipd.by
Added
- Направления новая таблица `directions` (external_id, name, description, status, priority, tasks_count, source) + модель `Direction`
- Синхронизация данных `SyncService` импортирует направления, проекты, задачи и шаги из внешнего API (`task.ipd.by/api/export/full`)
- Страница синхронизации `/admin/sync` — интерфейс запуска синхронизации с отображением статистики (направления, проекты, задачи, шаги) и статуса последнего запуска
- AdminSyncController `index()` для отображения страницы, `run()` для AJAX-запуска синхронизации
- Upsert логика при синхронизации существующие записи обновляются, новые создаются, удалённые из источника удаляются локально
- Привязка проектов к направлениям `direction_id` добавлен в таблицу `projects` (nullable FK)
- Метка источника поле `source` ('local'/'synced') в таблице `projects` для разделения локальных и импортированных проектов
Changed
- Версия платформы: 0.6.2 → 0.7.0
- `TaskApiService` — `baseUrl` по умолчанию изменён с `task.ipd.local` на `task.ipd.by`
- `Project` модель — добавлены `direction_id`, `source`, `is_active` в fillable; добавлена связь `direction()` BelongsTo
- `Task` модель — добавлен `external_id` в fillable
- Боковая панель — добавлены ссылки: «Синхронизация» и «Проекты (Админ)»
- `routes/web.php` — добавлены 2 роута: `GET /admin/sync`, `POST /admin/sync/run`
Migration
- `2026_07_24_100001_create_directions_table.php` — таблица направлений
- `2026_07_24_100002_add_direction_id_to_projects_table.php` — direction_id, source, is_active в projects
v0.6.2
24.07.2026
MINOR
Проекты: CRUD, задачи, шаги, ссылки для клиентов
Added
- CRUD проектов создание, редактирование, удаление проектов через админ-панель (`AdminProjectController`: create, store, edit, update, destroy)
- Форма создания проекта `admin/projects/create.blade.php`: название, описание, владелец (клиент), статус, external_id
- Форма редактирования проекта `admin/projects/edit.blade.php`: те же поля + Danger Zone (удаление)
- Локальные задачи новая таблица `tasks` (project_id, name, description, status, priority, due_date, sort_order) + модель `Task`
- CRUD задач в проектах `AdminTaskController`: create, store, show, edit, update, destroy (вложенные под проектом)
- Локальные шаги новая таблица `steps` (task_id, name, description, is_completed, sort_order) + модель `Step`
- Управление шагами `AdminStepController`: добавление, переключение (toggle), удаление шагов в задаче
- Прогресс задач автоматический расчёт `progress_pct` на основе выполненных шагов
- Прогресс проекта автоматический `syncProjectCounts` при изменении задач
- Ссылка для клиента на странице проекта отображается активная ссылка (`/p/{token}`) для клиента
- Обновлён guest/project.blade.php теперь показывает локальные задачи + шаги (а не только внешний API)
- Admin tasks CRUD views create, edit, show (с чекбоксами шагов и формой добавления)
Changed
- Версия платформы: 0.6.1 → 0.6.2
- `AdminProjectController` — расширен с 2 методов (index, show) до 7 (full CRUD)
- `Project` модель — добавлена связь `tasks()` HasMany
- `routes/web.php` — добавлены CRUD роуты для projects, tasks, steps (16 роутов total)
- `.env`: `APP_VERSION=0.6.1` → `0.6.2`
v0.6.1
22.07.2026
PATCH
Пользователи: CRUD — создание, редактирование, удаление, привязка проектов
Added
- Создание пользователя `GET /admin/users/create` + `POST /admin/users` — форма создания с полями: имя, email, пароль, роль, компания, телефон, статус, выбор проектов
- Редактирование пользователя `GET /admin/users/{id}/edit` + `PUT /admin/users/{id}` — форма редактирования с теми же полями; пароль опциональный (оставить пустым = не менять)
- Удаление пользователя `DELETE /admin/users/{id}` — удаление с подтверждением (confirm-dialog), cascade: detach проектов, удаление токенов, настроек, логов
- Кнопки действий в таблице «Просмотр», «Редактировать», «Удалить» для каждого пользователя в списке
- Кнопка «+ Добавить» в шапке страницы пользователей
- Проекты (доступ) — чеклист при создании/редактировании пользователя отображается список проектов с чекбоксами для привязки через pivot `client_project`
- Danger Zone блок удаления внизу формы редактирования
Changed
- Версия платформы: 0.6.0 → 0.6.1
- `AdminUserController` — добавлены методы: `create()`, `store()`, `edit()`, `update()`, `destroy()`
- `routes/web.php` — добавлены CRUD роуты для users
- `.env`: `APP_VERSION=0.6.0` → `0.6.1`
v0.6.0
21.07.2026
PATCH
Пользователи: управление, связь с проектами, last login
Added
- Admin-панель «Пользователи» новый контроллер `AdminUserController` с index (список) и show (детально)
- Просмотр пользователей страница `/admin/users` с таблицей: имя, email, роль, статус, количество проектов, последний вход, дата создания
- Детальная страница пользователя `/admin/users/{id}` с профилем, проектами (владеет и имеет доступ), логами активности
- Связь пользователей и проектов (Many-to-Many) новая таблица `client_project` с unique-ограничением и timestamps
- `Client->ownedProjects()` — HasMany проектов, где пользователь — владелец (через `client_id`)
- `Client->projects()` — BelongsToMany проектов через pivot `client_project`
- `Project->clients()` — BelongsToMany клиентов через pivot `client_project`
- Отслеживание последнего входа поле `last_login_at` в таблице `clients`, обновляется при авторизации
- Sidebar: активная ссылка «Пользователи» обновлена с `#` на `route('admin.users.index')` с active-классом
- User counts на админ-дашборде теперь отображается количество пользователей
Changed
- Версия платформы: 0.5.1 → 0.6.0
- `ClientAuthController@login` — теперь записывает `last_login_at` при входе
- `Client` модель — добавлен `BelongsToMany` для связи с проектами
- `Project` модель — добавлен `BelongsToMany` для связи с клиентами
- `.env`: `APP_VERSION=0.5.1` → `0.6.0`
v0.5.0
21.07.2026
MINOR
Управление проектами: локальные проекты, шаряемые ссылки, логирование
Added
- Локальное управление проектами новая таблица `projects` с полями: name, description, client_id, status, external_id, progress_pct, tasks_total, tasks_completed
- Страница «Мои проекты» `/dashboard/projects` с карточками проектов, прогресс-барами, статусами, количеством задач
- Детальная страница проекта `/dashboard/projects/{id}` с задачами из внешней системы, прогрессом, шаряемыми ссылками
- Шаряемые ссылки на проекты `/p/{token}` — новый гостевой роут для временного доступа к проекту; токен с истечением (настраиваемые часы), меткой и возможностью отзыва
- Генерация проектных токенов форма на детальной странице проекта: количество часов, метка, кнопка «Сгенерировать» и «Отозвать»
- Система логирования таблица `activity_log` с полями: client_id, project_id, action, details, ip_address, user_agent
- Логирование входа запись `login` в activity_log при авторизации клиента
- Логирование просмотров проектов запись `view_project` при открытии детальной страницы проекта
- Гостевая страница проекта `/p/{token}` с информацией о проекте, задачами, тарифом; с призывом авторизоваться
- Связь токенов с проектами поле `project_id` в `client_tokens`, модель ClientToken теперь belongsTo Project
- Sidebar: Мои проекты исправлена ссылка, ведёт на `/dashboard/projects` с active-классом
- Seed-данные: 5 демо-проектов для каждого не-администраторского клиента и проектный токен для демонстрационного аккаунта.
Changed
- Версия платформы: 0.4.1 → 0.5.0
- Client модель: добавлены методы `projects()`, `logs()`, `log()` (статический хелпер для записи activity)
- ClientToken модель: добавлена связь `project()`, поле `project_id` в fillable
- DatabaseSeeder: расширен — создаёт проекты и проектные токены
v0.4.1
21.07.2026
MINOR
Админ-панель: /admin/ индекс, шаряемые URL-табы настроек
Added
- Главная страница админ-панели `/admin/` новая точка входа для администраторов с карточками (настройки, пользователи, окружение) и быстрыми ссылками
- Route `admin.dashboard` `AdminController@index` с приветствием, ролью, версией
Changed
- Табы настроек теперь шаряемые URL вместо `<button onclick="switchTab(...)">` используются `<a href="{{ route('admin.settings', ['tab' => '...']) }}">` — каждый таб имеет собственный URL (`/admin/settings/general`, `/admin/settings/security`, `/admin/settings/versions`), который можно открыть по прямой ссылке или закладке
- Удалена JS-функция `switchTab()` переключение вкладок теперь происходит через нативные ссылки (GET-запросы с параметром tab)
- Боковое меню админа добавлена ссылка «Админ-панель» (`/admin/`) над «Настройки»
- Версия платформы: 0.4.0 → 0.4.1
v0.4.0
21.07.2026
PATCH
Ролевая система: админ, клиент, тестовый доступ + боковое меню
Added
- Ролевая система (admin / client / test): новая колонка `role` в таблице `clients`, константы и методы проверки в модели `Client`
- Middleware `role`: фильтр доступа к роутам по роли — `/admin/*` только для `admin`
- Для проверки ролевой модели добавлены отдельные учётные записи с ролями «администратор», «клиент» и «тестовая». Логины и пароли хранятся только в локальном окружении и не публикуются.
- Постоянное боковое меню: единый layout `layouts.app` с фиксированным левым сайдбаром, роль отображается в топбаре (цветной тег)
- Ролевая навигация: admin видит «Настройки» + «Пользователи», client/test видят только «Панель управления» и «Мои проекты»
- Dashboard переведён на единый layout: используется `layouts.app` вместо самописного
Changed
- Версия платформы: 0.3.0 → 0.4.0
- ClientSeeder: переработан — 4 аккаунта с разными ролями, обновлены сиды токенов
v0.3.0
20.07.2026
MINOR
Админ-панель: настройки, безопасность, левый сайдбар
Added
- Админ-панель с левым сайдбаром единый layout `layouts.admin` с навигацией (Панель управления, Настройки), версией в футере, формой выхода
- Страница настроек `/admin/settings` с табами три вкладки: Основные, Безопасность, Версии системы (паттерн как в task.ipd)
- Вкладка «Безопасность» переключатели: требования к сложности пароля, 2FA, автоматический выход по таймеру, лимит попыток входа, отображение служебных параметров среды (в настройках администратора)
- Вкладка «Основные» название сайта, email администратора, язык, часовой пояс
- Вкладка «Версии системы» переехала из отдельного роута в таб настроек
- Система настроек пользователя таблица `user_settings` (key-value EAV), модель `UserSetting`, трекинг изменений через `settings_history`
- Методы User model `getSetting()`, `getAllSettings()`, `setSetting()`, `setSettings()` для работы с настройками
Changed
- Версия платформы 0.2.1 → 0.3.0
- Version-footer ссылка ведёт на `route('admin.settings', ['tab' => 'versions'])` (через сайдбар)
- Удалён отдельный route `/admin/settings/versions` заменён на таб в `admin.settings`
v0.2.0
20.07.2026
MINOR
MVP платформы
Added
- Dashboard клиента: отображение статистики задач (всего, в работе, выполнено, прогресс), список проектов, информация о тарифе
- Гостевой доступ: публичная страница `/s/{token}` для просмотра задач без авторизации
- Авторизация: форма входа с email/паролем, middleware аутентификации, редирект на dashboard
- Интеграция с task.ipd.by: API-сервис для получения задач, проектов, направлений через `/api/export/full`
- Тарифы: 4 тарифа (Минимум, Стандарт, Полный, VIP) с фичами и временем ответа
- Система клиентов: 4 таблицы (users, clients, tariffs, client_tokens), сиды с демо-данными
- Версия платформы: отображение версии в левом нижнем углу с попапом истории изменений
Changed
- Архитектура: сайт перенесён с OSPanel (старая версия) на Laravel Herd (PHP 8.4, MariaDB 10.11)
- Кодовая база: полная переработка с кастомного PHP на Laravel 13
v0.1.0
13.07.2026
MINOR
Первая версия платформы
Added
- Первая версия платформы: создана Помощником (System 4) на базе Laravel
- Базовая структура: авторизация, маршруты, контроллеры, Blade-шаблоны
- Конфигурация окружения: .env, подключение к БД, настройки сессий
- Демо-данные: демонстрационный клиентский аккаунт, тариф «Полный», гостевой токен доступа.
- Интеграция с task.ipd.by: первичная настройка API-клиента
Architecture
- Laravel 13, PHP 8.3+, MariaDB, Blade, middleware аутентификации
- TaskApiService для получения данных из внешней системы задач
- ClientAuthController с guard `client` и bcrypt-паролями
Полезные ссылки