# Wishpad: UI specification

## 1. Цель документа

UI specification описывает пользовательские поверхности Wishpad: публичную часть, кабинет владельца, админку и mobile app.

Основной production-домен проекта: `wishpad.me`.

Документ дополняет `SPEC.md` и отвечает на вопросы:

- какие экраны нужны;
- что пользователь видит на каждом экране;
- какие состояния должны быть предусмотрены;
- какие UI-принципы используются;
- как не испортить владельцу сюрприз через интерфейс.

## 2. Общий UI-подход

Приложение строится как mobile-first.

Главные принципы:

- сначала удобный мобильный интерфейс;
- desktop расширяет mobile layout, а не существует отдельно;
- основной подход для user-facing части: responsive design;
- adaptive-поведение допускается точечно для сложных компонентов;
- вся user-facing часть должна работать как один интерфейс на web mobile, mobile app и desktop;
- public guest UI и owner UI не должны иметь отдельные версии под web/mobile/app;
- mobile web и mobile app должны иметь одинаковый UX, визуальную модель и пользовательские сценарии;
- mobile app не должен вводить отдельную навигацию или отличающиеся сценарии без явной причины;
- публичная часть должна быть тёплой, лёгкой и понятной;
- кабинет владельца должен быть спокойным и практичным;
- mobile app предназначен только для user-facing части: public guest UI и owner UI;
- админка не входит в mobile app;
- админка не обязана поддерживать мобильный телефонный viewport;
- минимальная целевая ширина админки: tablet viewport;
- админка должна быть плотной, сканируемой и рабочей;
- интерфейс не должен показывать владельцу статусы бронирования его списка;
- PrimeVue используется как единая UI-библиотека;
- Lucide используется как единый набор иконок во всех частях frontend;
- PrimeIcons не используется как самостоятельный icon set;
- публичная часть не должна выглядеть как админка.

## 3. Поверхности приложения

### Public guest UI

Публичная часть для гостей, которые открыли wishlist по ссылке.

Public guest UI использует тот же responsive интерфейс на mobile web, mobile app и desktop. Различается только количество доступного пространства, а не сценарии или информационная архитектура.

Основные задачи:

- посмотреть список подарков;
- понять, какие подарки свободны или забронированы;
- забронировать подарок;
- отменить свою бронь;
- увидеть, что подарок забронирован именно текущим гостем, если совпала guest cookie.

### Owner UI

Кабинет владельца списка.

Owner UI использует тот же responsive подход, что и public guest UI: mobile web, mobile app и desktop не являются разными продуктами.

Основные задачи:

- войти или зарегистрироваться;
- видеть свои wishlists;
- создать wishlist;
- редактировать wishlist;
- добавлять, редактировать и удалять подарки;
- скопировать публичную ссылку;
- понимать, что статусы бронирования скрыты намеренно.

### Admin UI

Закрытая админка для администратора.

Админка проектируется как tablet/desktop-first рабочий интерфейс. Она не обязана быть удобной на телефоне и не входит в mobile app.

Основные задачи:

- управлять пользователями;
- видеть все wishlists;
- управлять wishlists;
- видеть бронирования;
- видеть файловые логи;
- видеть failed jobs;
- выполнять действия поддержки и модерации.

### Mobile app

Mobile app использует тот же user-facing frontend через Capacitor.

Mobile app не имеет отдельного дизайна для публичной части и кабинета владельца. Он использует тот же responsive UI, что и web mobile.

Админка в mobile app не включается.

Основные задачи:

- открыть приложение;
- авторизоваться;
- работать с wishlists владельца;
- открывать публичные списки;
- бронировать и отменять брони;
- получать тот же UX, что и в mobile web.

## 4. Навигационная модель

### Public guest navigation

Публичная страница не требует глобальной навигации.

Public guest navigation одинаковая для mobile web, mobile app и desktop. На широком экране можно показать больше воздуха и вспомогательных деталей, но нельзя менять основной сценарий.

Минимальная структура:

- header с названием wishlist;
- список подарков;
- действия на карточках подарков;
- footer с минимальными ссылками на документы.

### Owner navigation

Owner navigation единая для mobile web, mobile app и desktop.

Основные разделы:

- dashboard со списками;
- настройки;
- выход.

Responsive behavior:

- на узких экранах навигация компактная и не забирает место у контента;
- на широких экранах та же навигация может раскрываться шире или становиться более заметной;
- desktop не должен добавлять отдельную информационную архитектуру;
- пользовательские сценарии на desktop, mobile web и mobile app должны совпадать.

### Admin navigation

Админка использует sidebar navigation и рассчитана минимум на tablet viewport.

Разделы:

- dashboard;
- users;
- wishlists;
- logs;
- failed jobs;
- settings.

## 5. Основные экраны MVP

### Auth screen

Маршрут:

- `/auth`

Назначение:

- вход;
- регистрация;
- восстановление доступа в будущем.

MVP-состояния:

- login mode;
- register mode;
- loading;
- validation errors;
- wrong credentials;
- blocked/deactivated account;
- required password change for first admin.

### Owner dashboard

Маршрут:

- `/dashboard`

Назначение:

- показать краткое состояние аккаунта и активности владельца;
- показать все wishlists владельца;
- создать новый wishlist;
- перейти к редактированию wishlist;
- скопировать публичную ссылку;
- деактивировать wishlist;
- подсказать создать первый wishlist, если списков нет.

Состояния:

- loading;
- empty state;
- list;
- error;
- deactivated wishlist.

### Owner wishlist edit

Маршрут:

- `/wishlists/:wishlistUuid/edit`

Назначение:

- редактировать название и описание wishlist;
- управлять подарками;
- импортировать метаданные подарка по ссылке;
- копировать публичную ссылку.

Важное правило:

- владелец не видит статусы бронирования подарков.
- рядом с полем описания wishlist нужно явно показать, что это описание будет видно гостям по публичной ссылке.

### Owner settings

Маршрут:

- `/settings`

Назначение:

- включить или отключить email-уведомления;
- посмотреть email аккаунта;
- выбрать язык интерфейса, когда появятся дополнительные языки;
- выбрать форматы даты, времени и чисел;
- изменить пароль.

### Public wishlist

Маршрут:

- `/wishlist/:publicToken`

Назначение:

- показать wishlist гостю;
- дать забронировать свободный подарок;
- показать занятые подарки;
- показать имя гостя, который забронировал подарок;
- показать "забронировано вами", если бронь принадлежит текущему guest cookie.

Состояния:

- loading;
- not found;
- inactive/deactivated;
- available gift;
- reserved gift;
- reserved by current guest;
- reservation conflict;
- owner-safe view без статусов бронирования.

### Admin dashboard

Маршрут:

- `/admin`

Назначение:

- дать администратору быстрый обзор системы;
- показать ключевую статистику;
- показать ссылки на users, wishlists, logs и failed jobs.

Статистика MVP:

- всего пользователей;
- активных пользователей;
- деактивированных пользователей;
- всего wishlists;
- активных wishlists;
- деактивированных wishlists;
- всего подарков;
- всего активных броней;
- failed jobs.

Принцип:

- dashboard должен помогать быстро понять состояние системы;
- статистика не должна превращаться в полноценную аналитику;
- все карточки статистики должны вести в соответствующие разделы админки с применённым фильтром, если это уместно.

### Admin users

Маршруты:

- `/admin/users`
- `/admin/users/:userUuid`

Назначение:

- список пользователей;
- фильтрация, сортировка и пагинация;
- карточка пользователя;
- блокировка;
- деактивация;
- hard delete, если доступно администратору.

