Files
ds-galo/openspec/changes/lightroom-workspace-ui/specs/photos/spec.md
T
iu 61b45b2742 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)
2026-10-10 22:45:41 +03:00

119 lines
7.9 KiB
Markdown

# Spec Delta
## ADDED Requirements
### Requirement: Обложка по умолчанию
При обработке загруженного фото система SHALL назначать его обложкой альбома только если
обложка ещё не задана.
#### Scenario: Первое фото
- **WHEN** фото обработано, и у альбома нет обложки
- **THEN** система назначает это фото обложкой
#### Scenario: Обложка уже задана
- **WHEN** фото обработано, а обложка уже задана
- **THEN** система не меняет обложку
### Requirement: Переименование фото
Автор фото, владелец и админ альбома SHALL иметь возможность изменить имя файла фото;
остальные SHALL получать отказ, а пустое имя SHALL отклоняться.
#### Scenario: Переименование автором
- **WHEN** автор меняет имя файла своего фото на непустое
- **THEN** система сохраняет новое имя и транслирует изменение подписчикам альбома
#### Scenario: Пустое имя
- **WHEN** пользователь подтверждает пустое имя файла
- **THEN** система отклоняет запрос
#### Scenario: Нет прав
- **WHEN** участник без прав переименовывает чужое фото
- **THEN** система отклоняет запрос
### Requirement: Ограничение размера загрузки
Система SHALL ограничивать размер загружаемых файлов и SHALL отвечать понятной ошибкой при
превышении лимита, а не ошибкой разбора формы.
#### Scenario: Превышение лимита
- **WHEN** загружаемый файл превышает максимальный размер
- **THEN** система отклоняет загрузку с явной ошибкой о превышении размера
#### Scenario: Файл в пределах лимита
- **WHEN** файл не превышает лимит
- **THEN** система принимает multipart-загрузку и обрабатывает фото
### Requirement: Метаданные фото (EXIF)
При загрузке фото система SHALL извлекать распознаваемые EXIF-атрибуты и SHALL возвращать их
вместе с данными фото; при отсутствии метаданных SHALL возвращать пустой набор.
#### Scenario: Фото с метаданными
- **WHEN** загружается фото с EXIF
- **THEN** данные фото содержат извлечённые атрибуты (камера, экспозиция, ISO, дата съёмки и др.)
#### Scenario: Фото без метаданных
- **WHEN** загружается фото без читаемых EXIF
- **THEN** данные фото не содержат атрибутов, а загрузка выполняется успешно
### Requirement: Перенос и копирование фото между альбомами
Пользователь с правом удаления в исходном альбоме и правом загрузки в целевом SHALL иметь
возможность перенести или скопировать фото в другой альбом. Операции SHALL изменять только
метаданные и SHALL не изменять объекты хранилища.
#### Scenario: Перенос
- **WHEN** пользователь переносит фото в другой альбом
- **THEN** фото исчезает из исходного альбома и появляется в целевом, объекты S3 не меняются
#### Scenario: Копирование
- **WHEN** пользователь копирует фото в другой альбом
- **THEN** в целевом альбоме появляется новая запись, ссылающаяся на те же объекты S3
### Requirement: Корзина фото
Удаление фото SHALL помещать его в корзину (мягкое удаление). Восстановление SHALL возвращать
фото в его альбом. Окончательное удаление SHALL удалять запись, а объекты хранилища SHALL
удаляться только когда на них не остаётся ссылок ни в одном альбоме.
#### Scenario: Удаление в корзину
- **WHEN** пользователь удаляет фото
- **THEN** фото пропадает из альбома и появляется в корзине
#### Scenario: Восстановление
- **WHEN** пользователь восстанавливает фото из корзины
- **THEN** фото возвращается в свой альбом
#### Scenario: Окончательное удаление без ссылок
- **WHEN** пользователь удаляет фото из корзины навсегда, и на те же объекты нет ссылок в других альбомах
- **THEN** запись и объекты хранилища удаляются
#### Scenario: Окончательное удаление при наличии ссылок
- **WHEN** на объект хранилища ссылается копия в другом альбоме
- **THEN** запись удаляется, а объект хранилища сохраняется
### Requirement: Удаление альбома и восстановление фото без альбома
Удаление альбома SHALL помещать его фото в корзину, не удаляя объекты хранилища. Если при
восстановлении фото его альбом отсутствует, система SHALL предложить выбрать существующий
альбом или создать новый.
#### Scenario: Удаление альбома
- **WHEN** владелец удаляет альбом
- **THEN** его фото оказываются в корзине без привязки к альбому, файлы сохраняются
#### Scenario: Восстановление без альбома
- **WHEN** пользователь восстанавливает фото, у которого нет альбома
- **THEN** система предлагает выбрать альбом или создать новый и восстанавливает фото в него
### Requirement: Метки фото
Пользователь SHALL присваивать фото свои метки (область — пользователь): создавать новые и
выбирать из списка с поиском; метки SHALL отдаваться вместе с фото и сниматься.
#### Scenario: Создание метки
- **WHEN** пользователь вводит новую метку и подтверждает
- **THEN** метка создаётся и привязывается к фото
#### Scenario: Выбор из списка с поиском
- **WHEN** пользователь вводит часть названия метки
- **THEN** показываются подходящие метки пользователя для выбора
#### Scenario: Снятие метки
- **WHEN** пользователь убирает метку с фото
- **THEN** метка отвязывается от фото