Необходимо создать для администратора редактор объявлений для обычных пользователей приложения
Критерий готовности
Макет принят владельцем, система протестирована владельцем, система прошла автотесты, код проверен на безопасность.
Порядок выполнения
Сначала делается мокап (макет) объявления и редактора, после его доработки и подтверждения владельцем проекта начинается разработка и реализация полноценной системы.
Требования
Форма объявлений аналогична просьбе ввести пароль для аккаунта, но при этом туда выводится только текст, и для закрытия нужно нажать крестик в правом верхнем углу (либо кнопку "понятно" под текстом объявления, это нужно посмотреть в мокапе).
В админском аккаунте новый раздел - создание объявлений. Редактируется заголовок, текст, дата начала показа, дата окончания показа. В идеале сделать редактор текста с различными редакторами текста, позволяющими указывать акценты эмодзи, выделениями, разными шрифтами и т.п. Условный редактор поста, но с условием, что пост короткий.
Объявление показывается игрокам только в период, указанный при его создании, одному пользователю - один раз за весь период.
В случае, если показываются несколько объявлений (периоды показа пересекаются), они показываются от старого к новому, следующее объявление показывается после закрытия предыдущего.
# Суть задачи
Необходимо создать для администратора редактор объявлений для обычных пользователей приложения
# Критерий готовности
Макет принят владельцем, система протестирована владельцем, система прошла автотесты, код проверен на безопасность.
# Порядок выполнения
Сначала делается мокап (макет) объявления и редактора, после его доработки и подтверждения владельцем проекта начинается разработка и реализация полноценной системы.
# Требования
1) Форма объявлений аналогична просьбе ввести пароль для аккаунта, но при этом туда выводится только текст, и для закрытия нужно нажать крестик в правом верхнем углу (либо кнопку "понятно" под текстом объявления, это нужно посмотреть в мокапе).
2) В админском аккаунте новый раздел - создание объявлений. Редактируется заголовок, текст, дата начала показа, дата окончания показа. В идеале сделать редактор текста с различными редакторами текста, позволяющими указывать акценты эмодзи, выделениями, разными шрифтами и т.п. Условный редактор поста, но с условием, что пост короткий.
3) Объявление показывается игрокам только в период, указанный при его создании, одному пользователю - один раз за весь период.
4) В случае, если показываются несколько объявлений (периоды показа пересекаются), они показываются от старого к новому, следующее объявление показывается после закрытия предыдущего.
NotBigGhost
added this to the 1.45 — Уведомления от администрации milestone 2026-09-18 02:24:18 +03:00
Проход закрывает первый этап из тела задачи — макеты; разработка начинается отдельным проходом после того, как макет принят и закрыты вопросы ниже.
mockups/announcements/ — отдельная папка (существующие макеты «указание мест» не перезаписываю), общий shared.css на токенах приложения (frontend/src/styles/tokens.css) и index.html-оглавление с плюсами/минусами вариантов.
Окно объявления игроку — 3 варианта, все на .modal-overlay/.modal, как SetPasswordDialog: A — крестик вверху справа + кнопка «Понятно»; B — только «Понятно» во всю ширину, сверху плашка «Объявление» с датой; C — длинный текст: липкая шапка с крестиком, прокрутка тела, липкая кнопка, индикатор очереди «1 из 3». В каждом прокликивается очередь из трёх объявлений от старого к новому.
Редактор в админке — 2 варианта (шестая вкладка рядом с «Логи»): R1 — тулбар (жирный/курсив/акцент/размер/эмодзи) поверх contenteditable; R2 — короткая разметка **жирный**, _курсив_, ==акцент== с живым предпросмотром. Общее: заголовок, текст со счётчиком символов, «Показывать с»/«по», предпросмотр ровно тем окном из п.2, список объявлений со статусами (Черновик / Запланировано / Идёт / Завершено) и счётчиком закрывших.
В index.html фиксирую правила показа из ТЗ (период; один раз на пользователя за период; при пересечении — от старого к новому, следующее после закрытия предыдущего) и открытые вопросы: крестик или «Понятно» (или оба); что считается «показано» — открытие или закрытие; видят ли объявление зарегистрировавшиеся в середине периода; где хранится факт показа (сервер или браузер); «разные шрифты» — в приложении шрифт один, предлагаю ограничиться жирным/курсивом/акцентом/размером/эмодзи; приоритет относительно обязательного окна пароля и на каких страницах всплывает.
Критерии готовности
Три варианта окна игрока и два варианта редактора — рабочие статические страницы: очередь прокликивается, предпросмотр открывается, читаются на ширине 480px и на десктопе.
Цвета и модалка совпадают с приложением (tokens.css, .modal из global.css).
Правила показа и открытые вопросы перечислены в index.html и продублированы в отчёте.
Код backend/ и frontend/ не меняется.
Ветка:issue-84-announcements от dev. Папка mockups/ в .gitignore — макеты остаются локальными, PR в этом проходе не открывается (согласовано с владельцем); в ветке будет только правка .gitignore.
## План выполнения
Проход закрывает **первый этап из тела задачи — макеты**; разработка начинается отдельным проходом после того, как макет принят и закрыты вопросы ниже.
1. `mockups/announcements/` — отдельная папка (существующие макеты «указание мест» не перезаписываю), общий `shared.css` на токенах приложения (`frontend/src/styles/tokens.css`) и `index.html`-оглавление с плюсами/минусами вариантов.
2. **Окно объявления игроку — 3 варианта**, все на `.modal-overlay`/`.modal`, как `SetPasswordDialog`: **A** — крестик вверху справа + кнопка «Понятно»; **B** — только «Понятно» во всю ширину, сверху плашка «Объявление» с датой; **C** — длинный текст: липкая шапка с крестиком, прокрутка тела, липкая кнопка, индикатор очереди «1 из 3». В каждом прокликивается очередь из трёх объявлений от старого к новому.
3. **Редактор в админке — 2 варианта** (шестая вкладка рядом с «Логи»): **R1** — тулбар (жирный/курсив/акцент/размер/эмодзи) поверх contenteditable; **R2** — короткая разметка `**жирный**`, `_курсив_`, `==акцент==` с живым предпросмотром. Общее: заголовок, текст со счётчиком символов, «Показывать с»/«по», предпросмотр ровно тем окном из п.2, список объявлений со статусами (Черновик / Запланировано / Идёт / Завершено) и счётчиком закрывших.
4. В `index.html` фиксирую правила показа из ТЗ (период; один раз на пользователя за период; при пересечении — от старого к новому, следующее после закрытия предыдущего) и **открытые вопросы**: крестик или «Понятно» (или оба); что считается «показано» — открытие или закрытие; видят ли объявление зарегистрировавшиеся в середине периода; где хранится факт показа (сервер или браузер); «разные шрифты» — в приложении шрифт один, предлагаю ограничиться жирным/курсивом/акцентом/размером/эмодзи; приоритет относительно обязательного окна пароля и на каких страницах всплывает.
**Критерии готовности**
- Три варианта окна игрока и два варианта редактора — рабочие статические страницы: очередь прокликивается, предпросмотр открывается, читаются на ширине 480px и на десктопе.
- Цвета и модалка совпадают с приложением (`tokens.css`, `.modal` из `global.css`).
- Правила показа и открытые вопросы перечислены в `index.html` и продублированы в отчёте.
- Код `backend/` и `frontend/` не меняется.
**Ветка:** `issue-84-announcements` от `dev`. Папка `mockups/` в `.gitignore` — макеты остаются локальными, PR в этом проходе не открывается (согласовано с владельцем); в ветке будет только правка `.gitignore`.
Где смотреть:mockups/announcements/index.html — открыть в браузере (папка mockups/ в .gitignore, поэтому макеты остались локальными, как и прошлые прототипы). В оглавлении — все пять страниц с плюсами и минусами, правила показа и вопросы.
Окно у игрока
A (player-a.html) — крестик в правом верхнем углу и кнопка «Понятно», модалка ровно как окно установки пароля.
B (player-b.html) — крестика нет, закрывает только широкая кнопка «Понятно»; сверху плашка «Объявление» с датой, снизу точки очереди.
C (player-c.html) — под длинный текст: закреплённые шапка с крестиком и низ с кнопкой, прокручивается только текст.
В каждом варианте прокликивается очередь из трёх объявлений от старого к новому — следующее открывается после закрытия предыдущего, внизу страницы видно, что при этом ушло бы на сервер.
Редактор в админке (шестая вкладка рядом с «Логи», список объявлений со статусами и счётчиком «закрыли N из 8»)
R1 (admin-r1.html) — тулбар: жирный, курсив, акцент, эмодзи прямо в поле; предпросмотр кнопкой.
R2 (admin-r2.html) — короткая разметка **жирный**, _курсив_, ==акцент== с живым предпросмотром; в базе остаётся обычный текст, HTML из поля не просачивается.
Вопросы, без ответов на которые не начать разработку (подробно — в index.html):
чем закрывать: крестик, «Понятно» или оба;
что считать «показано» — открытие окна или закрытие (предлагаю закрытие);
видят ли объявление те, кто пришёл в середине периода (предлагаю — да);
где хранить «уже видел»: таблица на сервере или память браузера (предлагаю таблицу);
«разные шрифты» из требований: шрифт в приложении один, предлагаю жирный + курсив + акцентный цвет + эмодзи;
на каких страницах всплывает окно и что делать, если у игрока ещё не задан пароль (окно пароля закрыть нельзя);
показывать ли заново объявление, текст которого поправили посреди периода (предлагаю — нет).
Проверки: логика очереди и разбор разметки прогнаны в Node (порядок 12 → 13 → 14, следующее только после закрытия предыдущего; <script> из поля редактора экранируется), синтаксис всех страниц проверен. Визуально в браузере макеты не открывались — расширение Chrome в этой сессии не подключено, так что вёрстку на живом экране стоит посмотреть вам.
Ещё в проходе:AGENTS.md добавлен в .gitignore (коммит 2e496a3 в локальной ветке issue-84-announcements). PR не открывал — в ветке только эта одна строка, сольёте когда удобно.
Статус:Status/In Review — жду, какой вариант окна и какой редактор берём и что отвечаем по семи вопросам. После этого завожу задачу на реализацию (модель, API, раздел админки, окно в AppShell, тесты).
Макеты готовы, жду выбора варианта.
**Где смотреть:** `mockups/announcements/index.html` — открыть в браузере (папка `mockups/` в `.gitignore`, поэтому макеты остались локальными, как и прошлые прототипы). В оглавлении — все пять страниц с плюсами и минусами, правила показа и вопросы.
**Окно у игрока**
- **A** (`player-a.html`) — крестик в правом верхнем углу **и** кнопка «Понятно», модалка ровно как окно установки пароля.
- **B** (`player-b.html`) — крестика нет, закрывает только широкая кнопка «Понятно»; сверху плашка «Объявление» с датой, снизу точки очереди.
- **C** (`player-c.html`) — под длинный текст: закреплённые шапка с крестиком и низ с кнопкой, прокручивается только текст.
В каждом варианте прокликивается очередь из трёх объявлений от старого к новому — следующее открывается после закрытия предыдущего, внизу страницы видно, что при этом ушло бы на сервер.
**Редактор в админке** (шестая вкладка рядом с «Логи», список объявлений со статусами и счётчиком «закрыли N из 8»)
- **R1** (`admin-r1.html`) — тулбар: жирный, курсив, акцент, эмодзи прямо в поле; предпросмотр кнопкой.
- **R2** (`admin-r2.html`) — короткая разметка `**жирный**`, `_курсив_`, `==акцент==` с живым предпросмотром; в базе остаётся обычный текст, HTML из поля не просачивается.
**Вопросы, без ответов на которые не начать разработку** (подробно — в `index.html`):
1. чем закрывать: крестик, «Понятно» или оба;
2. что считать «показано» — открытие окна или закрытие (предлагаю закрытие);
3. видят ли объявление те, кто пришёл в середине периода (предлагаю — да);
4. где хранить «уже видел»: таблица на сервере или память браузера (предлагаю таблицу);
5. «разные шрифты» из требований: шрифт в приложении один, предлагаю жирный + курсив + акцентный цвет + эмодзи;
6. на каких страницах всплывает окно и что делать, если у игрока ещё не задан пароль (окно пароля закрыть нельзя);
7. показывать ли заново объявление, текст которого поправили посреди периода (предлагаю — нет).
**Проверки:** логика очереди и разбор разметки прогнаны в Node (порядок 12 → 13 → 14, следующее только после закрытия предыдущего; `<script>` из поля редактора экранируется), синтаксис всех страниц проверен. **Визуально в браузере макеты не открывались** — расширение Chrome в этой сессии не подключено, так что вёрстку на живом экране стоит посмотреть вам.
**Ещё в проходе:** `AGENTS.md` добавлен в `.gitignore` (коммит `2e496a3` в локальной ветке `issue-84-announcements`). PR не открывал — в ветке только эта одна строка, сольёте когда удобно.
**Статус:** `Status/In Review` — жду, какой вариант окна и какой редактор берём и что отвечаем по семи вопросам. После этого завожу задачу на реализацию (модель, API, раздел админки, окно в `AppShell`, тесты).
По запросу добавлен вариант окна D (mockups/announcements/player-d.html, в оглавлении выделен рамкой): сверху плашка «Объявление» из B, но без даты, на месте даты — крестик, то есть ровно в правом верхнем углу окна. Ниже — тело из A: заголовок, текст, точки очереди и кнопка «Понятно». Дата публикации в окне не показывается.
Стили плашки вынесены в общий shared.css, вариант B выглядит как раньше. Проверен синтаксис скриптов, в браузере не открывал. Статус прежний — Status/In Review.
По запросу добавлен вариант окна **D** (`mockups/announcements/player-d.html`, в оглавлении выделен рамкой): сверху плашка «Объявление» из B, но без даты, на месте даты — крестик, то есть ровно в правом верхнем углу окна. Ниже — тело из A: заголовок, текст, точки очереди и кнопка «Понятно». Дата публикации в окне не показывается.
Стили плашки вынесены в общий `shared.css`, вариант B выглядит как раньше. Проверен синтаксис скриптов, в браузере не открывал. Статус прежний — `Status/In Review`.
крестик убран — окно закрывается только кнопкой «Понятно»;
на его месте, в правом углу плашки «Объявление», — стикер темы: чуть повёрнутый, «наклеенный» на угол;
в оба редактора (R1, R2) добавлен выбор темы: 🏆 Турнир, 🎲 Игровой вечер, ✨ Новое на сайте, 🛠️ Техработы, 📜 Правила, ❗ Важно и «Без стикера». Предпросмотр в редакторах теперь показывает окно D со стикером (в R2 — живой, под полем), в списке объявлений эмодзи темы стоит перед заголовком.
В index.html добавлен вопрос 8: хватит ли фиксированного набора тем или админу нужны свои, и оставить эмодзи или рисовать стикеры картинками в стиле редизайна v1.7.
Проверено: синтаксис всех страниц; выбор темы и сборка плашки прогнаны в Node. В браузере не открывал. Статус прежний — Status/In Review.
Вариант **D** доработан по запросу:
- **крестик убран** — окно закрывается только кнопкой «Понятно»;
- на его месте, в правом углу плашки «Объявление», — **стикер темы**: чуть повёрнутый, «наклеенный» на угол;
- **в оба редактора (R1, R2) добавлен выбор темы**: 🏆 Турнир, 🎲 Игровой вечер, ✨ Новое на сайте, 🛠️ Техработы, 📜 Правила, ❗ Важно и «Без стикера». Предпросмотр в редакторах теперь показывает окно D со стикером (в R2 — живой, под полем), в списке объявлений эмодзи темы стоит перед заголовком.
В `index.html` добавлен вопрос 8: хватит ли фиксированного набора тем или админу нужны свои, и оставить эмодзи или рисовать стикеры картинками в стиле редизайна v1.7.
Проверено: синтаксис всех страниц; выбор темы и сборка плашки прогнаны в Node. В браузере не открывал. Статус прежний — `Status/In Review`.
По запросу добавлен вариант окна E (mockups/announcements/player-e.html, в оглавлении теперь выделен он):
стикеров нет;
в плашке вместо метки «Объявление» — сам заголовок объявления, отдельной строки заголовка ниже нет;
дальше как в D: текст, точки очереди и «Понятно» — крестика нет.
Редакторы (R1 и R2):
выделять можно золотым и красным: в R1 — вторая кнопка «A» красного цвета, в R2 — значок !!красный!! рядом с ==золотой==;
выбор темы (стикеры) убран, предпросмотр показывает окно E; эмодзи тем из списка объявлений тоже убраны.
Страница D осталась как была, со стикерами — для сравнения. Вопрос 8 про стикеры в index.html теперь помечен «только если выбран D».
Проверено: синтаксис всех страниц, все элементы, на которые ссылаются скрипты, есть в разметке; разбор ==…== / !!…!! в R2 прогнан в Node, HTML из поля по-прежнему экранируется. В браузере не открывал. Статус прежний — Status/In Review.
По запросу добавлен вариант окна **E** (`mockups/announcements/player-e.html`, в оглавлении теперь выделен он):
- стикеров нет;
- в плашке вместо метки «Объявление» — **сам заголовок объявления**, отдельной строки заголовка ниже нет;
- дальше как в D: текст, точки очереди и «Понятно» — крестика нет.
**Редакторы (R1 и R2):**
- выделять можно **золотым и красным**: в R1 — вторая кнопка «A» красного цвета, в R2 — значок `!!красный!!` рядом с `==золотой==`;
- выбор темы (стикеры) убран, предпросмотр показывает окно E; эмодзи тем из списка объявлений тоже убраны.
Страница D осталась как была, со стикерами — для сравнения. Вопрос 8 про стикеры в `index.html` теперь помечен «только если выбран D».
Проверено: синтаксис всех страниц, все элементы, на которые ссылаются скрипты, есть в разметке; разбор `==…==` / `!!…!!` в R2 прогнан в Node, HTML из поля по-прежнему экранируется. В браузере не открывал. Статус прежний — `Status/In Review`.
Ответы владельца: прочитанным считается по закрытию; «показывать новым игрокам» — галочка в редакторе; факт показа хранится на сервере; шрифт один; окно — после окна пароля, на любой странице; правка идущего объявления — по галочке «показать заново», с красной пометкой «обновлено» под заголовком. Дополнительно: выделение текста в редакторе — полупрозрачным оранжевым вместо синего.
Модель и миграция 0015 (идемпотентная): announcements (заголовок, очищенный HTML текста, период показа, флаг «показывать новым», номер версии) и announcement_views (кто закрыл и какую версию).
Очистка HTML на сервере — белый список b / em / mark (+ class="red") / p / br, всё остальное отбрасывается; script/style — вместе с содержимым. Фронт вставляет только очищенный сервером HTML.
API игрока:GET /api/announcements/pending (активные, ещё не закрытые, от старого к новому), POST /api/announcements/{id}/ack.
API админки: список со статусом и счётчиком «закрыли N из M», создание, правка (с «показать заново»), «снять с показа», удаление; всё — в журнал аудита. SSE-событие announcements игрокам.
Фронт: окно E в AppShell (только когда пароль задан), вкладка «Объявления» в админке с редактором R1 (жирный, курсив, золотой и красный акцент, эмодзи, очистка оформления), предпросмотром окна E и списком объявлений.
Тесты: очистка HTML (в т.ч. попытки XSS), права доступа, период показа, «новые игроки», повторный показ с пометкой, снятие с показа и удаление.
Решения по ходу: «новые игроки» — зарегистрированные после начала показа; пометку «обновлено» видят только те, кто закрывал прежнюю версию; черновиков нет (период обязателен), вместо них — «Дублировать».
Ветка:issue-84-announcements от dev, PR в dev с Closes #84.
## План реализации (выбраны E + R1)
Ответы владельца: прочитанным считается по закрытию; «показывать новым игрокам» — галочка в редакторе; факт показа хранится на сервере; шрифт один; окно — после окна пароля, на любой странице; правка идущего объявления — по галочке «показать заново», с красной пометкой «обновлено» под заголовком. Дополнительно: выделение текста в редакторе — полупрозрачным оранжевым вместо синего.
1. **Модель и миграция `0015`** (идемпотентная): `announcements` (заголовок, очищенный HTML текста, период показа, флаг «показывать новым», номер версии) и `announcement_views` (кто закрыл и какую версию).
2. **Очистка HTML на сервере** — белый список `b / em / mark (+ class="red") / p / br`, всё остальное отбрасывается; `script`/`style` — вместе с содержимым. Фронт вставляет только очищенный сервером HTML.
3. **API игрока:** `GET /api/announcements/pending` (активные, ещё не закрытые, от старого к новому), `POST /api/announcements/{id}/ack`.
4. **API админки:** список со статусом и счётчиком «закрыли N из M», создание, правка (с «показать заново»), «снять с показа», удаление; всё — в журнал аудита. SSE-событие `announcements` игрокам.
5. **Фронт:** окно E в `AppShell` (только когда пароль задан), вкладка «Объявления» в админке с редактором R1 (жирный, курсив, золотой и красный акцент, эмодзи, очистка оформления), предпросмотром окна E и списком объявлений.
6. **Тесты:** очистка HTML (в т.ч. попытки XSS), права доступа, период показа, «новые игроки», повторный показ с пометкой, снятие с показа и удаление.
**Решения по ходу:** «новые игроки» — зарегистрированные после начала показа; пометку «обновлено» видят только те, кто закрывал прежнюю версию; черновиков нет (период обязателен), вместо них — «Дублировать».
**Ветка:** `issue-84-announcements` от `dev`, PR в `dev` с `Closes #84`.
Итог: реализованы окно E у игрока (на любой странице, после окна пароля, очередь от старого к новому, закрытие только «Понятно», красная пометка «обновлено» при повторном показе) и редактор R1 в админке (жирный, курсив, золотой и красный акцент, эмодзи; «Показывать новым игрокам»; «Показать заново»; предпросмотр; список со статусом и счётчиком «закрыли N из M»). Факт закрытия хранится на сервере. Выделение текста в форме — полупрозрачный оранжевый. Текст объявления очищается на сервере по белому списку тегов. Проверки:pytest — 208 passed (+14 новых); npm run build — зелёный; миграция 0015 — на копии dev-БД и на пустой базе; /security-review — уязвимостей HIGH/MEDIUM нет. В браузере не проверялось (нет подключения к Chrome) — нужна ручная проверка. Отклонения: черновиков нет (период обязателен, есть «Дублировать»); «новые игроки» — зарегистрированные после начала показа. Статус:Status/In Review Осталось за вами: ручная проверка («система протестирована владельцем» из критериев), ревью и мёрж PR — задача закроется автоматически.
Работа выполнена, открыт PR: https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/85
**Итог:** реализованы окно **E** у игрока (на любой странице, после окна пароля, очередь от старого к новому, закрытие только «Понятно», красная пометка «обновлено» при повторном показе) и редактор **R1** в админке (жирный, курсив, золотой и красный акцент, эмодзи; «Показывать новым игрокам»; «Показать заново»; предпросмотр; список со статусом и счётчиком «закрыли N из M»). Факт закрытия хранится на сервере. Выделение текста в форме — полупрозрачный оранжевый. Текст объявления очищается на сервере по белому списку тегов.
**Проверки:** `pytest` — 208 passed (+14 новых); `npm run build` — зелёный; миграция `0015` — на копии dev-БД и на пустой базе; `/security-review` — уязвимостей HIGH/MEDIUM нет. В браузере не проверялось (нет подключения к Chrome) — нужна ручная проверка.
**Отклонения:** черновиков нет (период обязателен, есть «Дублировать»); «новые игроки» — зарегистрированные после начала показа.
**Статус:** `Status/In Review`
**Осталось за вами:** ручная проверка («система протестирована владельцем» из критериев), ревью и мёрж PR — задача закроется автоматически.
По просьбе владельца объявления выпускаются в main раньше рейтинга. Теперь PR два:
PR #85 → dev (#85) — как был, плюс коммит ec0445f: миграция 0015 отходит от 0013, пустая 0016 сводит её с рейтинговой 0014. Закрывает задачу.
PR #86 → main (#86) — те же изменения, перенесённые на main без рейтинга. Проверки: pytest 161 passed, npm run build зелёный, schema.d.ts сверен с OpenAPI ветки.
Путь миграций прода проверен на одной базе: сейчас 0013 → релиз объявлений 0015 → позже, с dev, alembic сам применит 0014 и 0016. Порядок мёржа двух PR не важен. При будущем слиянии dev → main будет один конфликт — строка импортов в backend/app/schemas/api.py, правильная версия из dev.
По просьбе владельца объявления выпускаются в `main` раньше рейтинга. Теперь PR два:
- **PR #85 → `dev`** (https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/85) — как был, плюс коммит `ec0445f`: миграция `0015` отходит от `0013`, пустая `0016` сводит её с рейтинговой `0014`. Закрывает задачу.
- **PR #86 → `main`** (https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/86) — те же изменения, перенесённые на `main` без рейтинга. Проверки: `pytest` 161 passed, `npm run build` зелёный, `schema.d.ts` сверен с OpenAPI ветки.
Путь миграций прода проверен на одной базе: сейчас `0013` → релиз объявлений `0015` → позже, с `dev`, alembic сам применит `0014` и `0016`. Порядок мёржа двух PR не важен. При будущем слиянии `dev → main` будет один конфликт — строка импортов в `backend/app/schemas/api.py`, правильная версия из `dev`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Суть задачи
Необходимо создать для администратора редактор объявлений для обычных пользователей приложения
Критерий готовности
Макет принят владельцем, система протестирована владельцем, система прошла автотесты, код проверен на безопасность.
Порядок выполнения
Сначала делается мокап (макет) объявления и редактора, после его доработки и подтверждения владельцем проекта начинается разработка и реализация полноценной системы.
Требования
План выполнения
Проход закрывает первый этап из тела задачи — макеты; разработка начинается отдельным проходом после того, как макет принят и закрыты вопросы ниже.
mockups/announcements/— отдельная папка (существующие макеты «указание мест» не перезаписываю), общийshared.cssна токенах приложения (frontend/src/styles/tokens.css) иindex.html-оглавление с плюсами/минусами вариантов..modal-overlay/.modal, какSetPasswordDialog: A — крестик вверху справа + кнопка «Понятно»; B — только «Понятно» во всю ширину, сверху плашка «Объявление» с датой; C — длинный текст: липкая шапка с крестиком, прокрутка тела, липкая кнопка, индикатор очереди «1 из 3». В каждом прокликивается очередь из трёх объявлений от старого к новому.**жирный**,_курсив_,==акцент==с живым предпросмотром. Общее: заголовок, текст со счётчиком символов, «Показывать с»/«по», предпросмотр ровно тем окном из п.2, список объявлений со статусами (Черновик / Запланировано / Идёт / Завершено) и счётчиком закрывших.index.htmlфиксирую правила показа из ТЗ (период; один раз на пользователя за период; при пересечении — от старого к новому, следующее после закрытия предыдущего) и открытые вопросы: крестик или «Понятно» (или оба); что считается «показано» — открытие или закрытие; видят ли объявление зарегистрировавшиеся в середине периода; где хранится факт показа (сервер или браузер); «разные шрифты» — в приложении шрифт один, предлагаю ограничиться жирным/курсивом/акцентом/размером/эмодзи; приоритет относительно обязательного окна пароля и на каких страницах всплывает.Критерии готовности
tokens.css,.modalизglobal.css).index.htmlи продублированы в отчёте.backend/иfrontend/не меняется.Ветка:
issue-84-announcementsотdev. Папкаmockups/в.gitignore— макеты остаются локальными, PR в этом проходе не открывается (согласовано с владельцем); в ветке будет только правка.gitignore.Макеты готовы, жду выбора варианта.
Где смотреть:
mockups/announcements/index.html— открыть в браузере (папкаmockups/в.gitignore, поэтому макеты остались локальными, как и прошлые прототипы). В оглавлении — все пять страниц с плюсами и минусами, правила показа и вопросы.Окно у игрока
player-a.html) — крестик в правом верхнем углу и кнопка «Понятно», модалка ровно как окно установки пароля.player-b.html) — крестика нет, закрывает только широкая кнопка «Понятно»; сверху плашка «Объявление» с датой, снизу точки очереди.player-c.html) — под длинный текст: закреплённые шапка с крестиком и низ с кнопкой, прокручивается только текст.В каждом варианте прокликивается очередь из трёх объявлений от старого к новому — следующее открывается после закрытия предыдущего, внизу страницы видно, что при этом ушло бы на сервер.
Редактор в админке (шестая вкладка рядом с «Логи», список объявлений со статусами и счётчиком «закрыли N из 8»)
admin-r1.html) — тулбар: жирный, курсив, акцент, эмодзи прямо в поле; предпросмотр кнопкой.admin-r2.html) — короткая разметка**жирный**,_курсив_,==акцент==с живым предпросмотром; в базе остаётся обычный текст, HTML из поля не просачивается.Вопросы, без ответов на которые не начать разработку (подробно — в
index.html):Проверки: логика очереди и разбор разметки прогнаны в Node (порядок 12 → 13 → 14, следующее только после закрытия предыдущего;
<script>из поля редактора экранируется), синтаксис всех страниц проверен. Визуально в браузере макеты не открывались — расширение Chrome в этой сессии не подключено, так что вёрстку на живом экране стоит посмотреть вам.Ещё в проходе:
AGENTS.mdдобавлен в.gitignore(коммит2e496a3в локальной веткеissue-84-announcements). PR не открывал — в ветке только эта одна строка, сольёте когда удобно.Статус:
Status/In Review— жду, какой вариант окна и какой редактор берём и что отвечаем по семи вопросам. После этого завожу задачу на реализацию (модель, API, раздел админки, окно вAppShell, тесты).По запросу добавлен вариант окна D (
mockups/announcements/player-d.html, в оглавлении выделен рамкой): сверху плашка «Объявление» из B, но без даты, на месте даты — крестик, то есть ровно в правом верхнем углу окна. Ниже — тело из A: заголовок, текст, точки очереди и кнопка «Понятно». Дата публикации в окне не показывается.Стили плашки вынесены в общий
shared.css, вариант B выглядит как раньше. Проверен синтаксис скриптов, в браузере не открывал. Статус прежний —Status/In Review.Вариант D доработан по запросу:
В
index.htmlдобавлен вопрос 8: хватит ли фиксированного набора тем или админу нужны свои, и оставить эмодзи или рисовать стикеры картинками в стиле редизайна v1.7.Проверено: синтаксис всех страниц; выбор темы и сборка плашки прогнаны в Node. В браузере не открывал. Статус прежний —
Status/In Review.По запросу добавлен вариант окна E (
mockups/announcements/player-e.html, в оглавлении теперь выделен он):Редакторы (R1 и R2):
!!красный!!рядом с==золотой==;Страница D осталась как была, со стикерами — для сравнения. Вопрос 8 про стикеры в
index.htmlтеперь помечен «только если выбран D».Проверено: синтаксис всех страниц, все элементы, на которые ссылаются скрипты, есть в разметке; разбор
==…==/!!…!!в R2 прогнан в Node, HTML из поля по-прежнему экранируется. В браузере не открывал. Статус прежний —Status/In Review.План реализации (выбраны E + R1)
Ответы владельца: прочитанным считается по закрытию; «показывать новым игрокам» — галочка в редакторе; факт показа хранится на сервере; шрифт один; окно — после окна пароля, на любой странице; правка идущего объявления — по галочке «показать заново», с красной пометкой «обновлено» под заголовком. Дополнительно: выделение текста в редакторе — полупрозрачным оранжевым вместо синего.
0015(идемпотентная):announcements(заголовок, очищенный HTML текста, период показа, флаг «показывать новым», номер версии) иannouncement_views(кто закрыл и какую версию).b / em / mark (+ class="red") / p / br, всё остальное отбрасывается;script/style— вместе с содержимым. Фронт вставляет только очищенный сервером HTML.GET /api/announcements/pending(активные, ещё не закрытые, от старого к новому),POST /api/announcements/{id}/ack.announcementsигрокам.AppShell(только когда пароль задан), вкладка «Объявления» в админке с редактором R1 (жирный, курсив, золотой и красный акцент, эмодзи, очистка оформления), предпросмотром окна E и списком объявлений.Решения по ходу: «новые игроки» — зарегистрированные после начала показа; пометку «обновлено» видят только те, кто закрывал прежнюю версию; черновиков нет (период обязателен), вместо них — «Дублировать».
Ветка:
issue-84-announcementsотdev, PR вdevсCloses #84.Работа выполнена, открыт PR: #85
Итог: реализованы окно E у игрока (на любой странице, после окна пароля, очередь от старого к новому, закрытие только «Понятно», красная пометка «обновлено» при повторном показе) и редактор R1 в админке (жирный, курсив, золотой и красный акцент, эмодзи; «Показывать новым игрокам»; «Показать заново»; предпросмотр; список со статусом и счётчиком «закрыли N из M»). Факт закрытия хранится на сервере. Выделение текста в форме — полупрозрачный оранжевый. Текст объявления очищается на сервере по белому списку тегов.
Проверки:
pytest— 208 passed (+14 новых);npm run build— зелёный; миграция0015— на копии dev-БД и на пустой базе;/security-review— уязвимостей HIGH/MEDIUM нет. В браузере не проверялось (нет подключения к Chrome) — нужна ручная проверка.Отклонения: черновиков нет (период обязателен, есть «Дублировать»); «новые игроки» — зарегистрированные после начала показа.
Статус:
Status/In ReviewОсталось за вами: ручная проверка («система протестирована владельцем» из критериев), ревью и мёрж PR — задача закроется автоматически.
По просьбе владельца объявления выпускаются в
mainраньше рейтинга. Теперь PR два:dev(#85) — как был, плюс коммитec0445f: миграция0015отходит от0013, пустая0016сводит её с рейтинговой0014. Закрывает задачу.main(#86) — те же изменения, перенесённые наmainбез рейтинга. Проверки:pytest161 passed,npm run buildзелёный,schema.d.tsсверен с OpenAPI ветки.Путь миграций прода проверен на одной базе: сейчас
0013→ релиз объявлений0015→ позже, сdev, alembic сам применит0014и0016. Порядок мёржа двух PR не важен. При будущем слиянииdev → mainбудет один конфликт — строка импортов вbackend/app/schemas/api.py, правильная версия изdev.