### Admin wishlists

Маршруты:

- `/admin/wishlists`
- `/admin/wishlists/:wishlistUuid`

Назначение:

- список всех wishlists;
- фильтрация, сортировка и пагинация;
- карточка wishlist;
- просмотр подарков;
- просмотр бронирований;
- деактивация или удаление wishlist.

### Admin logs

Маршрут:

- `/admin/logs`

Назначение:

- просмотр файловых логов;
- фильтрация, сортировка и пагинация;
- переход из карточек пользователей/wishlists/items/reservations к связанным логам;
- архивация;
- ручное удаление.

### Admin failed jobs

Маршрут:

- `/admin/failed-jobs`

Назначение:

- просмотр failed jobs;
- фильтрация, сортировка и пагинация;
- просмотр ошибки выполнения;
- повторный запуск задачи, если это поддерживается backend;
- быстрый переход к связанным логам, если связь доступна.

### Admin settings

Маршрут:

- `/admin/settings`

Назначение:

- управлять включёнными языками приложения;
- задавать глобальные форматы дат, времени, чисел и валюты для языка;
- хранить настройки, которые применяются по умолчанию, если пользователь не задал свои.

## 6. Детализация Auth Screen

Auth screen — входная точка для владельца списка и администратора. Экран должен быть компактным и понятным, без превращения в landing page.

### Layout-Spec И Wireframes

Layout-spec:

- auth screen не является landing page;
- экран показывает компактный брендовый блок Wishpad и форму;
- login/register переключаются через segmented control;
- восстановление доступа в MVP не реализуется как полноценный flow, но место под future link можно оставить;
- после успешного входа пользователь попадает на owner dashboard, если нет forced password change;
- если `mustChangePassword = true`, пользователь попадает в forced password change state до продолжения работы.

Mobile layout:

```text
[Auth page]
  [Wishpad logo/marker]
  Wishpad
  Short friendly subtitle

  [Segmented control]
    [Вход] [Регистрация]

  [Email input]
  [Password input]

  Login mode:
    [Войти]

  Register mode:
    [Зарегистрироваться]

  [Future forgot password link, disabled/secondary]
```

Desktop layout:

```text
[Centered auth container]

  [Wishpad logo/marker]
  Wishpad
  Short friendly subtitle

  [Auth form panel]
    [Вход] [Регистрация]
    [Email]
    [Password]
    [Primary action]
    [Secondary/future link]
```

Forced password change layout:

```text
[Centered auth container]
  Нужно сменить пароль
  Короткое объяснение

  [Current password]
  [New password]
  [Repeat new password]

  [Сменить пароль]
```

Error states:

```text
[Inline field error]
  Email обязателен

[Form-level error]
  Неверный email или пароль

[Blocked/deactivated state]
  Аккаунт недоступен
  [Contact/support text, future-safe]
```

PrimeVue components:

- `InputText`;
- `Password`;
- `Button`;
- `Message`;
- `Toast`;
- `ProgressSpinner` or button loading state;
- segmented control can be built with `SelectButton` or local tabs/segmented component.

## 7. Детализация Публичной Страницы

Публичная страница wishlist — основной guest-facing экран. Она должна быть простой, mobile-first и одинаковой по сценарию для mobile web, mobile app и desktop.

### Структура Страницы

Экран состоит из:

- compact header;
- названия wishlist;
- описания wishlist, если оно заполнено владельцем;
- количества подарков в списке;
- действия `Поделиться` для гостей и owner-safe view, если browser/mobile platform поддерживает share API;
- списка подарков;
- footer с минимальными ссылками на пользовательские документы.

На публичной странице не нужны поиск, фильтры или дополнительные режимы просмотра.

### Layout-Spec И Wireframes

Публичная страница детализируется как mobile-first экран.

Mobile layout:

```text
[Compact header]
  Wishpad marker                 [Share]
  Wishlist title
  Wishlist description, if any
  Gift count

[Owner-safe notice, only for owner-safe view]

[Gift list]
  [Gift card collapsed]
  [Gift card collapsed]
  [Gift card expanded, only one at a time]

[Footer links]
```

Collapsed gift card:

```text
[Image, if exists]  Gift title                 [Chevron]
                    Price + currency, if > 0
                    [Reservation badge, if reserved]
```

Collapsed gift card without image:

```text
Gift title                                      [Chevron]
Price + currency, if > 0
[Reservation badge, if reserved]
```

Expanded gift card:

```text
[Image, if exists]
Gift title                                      [Chevron up]
Price + currency, if > 0
[Reservation badge, if reserved]

Description, if any
Product URL / readable host, if any
[Open marketplace/link button, if URL exists]

[Primary action area]
  Available gift:        [Забронировать]
  Reserved by me:        [Отменить бронь]
  Reserved by another:   no reservation action
  Owner-safe view:       no reservation action
```

Reservation bottom sheet on mobile:

```text
[Bottom sheet]
  Забронировать подарок
  Gift title

  [Имя]
  [Input]

  [Cancel]                         [Забронировать]
```

Cancel confirmation:

```text
[Dialog / bottom confirmation]
  Отменить бронь?
  Вы уверены, что хотите отменить бронь?

  [Нет]                            [Отменить бронь]
```

Owner-safe public view:

```text
[Compact header]
  Wishpad marker                 [Share]
  Wishlist title
  Wishlist description, if any
  Gift count

[Notice]
  Статусы бронирования скрыты, чтобы не испортить сюрприз

[Gift list]
  [Gift card without reservation badge/actions]
  [Gift card without reservation badge/actions]

[Footer links]
```

Desktop layout:

```text
[Centered page container]

[Header row]
  Wishpad marker
  Wishlist title
  [Share]

[Description]
[Gift count]
[Owner-safe notice, if needed]

[Gift grid/list]
  [Gift card]        [Gift card]
  [Gift card]        [Gift card]

[Footer links]
```

Desktop может использовать две колонки для карточек, если раскрытая карточка остаётся читаемой. При раскрытии карточки остальные карточки не должны прыгать хаотично; предпочтительно сохранять устойчивую сетку или использовать single-column list на средних ширинах.

### Сортировка Подарков

Подарки отображаются в порядке, который задал владелец.

Если владелец не менял порядок вручную, порядок соответствует добавлению подарков в список.

### Карточка Подарка

Карточка подарка в списке показывает краткую информацию:

- изображение, если есть `image_url`;
- название;
- цену с валютой, если `price` больше `0`;
- бейдж бронирования;
- визуальный признак, что карточку можно раскрыть.

Если `image_url` отсутствует, image area не показывается. Остальная доступная информация остаётся на своих местах.

Если изображение по `image_url` не загрузилось, image area скрывается так же, как при отсутствующем изображении.

Если `price = 0`, цена не показывается.

Раскрытая карточка показывает подробности:

- описание подарка, если оно заполнено;
- ссылку на магазин или страницу товара, если есть `url`;
- кнопку маркетплейса, если frontend может распознать домен ссылки;
- полное состояние бронирования;
- основное действие: забронировать, отменить свою бронь или открыть ссылку.

Для mobile-first поведения используется expandable gift card. Это раскрытие прямо в списке, без модального окна.

Ссылка на товар в подробностях выглядит как текстовая строка с URL или читаемым доменом.

Если домен ссылки распознан как маркетплейс, рядом показывается кнопка перехода с названием площадки, например `Открыть на Ozon`, `Открыть на Wildberries`, `Открыть на Яндекс Маркете` или `Открыть на Lamoda`.

