295 lines
11 KiB
Markdown
295 lines
11 KiB
Markdown
# Frontend Guide: Component Inventory
|
||
|
||
Этот документ отвечает на вопрос: какие UI-примитивы и повторно используемые компоненты уже есть в коде и когда их нужно брать вместо создания нового решения.
|
||
|
||
## 1. Foundation primitives
|
||
|
||
Это первый выбор для любого нового экрана.
|
||
|
||
### `frontend/src/app/globals.css`
|
||
|
||
Назначение:
|
||
|
||
- цветовые токены
|
||
- surface hierarchy
|
||
- базовые form primitives
|
||
- action states
|
||
- feedback states
|
||
|
||
Использовать, когда:
|
||
|
||
- нужен новый экран на существующей дизайн-системе
|
||
- нужно собрать карточку, поле, кнопку или feedback-блок без изобретения новой визуальной базы
|
||
|
||
Не использовать как:
|
||
|
||
- место для локального feature-хака без повторного применения
|
||
|
||
### `frontend/src/components/AppShell.tsx`
|
||
|
||
Что даёт:
|
||
|
||
- shell background
|
||
- header
|
||
- global search
|
||
- mobile navigation drawers
|
||
- `PageHeader`
|
||
|
||
Использовать, когда:
|
||
|
||
- нужен normal page внутри основного приложения
|
||
|
||
Не использовать, когда:
|
||
|
||
- экран является отдельным auth/legal flow и у него другой layout-contract
|
||
|
||
### `frontend/src/components/ui/Modal.tsx`
|
||
|
||
Что даёт:
|
||
|
||
- единый modal shell
|
||
- focus trap
|
||
- close handling
|
||
- header/footer contract
|
||
- responsive sizing
|
||
|
||
Использовать, когда:
|
||
|
||
- нужен обычный modal dialog
|
||
- нет отдельного сценария drawer/workspace/fullscreen flow
|
||
|
||
Не использовать как:
|
||
|
||
- основание для новой параллельной modal-системы
|
||
|
||
### `frontend/src/components/shared/SearchableSelect.tsx`
|
||
|
||
Компоненты:
|
||
|
||
- `SearchableSelectSingle`
|
||
- `SearchableSelectMulti`
|
||
|
||
Использовать, когда:
|
||
|
||
- нужен поиск по списку
|
||
- нужен multi-select
|
||
- нужно chip-представление выбранных значений
|
||
- нужен compose-like address field
|
||
|
||
Не использовать native `select`, если:
|
||
|
||
- начинаются локальные доработки стрелки
|
||
- нужен поиск
|
||
- нужен выбор нескольких пользователей
|
||
|
||
## 2. Shell-level и системные компоненты
|
||
|
||
### `NotificationCenter`
|
||
|
||
Роль:
|
||
|
||
- системный блок уведомлений
|
||
|
||
Когда использовать:
|
||
|
||
- в shell или system-level surface
|
||
- не как основу для произвольных выпадающих списков
|
||
|
||
### `MobileLeftDrawer` и `MobileCalendarDrawer`
|
||
|
||
Роль:
|
||
|
||
- мобильные системные drawer-паттерны
|
||
|
||
Когда использовать:
|
||
|
||
- для app-level mobile navigation / side panels
|
||
|
||
Не использовать:
|
||
|
||
- как shortcut для каждой произвольной feature-модалки
|
||
|
||
## 3. Feature-shared компоненты
|
||
|
||
Эти компоненты полезны как референс и иногда как основа для похожих сценариев, но они уже ближе к домену.
|
||
|
||
### Calendar
|
||
|
||
Файлы:
|
||
|
||
- `CalendarModal.tsx`
|
||
- `EventModal.tsx`
|
||
- `CalendarParticipantsModal.tsx`
|
||
- `CalendarSubscriptionsModal.tsx`
|
||
- `ViewDayEventsModal.tsx`
|
||
- `ViewEventDetailsModal.tsx`
|
||
- `calendar/CalendarSidebar.tsx`
|
||
- `calendar/CalendarCard.tsx`
|
||
|
||
Использовать как референс для:
|
||
|
||
- event forms
|
||
- side panels
|
||
- selection-heavy dialog flows
|
||
|
||
С оговоркой:
|
||
|
||
- calendar UI ещё не является эталоном всей системы и содержит legacy-решения
|
||
|
||
### Documents
|
||
|
||
Файлы:
|
||
|
||
- `DocumentDetailModal.tsx`
|
||
- `DocumentUploadForm.tsx`
|
||
- `DocumentPreview.tsx`
|
||
- `DocumentMetadataEditor.tsx`
|
||
- `DocumentComments.tsx`
|
||
|
||
Использовать как референс для:
|
||
|
||
- document-heavy UI
|
||
- detail panels
|
||
- upload flows
|
||
- metadata forms
|
||
|
||
С оговоркой:
|
||
|
||
- documents содержит заметный пласт legacy-геометрии и нуждается в дальнейшем выравнивании
|
||
|
||
### Requests
|
||
|
||
Файлы:
|
||
|
||
- `requests/RequestAttachmentPreviewModal.tsx`
|
||
- `requests/RequestAvatar.tsx`
|
||
- `requests/RequestComposeModal.tsx`
|
||
- `requests/RequestDetailModal.tsx`
|
||
- `requests/RequestListControls.tsx`
|
||
- `requests/RequestListItem.tsx`
|
||
- `requests/RequestListSection.tsx`
|
||
- `requests/RequestSwipeModePanel.tsx`
|
||
- `requests/RequestUserBadge.tsx`
|
||
- `requests/SwipeApprovalMode.tsx`
|
||
- `app/requests/page.tsx`
|
||
- `hooks/useRequestsPage.ts`
|
||
- `hooks/useRequestsPageScreen.ts`
|
||
|
||
Использовать как референс для:
|
||
|
||
- compose-like form pattern
|
||
- detail modal с participant-heavy content
|
||
- page-level toolbar/filter block, который вынесен из страницы в feature component
|
||
- feature container для list shell, который собирает rows + empty state + load more
|
||
- отдельный mode-panel для alternate flow вроде swipe-review
|
||
- attachment preview modal как отдельный feature block, а не хвост page-level JSX
|
||
- локальный avatar/media wrapper вокруг `next/image`, когда feature уже использует несколько однотипных аватаров и preview
|
||
- participant badge pattern для request-like доменов
|
||
- participant-only UI decisions
|
||
- conditional action rendering по данным API
|
||
- разделения page-level orchestration и feature-level compose workspace
|
||
- screen-specific router/menu/deep-link orchestration без засорения page-level JSX
|
||
|
||
`requests/RequestUserBadge.tsx` использовать как ориентир, когда нужен person-chip паттерн:
|
||
|
||
- `avatar + name` внутри capsule
|
||
- опциональная ссылка на профиль
|
||
- compact и large варианты внутри одного feature wrapper
|
||
|
||
Не копировать компонент дословно во все модули, но сохранять приоритет этого паттерна над сырой строкой получателя или локально собранным badge.
|
||
|
||
С оговоркой:
|
||
|
||
- `requests` сейчас хороший ориентир по декомпозиции и compose/detail flow, но не должен автоматически становиться visual-template для всех dense lists
|
||
|
||
### Users / profile-like screens
|
||
|
||
Файлы:
|
||
|
||
- `users/ProfileSections.tsx`
|
||
- `users/EditUserProfileModal.tsx`
|
||
- `users/EmployeeActionModal.tsx`
|
||
- `users/EmployeeActionsTimeline.tsx`
|
||
- `hooks/useProfilePage.ts`
|
||
- `hooks/useUserDetailPage.ts`
|
||
|
||
Использовать как референс для:
|
||
|
||
- profile hero
|
||
- profile sections
|
||
- info cards
|
||
- action timeline
|
||
- page-level profile controllers, которые держат loading/derived state/API orchestration вне page JSX
|
||
|
||
### Departments
|
||
|
||
Файлы:
|
||
|
||
- `departments/DepartmentPersonChip.tsx`
|
||
- `app/departments/[id]/page.tsx`
|
||
- `hooks/useDepartmentPage.ts`
|
||
|
||
Использовать как референс для:
|
||
|
||
- detail screen, который должен уметь переключаться между `showcase` и `management` без смены route и без ощущения двух разных страниц
|
||
- page-level controller для domain actions вокруг одного объекта: overview, members и inline management actions
|
||
- entry-point с detail page в уже существующий object-chat, когда объекту нужен access к реальной message history без локального duplicate UI
|
||
- richer detail layout, где есть сильный header и один основной roster/workspace-блок, без раздувания служебных summary-panels и вторичных management-panels
|
||
- member roster с role assignment и person-chip подачей вместо сырых строк участников
|
||
- role management на уровне названий ролей и назначений участникам, встроенный в roster, без фронтового permission-editor
|
||
|
||
С оговоркой:
|
||
|
||
- `departments/[id]` должен оставаться привязанным к реальным backend-capabilities; если у отдела уже есть живой object-chat, не дублировать его локальной discussion-панелью на странице
|
||
|
||
### Messages
|
||
|
||
Файлы:
|
||
|
||
- `messages/MessageComposer.tsx`
|
||
- `messages/ChatMessageItem.tsx`
|
||
- `messages/ChatDialogHeader.tsx`
|
||
- `ScrollableMessageList.tsx`
|
||
|
||
Использовать как референс для:
|
||
|
||
- dense interactive lists
|
||
- composer area
|
||
- dialog-like chat surface
|
||
|
||
С оговоркой:
|
||
|
||
- chat решает очень специфический UX, поэтому не должен автоматически становиться шаблоном для остальных модулей
|
||
|
||
## 4. Что считать shared, а что нет
|
||
|
||
Компонент нужно считать shared, если:
|
||
|
||
- он решает повторяемую визуальную механику
|
||
- у него нет жёсткой доменной привязки
|
||
- он пригоден минимум для двух feature-зон
|
||
|
||
Компонент не нужно тащить в shared, если:
|
||
|
||
- он глубоко привязан к одному домену
|
||
- у него много domain-specific props и состояний
|
||
- он служит хорошим референсом, но плохим универсальным primitive
|
||
|
||
## 5. Быстрый выбор перед новой разработкой
|
||
|
||
Если нужен:
|
||
|
||
- page shell: `AppShell`, `PageHeader`
|
||
- modal: `Modal`
|
||
- searchable dropdown: `SearchableSelect*`
|
||
- compose address line: `SearchableSelectMulti` с compose-like layout
|
||
- comment composer / delete action: `shared/CommentControls.tsx`
|
||
- primary/secondary surface: `app-surface` / `app-surface-muted`
|
||
- feedback block: `app-selected`, `app-feedback-*`
|
||
|
||
Если после этого всё ещё хочется создать новый primitive, сначала проверить:
|
||
|
||
- нет ли уже похожего решения в `components`
|
||
- нельзя ли расширить существующий shared-компонент
|
||
- не пытаемся ли мы решить локальную проблему новым глобальным паттерном
|