## MOC: Фронтенд-паттерны и компоненты PhotoPlaces ### Архитектура фронтенда ```mermaid graph TB Next[Next.js 14 App Router] --> Pages[Pages] Next --> Components[Shared Components] Next --> Hooks[Custom Hooks] Components --> Modal[Modal] Components --> MapView[MapView + Leaflet] Components --> PlaceForm[PlaceForm] Hooks --> useAuth[AuthProvider + Context] Hooks --> useWebSocket[WebSocket] Pages --> API[api.ts: fetch wrapper] API --> Auth[Auto-refresh 401] API --> Backend[Go API :8080] ``` ### Ключевые компоненты | Компонент | Файл | Назначение | |---|---|---| | Modal (общий) | `components/Modal.tsx` | Переиспользуемая модалка, скроллбар внутри контента | | MapView | `components/MapView.tsx` | Карта Leaflet + PlaceCardModal | | PlaceForm | `components/PlaceForm.tsx` | Форма места / студии / услуги | | PlaceCardModal | `MapView.tsx` (встроен) | Bottom-panel карточка места | | LoginModal | `components/LoginModal.tsx` | Форма входа | | RegisterModal | `components/RegisterModal.tsx` | Форма регистрации | | AddLocationModal | `components/AddLocationModal.tsx` | Добавление места | | ErrorBoundary | `components/ErrorBoundary.tsx` | Отлов ошибок рендера | | ErrorReporter | `components/ErrorReporter.tsx` | Панель ошибок (debug) | | MapPicker | `components/MapPicker.tsx` | Полноэкранный выбор координат | | Header | `components/Header.tsx` | Навигация | ### Паттерны | Паттерн | Где | Заметка | |---|---|---| | **Shared Modal** с `overflow-y-auto` внутри | `components/Modal.tsx` | [[atomic-modal-scrollbar-inside]] | | Ad-hoc модалки → общий компонент (DRY) | decision record | [[decision-modal-component-extraction]] | | Auth через Context + custom hook | `hooks/useAuth.tsx` | [[frontend-api-client]] | | Ghost markers для гостей | `MapView.tsx:82-83` | [[atomic-ghost-markers]] | | Diff-обновление маркеров (markerIdsRef) | `MapView.tsx:70-87` | [[architecture-overview]] | | API client с auto-refresh токена | `lib/api.ts` | [[frontend-api-client]] | | Error suppression (антипаттерн) | `PlaceForm.tsx:42-43` | [[atomic-error-swallowing-frontend]] | | Fetch retry infinite loop (антипаттерн) | `lib/api.ts:87` | [[atomic-fetch-retry-infinite-loop]] | | Координаты [lat,lng] консистентность | `lib/map.ts` | [[atomic-map-coordinates-inconsistency]] | ### Стек - **Фреймворк**: Next.js 14 App Router - **Стили**: Tailwind CSS (без CSS-модулей) - **Карты**: Leaflet + OSM dark tiles - **Состояние**: React Context (AuthProvider) - **API**: Custom fetch wrapper (`lib/api.ts`) - **Тесты**: Vitest + @testing-library/react ### Code style - Все компоненты — `'use client'` (прямые функции, не классы) - Нет выделенных CSS/SCSS-файлов — только Tailwind utility классы в JSX - Импорты: `@/` алиас → `./src/` - Комментарии на русском языке - Нет сторонних UI-библиотек (MUI, Radix, Headless UI) ### Антипаттерны (исправленные) | Проблема | Заметка | |---|---| | 7 ad-hoc модалок | [[atomic-modal-scrollbar-inside]] | | Подавление ошибок | [[atomic-error-swallowing-frontend]] | | Retry loop на /auth/logout | [[atomic-fetch-retry-infinite-loop]] | | Ghost markers — стиль разный от logged-in | [[atomic-ghost-markers]] | | Типы не синхронизированы с backend | [[atomic-frontend-types-backend-sync]] | ### Связанные заметки - [[architecture-overview]] — общий обзор - [[frontend-api-client]] — API client и auth flow - [[atomic-modal-scrollbar-inside]] — модалка со скроллбаром - [[decision-modal-component-extraction]] — решение об извлечении Modal - [[atomic-error-swallowing-frontend]] — подавление ошибок - [[atomic-ghost-markers]] — ghost-маркеры на карте - [[MOC-backend-patterns]] — бэкенд-паттерны - [[MOC-security-patterns]] — безопасность #frontend #react #nextjs #MOC #best-practice #components