Если домен не распознан, используется универсальная кнопка `Открыть ссылку`.

Ссылки открываются:

- в web — в новой вкладке;
- в mobile app — в in-app browser.

### Бейдж Бронирования

Статус бронирования показывается бейджем.

Для гостя возможны состояния:

- свободен: бейдж не показывается;
- чужая бронь: бейдж с Lucide `Lock` и именем гостя;
- своя бронь: бейдж с Lucide `Check` и именем гостя.

Если подарок забронирован, на нераскрытой карточке показывается один бейдж: иконка + имя гостя.

Если подарок забронирован текущим guest cookie, бейдж должен быть визуально отличим от бейджа чужой брони.

Цветовая индикация бронирования:

- бронь другого гостя — красный акцент;
- бронь текущего гостя — зелёный акцент.

Карточка, забронированная другим гостем, раскрывается полностью, но не показывает действие бронирования.

Одновременно может быть раскрыта только одна gift card. При раскрытии новой карточки предыдущая сворачивается.

### Бронирование

Flow бронирования:

1. Гость раскрывает карточку свободного подарка.
2. Гость нажимает `Забронировать`.
3. Интерфейс показывает форму имени в адаптивном reservation dialog.
4. Гость вводит имя.
5. Frontend отправляет запрос бронирования.
6. После успеха frontend показывает toast.
7. Карточка сворачивается и в кратком состоянии показывает `Забронировано вами`.

Имя гостя запоминается локально в текущем браузере/app storage и подставляется при следующем бронировании.

Reservation dialog — это один компонент с адаптивным представлением:

- на mobile открывается как bottom sheet;
- на desktop открывается как modal dialog.

### Отмена Брони

Flow отмены:

1. Гость раскрывает карточку подарка со статусом `Забронировано вами`.
2. Гость нажимает `Отменить бронь`.
3. Интерфейс показывает confirmation dialog.
4. После подтверждения frontend отправляет запрос отмены.
5. После успеха frontend показывает toast.
6. Карточка сворачивается и возвращается в состояние `Свободен`.

Если guest cookie не совпадает с бронью, кнопка отмены не показывается.

### Представление Для Владельца

Если публичную ссылку открывает владелец wishlist, страница показывает:

- название wishlist;
- описание wishlist;
- список подарков;
- цены, если они больше `0`;
- ссылки на подарки;
- действие `Поделиться`;
- нейтральное уведомление о том, что статусы бронирования скрыты.

В owner-safe view нельзя показывать:

- бейдж бронирования;
- имя забронировавшего;
- статус `Свободен`;
- статус `Забронировано`;
- статус `Забронировано вами`;
- кнопки бронирования или отмены брони.

### Состояния Страницы

Публичная страница должна поддерживать:

- loading;
- skeleton loading для карточек подарков;
- empty wishlist;
- wishlist not found;
- inactive/deactivated wishlist;
- network error;
- reservation conflict;
- successful reservation;
- successful cancellation;
- owner-safe view.

При `409 Conflict` после попытки бронирования интерфейс должен обновить карточку и показать понятное сообщение, что подарок уже успели забронировать.

### Responsive-Поведение

Mobile:

- список в одну колонку;
- карточки компактные;
- подробности раскрываются внутри карточки;
- одновременно может быть раскрыта только одна gift card;
- форма бронирования открывается как bottom sheet;
- touch targets не меньше 44px по высоте;
- форма бронирования должна быть удобна для ввода одной рукой.

Desktop:

- можно использовать более широкую сетку или две колонки, если карточки остаются читаемыми;
- сценарий раскрытия и бронирования остаётся таким же, как на mobile;
- одновременно может быть раскрыта только одна gift card;
- форма бронирования открывается как modal dialog;
- desktop не добавляет отдельные фильтры, поиск или другие режимы.

### Иконки

Для публичной страницы используются Lucide icons:

- чужая бронь: `Lock`;
- своя бронь: `Check`;
- ссылка на товар: `Link`;
- поделиться: `Share2`;
- раскрыть/свернуть карточку: `ChevronDown` с анимацией переворачивания.

Состояние `Свободен` не имеет отдельной иконки или бейджа.

### Микротексты

Базовые тексты:

- `Забронировано`;
- `Забронировано вами`;
- `Забронировать`;
- `Отменить бронь`;
- `Открыть ссылку`;
- `Поделиться`;
- `Подарок уже забронирован`;
- `Статусы бронирования скрыты, чтобы не испортить сюрприз`;
- `В этом списке пока нет подарков`;
- `Список недоступен`.

## 8. Детализация Owner Dashboard

Owner dashboard — стартовый экран владельца после входа. Он должен быстро показывать состояние аккаунта и давать доступ к основным действиям со списками.

### Структура Страницы

Экран состоит из:

- compact header;
- краткого состояния владельца;
- подсказки о скрытых статусах бронирования;
- переключателя `Активные` / `Деактивированные`;
- списка wishlist cards;
- действия создания wishlist.

Настройки аккаунта не дублируются на dashboard и доступны через навигацию.

Если пользователь имеет роль `admin`, в навигации показывается пункт перехода в админку.

### Layout-Spec И Wireframes

Owner dashboard детализируется как mobile-first рабочий экран владельца.

Mobile layout:

```text
[Compact top bar]
  Wishpad / Dashboard             [Account/Menu]

[Owner summary]
  Всего списков: N
  Активные: N       Деактивированные: N
  Подарков: N

[Spoiler notice]
  Статусы бронирования скрыты, чтобы не испортить сюрприз

[Segmented control]
  [Активные] [Деактивированные]

[Wishlist list]
  [Wishlist card]
  [Wishlist card]
  [Wishlist card]

[Bottom navigation]
  [Dashboard] [Settings]

[FAB +]
```

Mobile wishlist card:

```text
[Wishlist title]                         [Actions menu]
[Description, max 2 lines]

[Status chip]   [Gift count]   [Created date]

[Inline quick actions]
  [Copy] [Share]
```

Mobile actions menu:

```text
[Menu]
  Редактировать
  Скопировать ссылку
  Поделиться
  Деактивировать / Восстановить
  Удалить
```

Desktop layout:

```text
[Owner shell]
  [Sidebar]
    Wishpad
    Dashboard
    Settings
    Admin, if admin
    Logout

  [Main content]
    [Header row]
      Dashboard                         [Создать список]

    [Owner summary row]
      [Всего списков] [Активные] [Деактивированные] [Подарков]

    [Spoiler notice]

    [Segmented control]
      [Активные] [Деактивированные]

    [Wishlist grid/list]
      [Wishlist card] [Wishlist card]
      [Wishlist card] [Wishlist card]
```

Empty state:

```text
[Empty state]
  У вас пока нет списков
  Создайте первый wishlist и поделитесь ссылкой с гостями

  [Создать список]
```

Deactivated empty state:

```text
[Empty state]
  Деактивированных списков нет
```

Confirmation dialogs:

```text
[Deactivate confirmation]
  Деактивировать список?
  Публичная ссылка перестанет открывать список для гостей.
  [Отмена]                         [Деактивировать]

[Delete confirmation]
  Удалить список навсегда?
  Список, подарки и связанные брони будут удалены без восстановления.
  [Отмена]                         [Удалить]
```

### Краткое Состояние

Dashboard показывает:

- всего списков;
- количество активных списков;
- количество деактивированных списков;
- общее количество подарков во всех списках.

Статусы бронирования, количество броней и любые данные о том, что выбрали гости, владельцу не показываются.

### Список Wishlists

