feat: context menus, unified photo panel, tags, album trash and counters

- backend: deleting an album moves its photos to the trash without losing files (photos.album_id nullable, FK ON DELETE SET NULL, migration 000006; dropped S3 prefix delete); restore accepts an album target (album_required when the album is gone); user-scoped tags (000007) with GET /tags, POST/DELETE /photos/:id/tags, tags included in photo responses; GET /trash/count
- frontend: right-click context menus for photos (grid + viewer) and albums (rail + overview); single shared PhotoPanel reused in viewer and gallery selection (not a copy); viewer download/delete moved to the panel 'Действия'; transparent compact viewer top bar; trash entry as last rail item with per-album photo counts and trash count; album rename/delete dialogs
- openspec: sync lightroom-workspace-ui artifacts (specs photos/ui-workspace, design #24-30, tasks 9.17-9.24)
This commit is contained in:
iu
2026-10-10 22:45:41 +03:00
parent 2d6a3fea1a
commit 61b45b2742
37 changed files with 1639 additions and 328 deletions
@@ -87,3 +87,32 @@
#### Scenario: Окончательное удаление при наличии ссылок
- **WHEN** на объект хранилища ссылается копия в другом альбоме
- **THEN** запись удаляется, а объект хранилища сохраняется
### Requirement: Удаление альбома и восстановление фото без альбома
Удаление альбома SHALL помещать его фото в корзину, не удаляя объекты хранилища. Если при
восстановлении фото его альбом отсутствует, система SHALL предложить выбрать существующий
альбом или создать новый.
#### Scenario: Удаление альбома
- **WHEN** владелец удаляет альбом
- **THEN** его фото оказываются в корзине без привязки к альбому, файлы сохраняются
#### Scenario: Восстановление без альбома
- **WHEN** пользователь восстанавливает фото, у которого нет альбома
- **THEN** система предлагает выбрать альбом или создать новый и восстанавливает фото в него
### Requirement: Метки фото
Пользователь SHALL присваивать фото свои метки (область — пользователь): создавать новые и
выбирать из списка с поиском; метки SHALL отдаваться вместе с фото и сниматься.
#### Scenario: Создание метки
- **WHEN** пользователь вводит новую метку и подтверждает
- **THEN** метка создаётся и привязывается к фото
#### Scenario: Выбор из списка с поиском
- **WHEN** пользователь вводит часть названия метки
- **THEN** показываются подходящие метки пользователя для выбора
#### Scenario: Снятие метки
- **WHEN** пользователь убирает метку с фото
- **THEN** метка отвязывается от фото
@@ -316,10 +316,69 @@ Ctrl/Cmd-клик — добавить или убрать, Shift-клик —
- **WHEN** пользователь открывает обзор альбомов
- **THEN** правая панель не отображается
### Requirement: Действия в режиме просмотра
В режиме просмотра правая панель SHALL содержать раздел «Действия» со строками-пунктами
(иконка + подпись) «Скачать», «Переместить», «Копировать» и «Удалить» (удаление — по правам).
Верхняя панель просмотра SHALL не дублировать скачивание и удаление.
#### Scenario: Действия в просмотре
- **WHEN** открыт режим просмотра фото
- **THEN** раздел «Действия» содержит «Скачать», «Переместить», «Копировать» и «Удалить», а в верхней панели этих кнопок нет
#### Scenario: Удаление из панели
- **WHEN** пользователь удаляет фото из раздела «Действия»
- **THEN** открывается подтверждение, и после удаления просмотр переходит к соседнему фото
### Requirement: Контекстное меню фото
Правый клик по фото в сетке и по изображению в режиме просмотра SHALL открывать контекстное
меню с пунктами раздела «Действия» («Скачать», «Переместить», «Копировать», «Удалить»; в сетке
также «Открыть (просмотр)»), выполняющими те же действия.
#### Scenario: Меню в сетке
- **WHEN** пользователь кликает правой кнопкой по фото в сетке
- **THEN** открывается меню с пунктами «Открыть (просмотр)», «Скачать», «Переместить», «Копировать», «Удалить»
#### Scenario: Меню в просмотре
- **WHEN** пользователь кликает правой кнопкой по изображению в режиме просмотра
- **THEN** открывается меню с пунктами «Скачать», «Переместить», «Копировать», «Удалить»
### Requirement: Контекстное меню альбома
Правый клик по альбому в рейле и по карточке на обзоре SHALL открывать меню с пунктами
«Переименовать» и «Удалить»; переименование SHALL открывать диалог, удаление — подтверждение.
#### Scenario: Меню альбома
- **WHEN** пользователь кликает правой кнопкой по альбому
- **THEN** открывается меню с «Переименовать» и «Удалить»
#### Scenario: Удаление альбома
- **WHEN** пользователь подтверждает удаление альбома
- **THEN** альбом удаляется, а его фото попадают в корзину
### Requirement: Счётчики в рейле
Рейл SHALL показывать число фото у каждого альбома и число элементов в корзине.
#### Scenario: Счётчики
- **WHEN** пользователь смотрит на рейл альбомов
- **THEN** у каждого альбома показано число фото, а у корзины — число удалённых фото
### Requirement: Метки в панели фото
Панель фото SHALL содержать раздел «Метки» с чипами меток фото и полем добавления, дающим
поиск по меткам пользователя и создание новых.
#### Scenario: Раздел «Метки»
- **WHEN** открыт просмотр или выбран один фото в сетке
- **THEN** в панели есть раздел «Метки» с текущими метками и полем добавления с поиском
### Requirement: Компактный верхний хром
Верхняя панель приложения и верхняя панель режима просмотра SHALL иметь минимальную высоту
(узкие отступы, иконочные органы управления), сохраняя все действия доступными.
(узкие отступы, иконочные органы управления), сохраняя все действия доступными. Верхняя панель
режима просмотра SHALL быть прозрачным оверлеем и SHALL не занимать место в раскладке,
увеличивая область фото.
#### Scenario: Компактные верхние панели
- **WHEN** открыт список альбомов или режим просмотра фото
- **THEN** верхняя панель занимает минимум высоты, а её действия (навигация, полный экран, скачивание, удаление) доступны
#### Scenario: Прозрачная панель просмотра
- **WHEN** открыт режим просмотра фото
- **THEN** верхняя панель нарисована поверх фото без фона и не уменьшает область изображения