eslint --fix
This commit is contained in:
@@ -8,14 +8,15 @@ Project root: `C:\MyApps\dating-app-frontend`
|
||||
Package manager: **pnpm**
|
||||
Stack: **Vue 3 (Composition API + `<script setup>`) + Vite + Tauri v2**
|
||||
The app must work in three contexts simultaneously:
|
||||
|
||||
1. Browser (dev/PWA) — desktop viewport
|
||||
2. Browser — mobile viewport (responsive, ≥ 375 px)
|
||||
3. Tauri desktop window (Windows / macOS / Linux)
|
||||
|
||||
---
|
||||
|
||||
All user-facing text (button labels, placeholders, error messages,
|
||||
toast notifications, empty states, navigation items, form hints,
|
||||
All user-facing text (button labels, placeholders, error messages,
|
||||
toast notifications, empty states, navigation items, form hints,
|
||||
section headings) must be written in Russian.
|
||||
Brand name, logo, and any decorative/editorial text remain in English.
|
||||
|
||||
@@ -32,6 +33,7 @@ Before writing any code, internalize these two skill files in full:
|
||||
|
||||
Apply **Impeccable** design principles throughout every component.
|
||||
Apply **Taste-skill** with these parameters:
|
||||
|
||||
- `DESIGN_VARIANCE: 8`
|
||||
- `MOTION_INTENSITY: 6`
|
||||
- `VISUAL_DENSITY: 4`
|
||||
@@ -39,6 +41,7 @@ Apply **Taste-skill** with these parameters:
|
||||
### Aesthetic Direction
|
||||
|
||||
The visual language is **"warm brutalism meets editorial intimacy"**:
|
||||
|
||||
- Dark base (`#0d0d0d`) with warm cream (`#f0ebe0`) as the primary text/accent surface
|
||||
- A single vivid signal color — deep terracotta `#c45c3a` — used sparingly for CTAs, likes, active states
|
||||
- Typography: pair **`Instrument Serif`** (display, headers, profile names) with **`DM Mono`** (UI labels, metadata, counts). Load both from Google Fonts.
|
||||
@@ -164,6 +167,7 @@ src/
|
||||
Implement every endpoint from `src/api/api.ts`. Below is the complete feature map:
|
||||
|
||||
### Auth (`/api/v1/auth/`)
|
||||
|
||||
- `POST /register` — registration form with phone + password, validation via Vuelidate
|
||||
- `POST /login` — login form
|
||||
- `POST /logout` — clear store + redirect
|
||||
@@ -173,11 +177,13 @@ Implement every endpoint from `src/api/api.ts`. Below is the complete feature ma
|
||||
Store access token in memory (Pinia), refresh token in `localStorage`. Never store access token in `localStorage`.
|
||||
|
||||
### Users (`/api/v1/users/`)
|
||||
|
||||
- `GET /me` — populate auth store on app load
|
||||
- `GET /:id` — view any user (admin use)
|
||||
- `PATCH /:id/ban` and `/activate` — admin panel actions
|
||||
|
||||
### Profiles (`/api/v1/profiles/`)
|
||||
|
||||
- `POST /` — profile creation wizard (multi-step: basic info → location → tags → photo)
|
||||
- `GET /my` — list own profiles in MyProfileView
|
||||
- `GET /:profileId` — public profile detail page
|
||||
@@ -185,20 +191,24 @@ Store access token in memory (Pinia), refresh token in `localStorage`. Never sto
|
||||
- `DELETE /:profileId` — with confirmation modal
|
||||
|
||||
### Media (`/api/v1/profiles/:profileId/media/`)
|
||||
|
||||
- `POST /upload?type=` — drag-and-drop + file picker; show upload progress bar
|
||||
- `GET /` — grid gallery with lightbox
|
||||
- `DELETE /:mediaId` — with optimistic UI removal
|
||||
|
||||
### Feed (`/api/v1/feed`)
|
||||
|
||||
- Infinite scroll OR card-stack swipe (implement BOTH modes, togglable via UI switch)
|
||||
- Filter panel: cityId, districtId, ageMin/ageMax, keyword, tagIds (multi-select chips)
|
||||
- Show "search paused" banner when match limit is exceeded (detect via API response or a dedicated flag)
|
||||
|
||||
### Likes (`/api/v1/likes`)
|
||||
|
||||
- `POST /` — like/dislike with swipe gesture (GSAP drag) or button
|
||||
- `GET /matches?profileId=` — matches list with match animation (confetti-lite or editorial flash)
|
||||
|
||||
### Chat (`/api/v1/chats`)
|
||||
|
||||
- `POST /` — open chat from a match card
|
||||
- `GET /?profileId=` — chats list; lock icon on inactive chats (only one active at a time)
|
||||
- `GET /:chatId/messages?profileId=` — message history with virtual scroll
|
||||
@@ -207,16 +217,19 @@ Store access token in memory (Pinia), refresh token in `localStorage`. Never sto
|
||||
- Implement **polling** (2 s interval) for new messages; note in code where WebSocket would replace this
|
||||
|
||||
### Dates (`/api/v1/dates`)
|
||||
|
||||
- `POST /` — proposal form: map picker (Leaflet) for lat/lng, datetime picker, optional statusId
|
||||
- `GET /?profileId=` — list with status chips
|
||||
- `PATCH /:id/status?profileId=` — accept / decline / complete actions
|
||||
- `GET /statuses` — fetch on mount, cache in Pinia
|
||||
|
||||
### Reports (`/api/v1/reports`)
|
||||
|
||||
- `POST /` — `ReportModal` triggered from profile or chat bubble context menu
|
||||
- `GET /` — admin table view with pagination
|
||||
|
||||
### Tags, Cities, Greetings
|
||||
|
||||
- `GET /tags`, `GET /cities`, `GET /cities/:cityId/districts`, `GET /greetings` — fetch on app init, cache in Pinia
|
||||
- Admin: create/delete tags, cities, districts, greetings
|
||||
|
||||
@@ -225,7 +238,9 @@ Store access token in memory (Pinia), refresh token in `localStorage`. Never sto
|
||||
## Component Implementation Standards
|
||||
|
||||
### FeedCard.vue
|
||||
|
||||
The centerpiece. Execute this with exceptional care:
|
||||
|
||||
- Full-bleed image background
|
||||
- Profile name in large Instrument Serif, overlapping bottom of image
|
||||
- Age, city, tags as DM Mono micro-labels
|
||||
@@ -235,6 +250,7 @@ The centerpiece. Execute this with exceptional care:
|
||||
- Tap to expand into ProfileDetailView
|
||||
|
||||
### ChatRoomView.vue
|
||||
|
||||
- Messages grouped by date separator
|
||||
- Own messages: right-aligned, cream background
|
||||
- Partner messages: left-aligned, dark card with subtle border
|
||||
@@ -245,18 +261,21 @@ The centerpiece. Execute this with exceptional care:
|
||||
- Input bar: text area auto-grows, attach button opens media type picker
|
||||
|
||||
### AppShell.vue
|
||||
|
||||
- Desktop: left sidebar (64px collapsed / 240px expanded) + main content area
|
||||
- Mobile: top header + bottom nav (5 items)
|
||||
- Tauri: custom titlebar with drag region, traffic lights on macOS (use `data-tauri-drag-region`)
|
||||
- Grain SVG filter applied as a pseudo-element over the entire shell
|
||||
|
||||
### Responsive breakpoints
|
||||
|
||||
```scss
|
||||
$mobile: 375px;
|
||||
$tablet: 768px;
|
||||
$desktop: 1024px;
|
||||
$wide: 1440px;
|
||||
```
|
||||
|
||||
Use `@container` queries inside card components where possible.
|
||||
|
||||
---
|
||||
@@ -285,18 +304,21 @@ Navigation guard: check auth store on every route change. Refresh token silently
|
||||
## State Management (Pinia)
|
||||
|
||||
### `auth.store.ts`
|
||||
|
||||
```ts
|
||||
state: { user, accessToken, profiles, activeProfileId }
|
||||
actions: login, logout, register, refreshToken, setActiveProfile
|
||||
```
|
||||
|
||||
### `feed.store.ts`
|
||||
|
||||
```ts
|
||||
state: { cards[], filters, page, hasMore, searchPaused }
|
||||
actions: fetchNextPage, applyFilters, likeCard, dislikeCard
|
||||
```
|
||||
|
||||
### `chat.store.ts`
|
||||
|
||||
```ts
|
||||
state: { chats[], activeChat, messages[], polling: NodeJS.Timer | null }
|
||||
actions: openChat, closeChat, sendMessage, fetchMessages, startPolling, stopPolling
|
||||
@@ -316,6 +338,7 @@ actions: openChat, closeChat, sendMessage, fetchMessages, startPolling, stopPoll
|
||||
## CSS Architecture
|
||||
|
||||
`src/styles/main.scss` imports in order:
|
||||
|
||||
1. `_variables.scss` — all CSS custom properties
|
||||
2. `_typography.scss` — font definitions, heading scale
|
||||
3. `_animations.scss` — keyframes, transition utilities
|
||||
@@ -324,26 +347,26 @@ actions: openChat, closeChat, sendMessage, fetchMessages, startPolling, stopPoll
|
||||
```scss
|
||||
// _variables.scss
|
||||
:root {
|
||||
--color-base: #0d0d0d;
|
||||
--color-surface: #161614;
|
||||
--color-base: #0d0d0d;
|
||||
--color-surface: #161614;
|
||||
--color-surface-2: #1e1e1b;
|
||||
--color-cream: #f0ebe0;
|
||||
--color-muted: #6b6860;
|
||||
--color-signal: #c45c3a;
|
||||
--color-signal-dim:#7a3822;
|
||||
--color-border: rgba(240, 235, 224, 0.08);
|
||||
--color-cream: #f0ebe0;
|
||||
--color-muted: #6b6860;
|
||||
--color-signal: #c45c3a;
|
||||
--color-signal-dim: #7a3822;
|
||||
--color-border: rgba(240, 235, 224, 0.08);
|
||||
|
||||
--font-display: 'Instrument Serif', Georgia, serif;
|
||||
--font-mono: 'DM Mono', 'Courier New', monospace;
|
||||
--font-mono: 'DM Mono', 'Courier New', monospace;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 16px;
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 16px;
|
||||
|
||||
--shadow-card: 0 2px 24px rgba(0,0,0,0.6);
|
||||
--shadow-card: 0 2px 24px rgba(0, 0, 0, 0.6);
|
||||
|
||||
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-base: 280ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-base: 280ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-spring: 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user