Списки отображаются карточками.

Mobile:

- карточки идут в одну колонку;
- создание нового wishlist доступно через floating action button.

Desktop:

- используются те же карточки в более широкой сетке или компактном списке;
- создание нового wishlist доступно кнопкой `Создать список` в верхней части экрана.

FAB на mobile:

- круглая кнопка с Lucide `Plus`;
- закреплена внизу справа;
- не перекрывает важный контент;
- открывает flow создания wishlist.

### Карточка Wishlist

Карточка wishlist показывает:

- название;
- описание, если заполнено, обрезанное до 2 строк;
- количество подарков;
- статус: активен или деактивирован;
- дату создания;
- действия копирования и шаринга публичной ссылки.

Карточка не показывает:

- статусы бронирования подарков;
- количество бронирований;
- имена гостей;
- длинный URL публичной ссылки текстом.

### Действия На Карточке

Основные действия:

- редактировать wishlist;
- скопировать публичную ссылку;
- поделиться публичной ссылкой, если browser/mobile platform поддерживает share API;
- деактивировать wishlist;
- восстановить wishlist, если он деактивирован;
- удалить wishlist навсегда.

Описание полностью показывается в detail/edit view wishlist.

Деактивация, восстановление и удаление должны требовать confirmation dialog.

Confirmation для деактивации должен предупреждать, что публичная ссылка перестанет открывать список для гостей, пока список деактивирован.

Удаление — отдельное irreversible действие. Confirmation для удаления должен явно предупреждать, что список, подарки и связанные брони будут удалены навсегда.

После всех owner dashboard действий frontend показывает toast: создание, копирование ссылки, share, редактирование, деактивация, восстановление и удаление.

### Иконки

Для owner dashboard используются Lucide icons:

- создать: `Plus`;
- редактировать: `ClipboardPenLine`, fallback `Pencil`, если выбранная версия Lucide не содержит clipboard-вариант;
- скопировать ссылку: `Copy`;
- поделиться: `Share2`;
- деактивировать: `EyeOff`;
- восстановить: `RotateCcw`;
- удалить: `Trash2`;
- перейти в админку: `KeyRound`;
- меню дополнительных действий: `Ellipsis`.

### Сегменты И Сортировка

Переключатель `Активные` / `Деактивированные` разделяет списки по состоянию.

В каждом сегменте списки сортируются по дате создания: последние созданные сверху.

Поиск и фильтры для owner dashboard в MVP не нужны.

### Empty States

Если у владельца нет списков:

- показывается пустое состояние;
- текст объясняет, что можно создать первый wishlist;
- основное действие ведёт к созданию wishlist.

Если выбран сегмент `Деактивированные`, а деактивированных списков нет, показывается отдельное нейтральное empty state.

### Лимит Списков

Если пользователь достиг лимита 256 wishlists:

- действие создания блокируется;
- frontend показывает понятное сообщение о достижении лимита;
- существующие списки остаются доступны для просмотра и редактирования.

### Микротексты

Базовые тексты:

- `Создать список`;
- `Активные`;
- `Деактивированные`;
- `Скопировать ссылку`;
- `Поделиться`;
- `Редактировать`;
- `Деактивировать`;
- `Восстановить`;
- `Удалить`;
- `Публичная ссылка перестанет открывать список для гостей`;
- `Список, подарки и связанные брони будут удалены навсегда`;
- `Статусы бронирования скрыты, чтобы не испортить сюрприз`;
- `У вас пока нет списков`;
- `Создайте первый список подарков`;
- `Достигнут лимит списков`.

## 9. Детализация Gift Form И Metadata Import

Gift form используется и для создания, и для редактирования подарка.

### Представление Формы

Форма открывается адаптивно:

- mobile: fullscreen sheet по умолчанию;
- desktop: modal dialog по умолчанию.

Компонент формы остаётся единым. Различается только shell/presentation под размер экрана.

После успешного сохранения форма закрывается, список подарков обновляется, frontend показывает toast.

### Layout-Spec И Wireframes

Gift form детализируется как адаптивная форма с единым содержимым и разными shell-представлениями.

Mobile create layout:

```text
[Fullscreen sheet]
  [Top bar]
    [Close]  Новый подарок                 [Save]

  [Field]
    Название
    [Input]

  [Field]
    Описание
    [Textarea]

  [Field]
    Ссылка на товар
    [Input]
    [Metadata import state]

  [Metadata preview, if available]

  [Field]
    Изображение
    [Input]
    [Image preview, if image loads]

  [Price row]
    [Цена]              [Валюта]

  [Sticky bottom action]
    [Сохранить]
```

Mobile edit layout:

```text
[Fullscreen sheet]
  [Top bar]
    [Close]  Редактировать подарок         [Save]

  [Same form fields with current values]

  [Sticky bottom action]
    [Сохранить изменения]
```

Desktop modal layout:

```text
[Modal dialog]
  Новый подарок / Редактировать подарок
  [Close]

  [Two-column capable form area]
    Left:
      Название
      Описание
      Ссылка на товар
      Metadata preview

    Right:
      Изображение
      Image preview
      Цена
      Валюта

  [Footer actions]
    [Отмена]                         [Сохранить]
```

На desktop форма может оставаться в одной колонке, если так проще и чище на первом этапе. Две колонки допустимы только если они не ухудшают чтение metadata preview и image preview.

Metadata import states:

```text
[Under product URL field]
  Idle:
    no extra block

  Loading:
    [Spinner] Ищем данные по ссылке

  Success:
    [Metadata preview]
      [Image thumbnail, if found]
      Title
      Description, max 2 lines
      Price + currency, if found
      Marketplace/domain

      [Применить] [Пропустить]

  Empty/error:
    Toast: Не удалось получить данные по ссылке
    Form remains editable
```

Image preview:

```text
[Image URL input]

[Preview area, only if image loads]
  [Image]
```

Validation state:

```text
[Название]
[Input with error state]
Название обязательно

[Цена]
[Input with error state]
Цена не может быть отрицательной
```

### Поля Формы

Поля:

- `Название`: input, обязательное;
- `Описание`: textarea, необязательное;
- `Ссылка на товар`: input, необязательное;
- `Изображение`: input для `image_url`, необязательное;
- `Цена`: numeric input, обязательное, по умолчанию `0`, допускает дробные значения до 2 знаков после запятой;
- `Валюта`: select из активных валют, добавленных в систему, с возможностью ввести custom currency code;
- `Порядок`: не показывается как обычное поле формы в MVP, управление порядком выполняется через список подарков.

Список валют берётся из системных настроек. Валюта по умолчанию берётся из глобальных настроек языка, которые задал администратор. Если metadata import вернул валюту, она подставляется в форму и может быть изменена владельцем.

Если metadata import вернул валюту, которой нет в системном списке, она подставляется как custom currency code и не добавляется в глобальный справочник.

Если владелец пытается сохранить форму без `Название`, ошибка показывается inline под полем, а не toast.

### Image Preview

Поле `Изображение` принимает ссылку на изображение.

После вставки `image_url` frontend сразу пытается показать preview.

Если изображение не загрузилось:

- preview не показывается;
- frontend показывает toast с понятным сообщением;
- владелец может оставить ссылку, заменить её или очистить поле.

### Metadata Import Flow

Импорт запускается автоматически после вставки ссылки в поле `Ссылка на товар` с debounce.

Flow:

1. Владелец вставляет ссылку на товар.
2. Frontend debounce-ит ввод и отправляет запрос metadata import.
3. Пока запрос выполняется, форма показывает loading state рядом с полем ссылки.
4. Backend возвращает найденные данные без сохранения подарка.
5. Frontend показывает preview найденных данных.
6. Владелец нажимает `Применить` или `Пропустить`.
7. При `Применить` найденные данные один раз заполняют поля формы.
8. Владелец может отредактировать любые поля.
9. Подарок сохраняется только после нажатия `Сохранить`.

Если владелец меняет ссылку на другую, metadata import запускается заново.

Preview может содержать:

- название;
- описание;
- изображение;
- цену;
- валюту;
- домен или marketplace label.

### Правила Применения Preview

Preview не сохраняет подарок и не меняет данные на backend.

При применении preview найденные данные могут перезаписать текущие значения формы один раз для текущей ссылки.

Если владелец изменил поля после применения preview, повторный import для той же ссылки не должен молча перетирать данные. Нужно снова показать preview и ждать действия владельца.

Если найдено только часть данных, применяются только найденные поля.

Если цена не найдена, остаётся `0`.

Если валюта не найдена, остаётся валюта по умолчанию.

Если валюта найдена, но отсутствует в системном списке, она применяется как custom currency code.

### Ошибки Импорта

Если импорт не нашёл данные, сайт заблокировал запрос или произошла ошибка:

- frontend показывает нейтральный toast;
- форма остаётся доступной для ручного заполнения;
- сохранение подарка не блокируется.

Импорт метаданных не должен выглядеть как обязательный шаг.

### Сохранение И Редактирование

Одна форма используется для:

- создания подарка;
- редактирования подарка.

В режиме редактирования форма открывается с текущими значениями подарка.

Удаление подарка не находится внутри gift form. Удаление выполняется из списка подарков и из detail view, всегда требует confirmation dialog.

### Микротексты

Базовые тексты:

- `Название`;
- `Описание`;
- `Ссылка на товар`;
- `Изображение`;
- `Цена`;
- `Валюта`;
- `Сохранить`;
- `Отмена`;
- `Мы нашли данные по ссылке`;
- `Применить`;
- `Пропустить`;
- `Не удалось получить данные по ссылке`;
- `Изображение не загрузилось`;
- `Подарок сохранён`;
- `Подарок удалён`.

## 10. Детализация Owner Settings

Owner settings — экран настроек аккаунта владельца.

### Структура Страницы

Экран состоит из секций:

- `Профиль`;
- `Уведомления`;
- `Язык и форматы`;
- `Безопасность`;
- `Аккаунт`.

Logout не дублируется в settings и остаётся действием в навигации.

### Layout-Spec И Wireframes

Owner settings детализируется как набор компактных секций.

Mobile layout:

```text
[Compact top bar]
  Settings                         [Account/Menu]

[Settings sections]

  [Профиль]
    Email
    user@example.com

  [Уведомления]
    Email-уведомления              [Toggle]
    Брони и отмены                 [Toggle]
    Изменения списков              [Toggle]
    Изменения подарков             [Toggle]

  [Язык и форматы]
    Язык                           [Disabled select: Русский]
    Другие языки появятся позже
    Формат даты                    [Select]
    Формат времени                 [Select]
    Формат чисел                   [Select]

  [Безопасность]
    Старый пароль                  [Password]
    Новый пароль                   [Password]
    Повторите новый пароль         [Password]
    [Изменить пароль]

  [Аккаунт]
    [Деактивировать аккаунт]

[Bottom navigation]
  [Dashboard] [Settings]
```

Desktop layout:

```text
[Owner shell]
  [Sidebar]
    Wishpad
    Dashboard
    Settings
    Admin, if admin
    Logout

  [Main content]
    [Header row]
      Настройки

    [Settings content]
      [Профиль]
      [Уведомления]
      [Язык и форматы]
      [Безопасность]
      [Аккаунт]
```

Desktop может использовать одну широкую колонку с ограниченной шириной формы. Две колонки допустимы только для независимых секций, но dangerous zone не должна визуально теряться.

Notification section:

```text
[Уведомления]
  Email-уведомления                            [Toggle]

  [Child notification toggles]
    Брони и отмены                             [Toggle]
    Изменения списков                          [Toggle]
    Изменения подарков                         [Toggle]

  [Save state]
    saved automatically or after explicit save
```

Password change:

```text
[Безопасность]
  Старый пароль
  [Password input]

  Новый пароль
  [Password input]

  Повторите новый пароль
  [Password input]

  [Изменить пароль]
```

Deactivate account confirmation:

```text
[Confirmation dialog]
  Деактивировать аккаунт?

  Аккаунт станет неактивным.
  Публичные ссылки на ваши списки перестанут работать.

  [Отмена]                         [Деактивировать аккаунт]
```

Validation and feedback:

```text
[Inline field error]
  Новый пароль обязателен

[Toast]
  Настройки сохранены
  Пароль изменён
```

### Профиль

В MVP профиль показывает только email аккаунта.

Email:

- отображается readonly;
- не редактируется в MVP;
- смена email остаётся future flow и должна требовать подтверждения нового email.

Display name и avatar в MVP не добавляются.

### Уведомления

Секция содержит:

- master toggle `Email-уведомления`;
- дочерний toggle `Брони и отмены`;
- дочерний toggle `Изменения списков`;
- дочерний toggle `Изменения подарков`.

Правила:

- master toggle включает или выключает email-уведомления глобально;
- дочерние toggles доступны, когда master включён;
- если пользователь выключил все дочерние toggles, master автоматически выключается;
- если master выключен, дочерние toggles визуально disabled;
- изменения сохраняются через owner settings endpoint;
- после сохранения frontend показывает toast.

### Язык И Форматы

Язык:

- selector языка показывается в disabled-состоянии, пока в системе доступен только русский язык;
- рядом показывается короткая приписка, что другие языки появятся позже.

Timezone:

- ручной выбор timezone в owner settings для MVP не показывается;
- timezone определяется автоматически и хранится в профиле пользователя.

Форматы:

- пользователь может выбрать формат даты;
- пользователь может выбрать формат времени;
- пользователь может выбрать формат чисел.

Если пользователь не выбрал формат вручную, применяются глобальные настройки выбранного языка.

### Безопасность

Смена пароля в MVP:

- старый пароль;
- новый пароль;
- подтверждение нового пароля.

Ошибки полей показываются inline. После успешной смены пароля frontend показывает toast.

Future direction:

- заменить ручную смену пароля на flow с magic link на email.

### Forced Password Change

Если первому администратору нужно сменить временный пароль, приложение показывает forced password change flow сразу после входа.

Правила:

- пользователь не попадает в приложение, пока не сменит временный пароль;
- flow использует те же поля нового пароля и подтверждения;
- старый временный пароль можно не запрашивать повторно, если backend уже подтвердил вход;
- после успешной смены пароля пользователь попадает в приложение.

### Аккаунт

Секция аккаунта содержит dangerous zone для деактивации аккаунта.

Деактивация аккаунта:

- доступна владельцу из settings;
- требует confirmation dialog;
- confirmation предупреждает, что аккаунт станет неактивным, а публичные ссылки на списки перестанут работать;
- после подтверждения backend деактивирует аккаунт и связанные wishlists;
- frontend завершает сессию и показывает понятное состояние выхода.

Hard delete аккаунта из owner settings не выполняется. Физическое удаление происходит по правилам модели удаления аккаунта.

### Микротексты

Базовые тексты:

- `Профиль`;
- `Email`;
- `Уведомления`;
- `Email-уведомления`;
- `Брони и отмены`;
- `Изменения списков`;
- `Изменения подарков`;
- `Язык и форматы`;
- `Другие языки появятся позже`;
- `Формат даты`;
- `Формат времени`;
- `Формат чисел`;
- `Безопасность`;
- `Старый пароль`;
- `Новый пароль`;
- `Повторите новый пароль`;
- `Изменить пароль`;
- `Аккаунт`;
- `Деактивировать аккаунт`;
- `Публичные ссылки на ваши списки перестанут работать`;
- `Настройки сохранены`;
- `Пароль изменён`.

## 11. Детализация Owner Wishlist Edit

Owner wishlist edit — рабочий экран владельца для редактирования списка и управления подарками. Владелец не видит статусы бронирования подарков на этом экране.

### Layout-Spec И Wireframes

Mobile layout:

```text
[Compact top bar]
  [Back]  Редактировать список          [Actions/Menu]

[Wishlist form]
  Название
  [Input]

  Описание
  [Textarea]
  Это описание увидят гости по публичной ссылке

[Public link actions]
  [Скопировать ссылку] [Поделиться]

[Spoiler notice]
  Статусы бронирования скрыты, чтобы не испортить сюрприз

[Gift list header]
  Подарки                              [Add gift]

[Gift management list]
  [Gift management card]
  [Gift management card]

[Bottom navigation]
  [Dashboard] [Settings]
```

Gift management card:

```text
[Image, if exists]  Gift title                 [Actions menu]
                    Price + currency, if > 0
                    Description, max 2 lines

[No reservation status]
```

Gift actions menu:

```text
[Menu]
  Редактировать
  Удалить
```

Desktop layout:

```text
[Owner shell]
  [Sidebar]

  [Main content]
    [Header row]
      [Back] Редактировать список
      [Скопировать ссылку] [Поделиться]

    [Two-section layout]
      [Wishlist settings]
        Название
        Описание
        Description visibility hint

      [Gift management]
        Подарки                         [Добавить подарок]
        [Gift list]
```

Add/edit gift flow:

```text
[Add gift]
  Opens Gift form

[Edit gift]
  Opens Gift form with current values
```

Delete gift confirmation:

```text
[Confirmation dialog]
  Удалить подарок?
  Если подарок был забронирован, бронь также будет удалена.

  [Отмена]                         [Удалить]
```

Empty gifts state:

```text
[Empty state]
  В списке пока нет подарков
  Добавьте первый подарок, чтобы поделиться списком с гостями

  [Добавить подарок]
```

### Правила

- owner wishlist edit не показывает статусы бронирования;
- gift cards на этом экране не содержат reservation badge;
- описание wishlist сопровождается подсказкой, что его увидят гости;
- добавление и редактирование подарка используют Gift form;
- удаление подарка не выполняется из Gift form, а только из списка/деталки и требует confirmation dialog.

## 12. Детализация Admin Tables

Admin tables строятся на PrimeVue DataTable.

### Layout-Spec И Wireframes

Admin UI детализируется как tablet/desktop рабочий интерфейс. Phone viewport не поддерживается.

Admin shell:

```text
[Admin shell]
  [Collapsible sidebar]
    Wishpad Admin
    Dashboard
    Users
    Wishlists
    Logs
    Failed jobs
    Settings
    Back to Wishpad
    Logout

  [Main content]
    [Page header]
      Page title
      Primary action, if exists

    [Page content]
```

Admin dashboard:

```text
[Admin shell]
  [Sidebar]

  [Main content]
    [Header]
      Dashboard

    [Stats row]
      [Users] [Wishlists] [Gifts] [Reservations] [Failed jobs]

    [Quick navigation]
      [Users]
      [Wishlists]
      [Logs]
      [Failed jobs]
      [Settings]
```

Admin table page:

```text
[Admin shell]
  [Sidebar]

  [Main content]
    [Header row]
      Users / Wishlists / Logs / Failed jobs / Settings table
      [Primary action, if applicable]

    [Toolbar]
      [Main filters]
      [More filters toggle]
      [Clear filters]

    [Collapsible filters panel, if opened]
      [Filter field]
      [Filter field]
      [Date period]

    [DataTable]
      [Column] [Column] [Column] [Actions]
      [Row]
      [Row]

    [Pagination]
      Page N / total
```

Row actions menu:

```text
[Ellipsis menu]
  Открыть
  Редактировать, if applicable
  Деактивировать / Восстановить, if applicable
  Удалить, if applicable
  Открыть логи, if entity is logged
```

Admin detail page:

```text
[Admin shell]
  [Sidebar]

  [Main content]
    [Back button] Entity title
    [Status / meta row]

    [Details sections]
      Basic data
      Related data
      Actions
      Related logs link
```

Phone unsupported state:

```text
[Unsupported viewport]
  Откройте админку на планшете или компьютере
```

Dangerous action confirmation:

```text
[Confirmation dialog]
  Подтвердите действие
  Описание последствия действия

  [Отмена]                         [Подтвердить]
```

### Общий Паттерн

Все admin tables используют:

- PrimeVue DataTable;
- server-side pagination;
- server-side filtering;
- server-side sorting;
- `perPage = 25` по умолчанию;
- сортировку по клику на заголовок колонки;
- row click для перехода в карточку сущности;
- actions menu через Lucide `Ellipsis`;
- skeleton loading;
- empty state;
- error state с retry.

Поиск не входит в MVP. Search input и full-text search остаются future scope.

CSV/export не входит в MVP.

Bulk actions не входят в MVP. Для будущего можно рассмотреть bulk archive/delete для logs и bulk retry для failed jobs.

### Toolbar И Фильтры

Фильтры располагаются в toolbar над таблицей.

Для tablet/desktop используется toolbar + collapsible filters:

- основные фильтры видны сразу;
- дополнительные фильтры можно раскрыть;
- применённые фильтры должны быть видимы;
- должна быть команда очистки фильтров.

### Responsive-Поведение

Admin UI рассчитан минимум на tablet viewport.

Tablet:

- таблицы могут использовать horizontal scroll;
- часть второстепенных колонок может быть скрыта;
- row actions остаются доступными через actions menu.

Фильтры по датам в MVP используют только выбор периода. Presets вроде `Сегодня`, `7 дней`, `30 дней` остаются future scope.

Phone:

- admin UI показывает unsupported notice;
- пользователь получает рекомендацию открыть админку на планшете или desktop.

### Действия Строки

Действия строки не показываются отдельными кнопками в строке.

Используется actions menu:

- открыть карточку;
- редактировать, если применимо;
- деактивировать/восстановить/удалить, если применимо;
- открыть связанные логи, если сущность логируется.

### Таблица Users

Колонки MVP:

- email;
- роль;
- статус;
- уведомления;
- дата создания;
- действия.

Фильтры MVP:

- роль;
- статус: active, blocked, deactivated;
- email notifications enabled;
- дата создания как период.

Действия:

- открыть карточку пользователя;
- заблокировать;
- разблокировать;
- деактивировать;
- hard delete, если доступно администратору;
- открыть связанные логи.

Hard delete показывается как dangerous action внутри actions menu и требует confirmation dialog.

Future scope:

- настройка отображаемых колонок таблицы.

### Таблица Wishlists

Колонки MVP:

- название;
- владелец/email;
- статус;
- количество подарков;
- количество броней;
- дата создания;
- действия.

Фильтры MVP:

- статус: active, deactivated;
- владелец;
- дата создания как период;
- наличие броней.

Действия:

- открыть карточку wishlist;
- редактировать;
- деактивировать;
- восстановить;
- удалить;
- открыть связанные логи.

Удаление показывается как dangerous action внутри actions menu и требует confirmation dialog.

Hard-deleted wishlists не отображаются в таблице, потому что они физически удалены.

### Таблица Logs

Колонки MVP:

- время;
- уровень;
- событие;
- actor;
- entity;
- message;
- действия.

Фильтры MVP:

- уровень;
- событие;
- actor;
- entity;
- дата как период.

Действия:

- открыть подробности log entry;
- перейти к связанной сущности, если связь доступна;
- архивировать старые логи через отдельное действие;
- очистить старые логи через отдельное действие, если это поддерживается log viewer.

Удаление отдельной строки лога в MVP не делаем.

### Таблица Failed Jobs

Колонки MVP:

- время;
- queue;
- job/class;
- ошибка;
- attempts;
- действия.

Фильтры MVP:

- queue;
- job/class;
- дата как период;
- наличие ошибки отправки email.

Действия:

- открыть подробности ошибки;
- retry одной задачи;
- открыть связанные логи, если связь доступна.

Bulk retry не входит в MVP.

### Таблицы Settings

Languages table:

- locale;
- enabled;
- date format;
- time format;
- number format;
- default currency;
- actions.

В MVP русский язык нельзя удалить. Можно редактировать форматы и валюту по умолчанию.

Currencies table:

- code;
- symbol;
- title;
- enabled;
- actions.

Действия:

- создать валюту;
- редактировать язык или валюту;
- включить/выключить язык или валюту.

Отключение валюты влияет только на новые выборы в формах. Подарки, где валюта уже использована, остаются без изменений.

### Микротексты

Базовые тексты:

- `Фильтры`;
- `Очистить фильтры`;
- `Действия`;
- `Открыть`;
- `Редактировать`;
- `Открыть логи`;
- `Повторить задачу`;
- `Архивировать`;
- `Нет данных`;
- `Не удалось загрузить данные`;
- `Откройте админку на планшете или компьютере`.

## 13. Детализация Responsive Navigation

### Breakpoints

Frontend использует единые breakpoint-правила для web и mobile app:

- mobile: `< 768px`;
- tablet: `768px-1023px`;
- desktop: `>= 1024px`.

User-facing часть строится responsive-first. Это значит, что mobile web, mobile app и desktop используют одну навигационную модель и одни маршруты, а различается только плотность и расположение элементов.

Admin UI не оптимизируется под телефон. На phone viewport админка показывает unsupported notice вместо рабочих таблиц и форм.

### Public Guest Navigation

Публичная страница wishlist не имеет глобальной навигации.

Верх страницы:

- компактный header;
- название wishlist;
- краткое описание wishlist, если оно заполнено;
- share action, если share доступен в текущей среде.

Header должен быть компактным, потому что интерфейс mobile/app-first. Wishpad может присутствовать как небольшой brand marker, но не должен конкурировать с названием списка.

На mobile header занимает одну плотную верхнюю область:

- первая строка: название wishlist и share action;
- описание ниже, если заполнено;
- без тяжёлого hero и без отдельной навигационной панели.

На desktop можно дать больше воздуха, но структура остаётся той же.

Низ страницы:

- минимальный footer;
- ссылки на документы;
- без owner/admin navigation.

Если публичную ссылку открывает владелец списка, страница остаётся в public layout. Интерфейс показывает owner-safe view без статусов бронирования и без owner navigation.

Deep link `/wishlist/:publicToken` всегда открывает public wishlist route:

- в mobile web;
- в desktop web;
- в mobile app через Capacitor deep link.

### Owner Mobile Navigation

На mobile viewport и в mobile app owner navigation использует bottom navigation.

Основные tabs:

- `Dashboard`;
- `Настройки`.

В bottom navigation не добавляется logout. Выход находится в account/settings menu.

Admin link показывается только пользователю с ролью `admin`:

- на mobile - внутри compact menu;
- в mobile app - только если admin route доступен в данной сборке/среде.

Вложенные owner screens используют back button:

- edit wishlist;
- wishlist details;
- gift details/edit;
- другие detail/edit экраны.

Back button возвращает пользователя к ближайшему логическому parent screen, обычно к owner dashboard или к detail screen.

FAB для создания wishlist используется только на mobile owner dashboard и учитывает safe area.

### Owner Desktop Navigation

На desktop owner navigation использует компактный sidebar или компактную левую область навигации.

Desktop не добавляет отдельные разделы, которых нет в mobile navigation. Он только делает навигацию более доступной за счёт ширины экрана.

Owner sidebar содержит:

- Wishpad brand marker;
- dashboard;
- настройки;
- admin link для роли `admin`;
- account/logout action.

Owner sidebar может быть сворачиваемым, если это не усложняет MVP. Базовое требование для MVP - компактность и понятный active state.

Кнопка создания wishlist на desktop находится в верхней части owner dashboard, а не в FAB.

Активный раздел всегда визуально выделяется.

### Admin Navigation

Admin navigation использует sidebar на tablet и desktop.

Admin sidebar сворачиваемый в обоих случаях:

- на tablet по умолчанию может быть compact;
- на desktop может быть expanded или compact в зависимости от сохранённого состояния пользователя;
- состояние раскрытия можно хранить локально на клиенте.

Admin sidebar содержит:

- dashboard;
- users;
- wishlists;
- logs;
- failed jobs;
- settings;
- переход обратно в user-facing часть;
- logout/account action.

На phone viewport admin layout не рендерит рабочую навигацию. Вместо этого показывается `UnsupportedViewportNotice`.

### Header Rules

Headers во всех user-facing экранах должны быть компактными.

Mobile/app-first правило:

- текущий экран или список должен быть главным текстовым акцентом;
- вторичные действия уходят в compact menu;
- длинные названия обрезаются по месту;
- header не должен занимать много вертикального пространства;
- header не должен дублировать bottom navigation.

На desktop header может содержать больше inline actions, но не должен менять сценарий.

### Safe Areas

Mobile app и mobile web должны учитывать safe areas:

- bottom navigation;
- FAB;
- bottom sheet;
- fullscreen drawer;
- toast;
- fixed headers.

Элементы управления не должны попадать под системные зоны iOS/Android.

### Active States

Все navigation items должны иметь active state.

Минимальные состояния:

- default;
- active;
- hover/focus для desktop;
- disabled, если маршрут временно недоступен.

Active state строится по текущему route name, а не по тексту ссылки.

## 14. Детализация Visual Style

### Общий Характер

Wishpad должен ощущаться как спокойное, понятное и современное приложение, а не как маркетинговый лендинг.

Public guest UI:

- тёплый;
- лёгкий;
- дружелюбный;
- mobile-first;
- без тяжёлых декоративных блоков.

Owner UI:

- практичный;
- спокойный;
- с быстрым доступом к действиям;
- без ощущения админки.

Admin UI:

- плотный;
- нейтральный;
- рабочий;
- оптимизированный для сканирования таблиц.

### Цветовая Модель

Цвета задаются через theme tokens и должны быть совместимы с PrimeVue theme customization.

Базовая палитра:

- background: тёплый почти белый;
- surface: белый;
- surface muted: очень светлый серо-тёплый;
- text primary: тёмный графит;
- text secondary: спокойный серый;
- border: мягкий нейтральный серый;
- primary: глубокий зелёно-бирюзовый;
- accent: мягкий коралловый;
- link/action: спокойный синий;
- success: зелёный;
- danger: красный;
- warning: янтарный.

Интерфейс не должен быть одноцветным. Нельзя строить весь UI только на оттенках одного зелёного, синего, фиолетового, бежевого или коричневого.

Семантика бронирований:

- свободный подарок не имеет отдельного цветового бейджа;
- чужая бронь: красный/опасный тон + Lucide `Lock` + имя;
- своя бронь: зелёный/успешный тон + Lucide `Check` + имя;
- owner-safe view не использует цвета бронирования, чтобы не создавать ложных подсказок владельцу.

### Типографика

Базовый font stack:

- системный sans-serif;
- без обязательной загрузки внешнего шрифта в MVP.

Размеры:

- mobile page title: `24px`;
- desktop page title: `28px`;
- section title: `20px`;
- card title: `16px`;
- body: `16px`;
- secondary text: `14px`;
- caption/meta: `13px`;
- admin table text: `14px`;
- admin dense meta: `12px`.

Правила:

- font-size не масштабируется от viewport width;
- letter-spacing по умолчанию `0`;
- длинные названия обрезаются с ellipsis только там, где есть отдельный detail view;
- текст кнопок и бейджей не должен переноситься так, чтобы ломать высоту элемента;
- hero-scale типографика не используется внутри карточек, таблиц, тулбаров и компактных панелей.

### Отступы И Размеры

Базовая сетка отступов: `4px`.

Основные значения:

- page padding mobile: `16px`;
- page padding tablet: `24px`;
- page padding desktop: `32px`;
- compact section gap: `12px`;
- regular section gap: `24px`;
- card internal padding mobile: `12px`;
- card internal padding desktop: `16px`;
- list item gap: `12px`;
- form field gap: `16px`;
- toolbar gap: `8px`;
- icon button size mobile: `44px`;
- icon button size desktop/admin: `36px-40px`;
- bottom navigation height: учитывает safe area;
- FAB size: `56px`.

Карточки и панели не должны визуально вкладываться друг в друга. Если нужна группировка, используется layout spacing, divider или full-width band, а не card inside card.

### Radius И Тени

Основной radius:

- card radius: `8px`;
- panel/dialog radius: `8px`;
- input/button radius: `8px`;
- small chip/badge radius: `999px`, если это именно pill badge;
- FAB и icon-only circular buttons могут быть круглыми.

Тени используются умеренно:

- карточки в публичке могут иметь очень лёгкую тень или только border;
- admin tables в основном используют border/divider;
- bottom sheet/modal может иметь более заметную тень;
- нельзя создавать декоративные glow/orb эффекты.

### Изображения

Подарок может быть без изображения.

Если изображение есть:

- используется стабильный aspect ratio;
- изображение не должно растягиваться с искажением;
- placeholder-картинка не показывается;
- при ошибке загрузки изображение скрывается, а текстовая информация остаётся доступной.

На публичной странице изображение не должно занимать столько места, чтобы вытеснять название и состояние подарка из первого взгляда.

### Интерактивные Состояния

Все интерактивные элементы должны иметь состояния:

- default;
- hover для desktop;
- focus-visible;
- active/pressed;
- loading;
- disabled;
- error, если действие завершилось ошибкой;
- success, если действие завершилось успешно.

Focus state должен быть видимым и не должен полагаться только на цвет.

Loading:

- для страниц и списков используется skeleton;
- для кнопок используется локальный loading state;
- для metadata import используется отдельное состояние проверки ссылки.

Errors:

- field validation показывается inline;
- общие ошибки показываются toast;
- destructive action errors показываются в dialog/toast в зависимости от контекста.

### Motion

Анимации должны быть короткими и функциональными.

Используются:

- раскрытие/сворачивание карточки;
- переворот Lucide `ChevronDown`/`ChevronUp` через rotation;
- открытие bottom sheet/modal/drawer;
- skeleton shimmer, если он не перегружает интерфейс.

Не используются:

- декоративные фоновые анимации;
- длительные page transitions;
- анимации, которые мешают быстрому бронированию подарка.

### PrimeVue Theme

PrimeVue используется как основа компонентов.

Базовый preset:

- `Aura`.

На старте MVP используется PrimeVue `Aura` с default tokens.

Отдельный Wishpad custom preset через `definePreset(Aura, ...)` создаётся позже, после появления первых UI-экранов и реальной проверки визуального характера приложения.

При настройке темы нужно:

- переопределять semantic tokens под палитру Wishpad только после решения о custom preset;
- не смешивать PrimeIcons с Lucide;
- приводить PrimeVue components к общей плотности отступов;
- использовать PrimeVue DataTable для admin tables;
- использовать PrimeVue Dialog/Drawer/Toast/ConfirmDialog там, где это совпадает с UX.

Если PrimeVue компонент визуально слишком тяжёлый для публички, его стили адаптируются через theme tokens и scoped styles.

## 15. Компоненты MVP

Базовые компоненты:

- `AppShell`
- `CompactTopBar`
- `OwnerBottomNavigation`
- `OwnerSidebar`
- `AdminSidebar`
- `BackButton`
- `OwnerDashboardPage`
- `PublicWishlistPage`
- `WishlistCard`
- `WishlistStatusSegmentControl`
- `GiftCard`
- `GiftReservationState`
- `ReserveGiftDialog`
- `CancelReservationDialog`
- `OwnerSpoilerNotice`
- `WishlistForm`
- `GiftForm`
- `MetadataImportControl`
- `MetadataPreview`
- `ImageUrlPreview`
- `CopyPublicLinkButton`
- `AuthForm`
- `OwnerSettingsForm`
- `NotificationSettingsForm`
- `LocaleFormatSettingsForm`
- `PasswordChangeForm`
- `DeactivateAccountDialog`
- `AdminLayout`
- `AdminSettingsPage`
- `DataTable`
- `AdminTableToolbar`
- `AdminFiltersPanel`
- `AdminRowActionsMenu`
- `UnsupportedViewportNotice`
- `SafeAreaContainer`
- `ConfirmDialog`
- `Toast`
- `EmptyState`
- `ErrorState`
- `LoadingState`

## 16. Первичные UX-правила

- У владельца не должно быть визуального способа узнать статусы бронирования.
- Если владелец открывает публичную ссылку своего списка, интерфейс показывает список без статусов и объясняет, что статусы скрыты.
- Гостю должно быть очевидно, какой подарок свободен, какой занят, и какой забронирован им.
- Отмена брони всегда требует confirmation dialog.
- Создание брони должно обрабатывать `409 Conflict` без ощущения поломки интерфейса.
- Импорт метаданных не должен блокировать ручное создание подарка.
- Формы используют VeeValidate + Zod для frontend validation.
- Ошибки конкретных полей показываются inline рядом с полем.
- Toast используется для успешных действий, общих ошибок и фоновых событий, а не вместо field validation.
- Админские таблицы должны поддерживать фильтрацию, сортировку, пагинацию и быстрый переход в карточку сущности.
- Поиск в admin tables не входит в MVP и остаётся future scope.

## 17. Следующие уровни детализации

Эта версия UI specification фиксирует общий каркас, подробное поведение auth screen, публичной страницы wishlist, owner dashboard, owner wishlist edit, gift form, metadata import flow, owner settings, admin tables, responsive navigation и visual style.

Формат детализации UI-макетов:

- текстовый layout-spec;
- low-fi wireframes в markdown;
- сначала mobile layout;
- затем desktop/tablet дополнения;
- без pixel-perfect требований на этом этапе.

Следующие шаги:

- использовать UI specification как implementation guide для первых frontend-экранов;
- дополнительные UI-детализации добавлять точечно после появления первых живых экранов.