init
This commit is contained in:
409
dating-app-frontend-prompt.md
Normal file
409
dating-app-frontend-prompt.md
Normal file
@@ -0,0 +1,409 @@
|
||||
# Dating App Frontend — Claude Code Master Prompt
|
||||
|
||||
## Context & Orientation
|
||||
|
||||
You are building the complete frontend for a dating application called **Daiting**. The backend is already implemented. The generated API client lives at `src/api/api.ts` — use it as the source of truth for all endpoint shapes, DTOs, and types. Do not re-declare types that already exist in the API client; import them.
|
||||
|
||||
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,
|
||||
section headings) must be written in Russian.
|
||||
Brand name, logo, and any decorative/editorial text remain in English.
|
||||
|
||||
---
|
||||
|
||||
## Design Skills — Read Before Writing a Single Line
|
||||
|
||||
Before writing any code, internalize these two skill files in full:
|
||||
|
||||
```
|
||||
.claude/skills/impeccable/SKILL.md (+ all 7 files in reference/)
|
||||
.claude/skills/taste-skill/SKILL.md
|
||||
```
|
||||
|
||||
Apply **Impeccable** design principles throughout every component.
|
||||
Apply **Taste-skill** with these parameters:
|
||||
- `DESIGN_VARIANCE: 8`
|
||||
- `MOTION_INTENSITY: 6`
|
||||
- `VISUAL_DENSITY: 4`
|
||||
|
||||
### 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.
|
||||
- Layouts that feel editorial — asymmetric cards, overlapping text on images, offset grids
|
||||
- Micro-interactions via CSS transitions + Vue `<Transition>` + GSAP (install it). Nothing gratuitous — motion serves information hierarchy.
|
||||
- Grain texture overlay (SVG `<feTurbulence>`) on the app shell background for tactility
|
||||
- NO: purple gradients, Inter/Roboto, glassmorphism clichés, floating hearts, soft pastels, rounded-rectangle cards that look like every other dating app
|
||||
|
||||
This should feel like an independent editorial magazine for human connection — not Tinder, not Bumble.
|
||||
|
||||
---
|
||||
|
||||
## Project Bootstrap
|
||||
|
||||
```bash
|
||||
# If scaffolding from scratch:
|
||||
pnpm create tauri-app@latest dating-app-frontend --template vue-ts
|
||||
cd dating-app-frontend
|
||||
|
||||
# Core dependencies
|
||||
pnpm add vue-router@4 pinia @vueuse/core axios gsap @vuelidate/core @vuelidate/validators
|
||||
|
||||
# UI primitives (no opinionated component library — build custom)
|
||||
pnpm add @floating-ui/vue
|
||||
|
||||
# Dev
|
||||
pnpm add -D sass tailwindcss @tailwindcss/vite autoprefixer
|
||||
```
|
||||
|
||||
Configure Tailwind in **CSS-only mode** (no JIT utility classes in templates — use scoped SCSS instead; Tailwind is for reset and baseline only).
|
||||
|
||||
---
|
||||
|
||||
## File & Folder Architecture
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/
|
||||
│ └── api.ts # ← generated, do not touch
|
||||
├── assets/
|
||||
│ ├── fonts.css # @font-face / Google Fonts import
|
||||
│ └── grain.svg # noise texture
|
||||
├── composables/
|
||||
│ ├── useAuth.ts
|
||||
│ ├── useFeed.ts
|
||||
│ ├── useChat.ts
|
||||
│ ├── useMedia.ts
|
||||
│ └── useGeolocation.ts
|
||||
├── stores/
|
||||
│ ├── auth.store.ts # Pinia
|
||||
│ ├── profile.store.ts
|
||||
│ ├── feed.store.ts
|
||||
│ ├── chat.store.ts
|
||||
│ └── ui.store.ts
|
||||
├── router/
|
||||
│ └── index.ts # vue-router with guards
|
||||
├── views/
|
||||
│ ├── auth/
|
||||
│ │ ├── LoginView.vue
|
||||
│ │ └── RegisterView.vue
|
||||
│ ├── onboarding/
|
||||
│ │ └── ProfileSetupView.vue
|
||||
│ ├── feed/
|
||||
│ │ └── FeedView.vue
|
||||
│ ├── matches/
|
||||
│ │ └── MatchesView.vue
|
||||
│ ├── chat/
|
||||
│ │ ├── ChatsListView.vue
|
||||
│ │ └── ChatRoomView.vue
|
||||
│ ├── dates/
|
||||
│ │ └── DatesView.vue
|
||||
│ ├── profile/
|
||||
│ │ ├── MyProfileView.vue
|
||||
│ │ └── ProfileDetailView.vue
|
||||
│ └── admin/
|
||||
│ └── ReportsView.vue
|
||||
├── components/
|
||||
│ ├── layout/
|
||||
│ │ ├── AppShell.vue # grain bg, Tauri titlebar, nav
|
||||
│ │ ├── BottomNav.vue # mobile navigation
|
||||
│ │ ├── SideNav.vue # desktop sidebar navigation
|
||||
│ │ └── TauriTitlebar.vue # custom Tauri drag region
|
||||
│ ├── feed/
|
||||
│ │ ├── FeedCard.vue # the main profile card
|
||||
│ │ ├── FeedCardStack.vue # swipe stack with GSAP
|
||||
│ │ ├── FeedFilters.vue # filter drawer/panel
|
||||
│ │ └── LikeButton.vue
|
||||
│ ├── profile/
|
||||
│ │ ├── ProfileAvatar.vue
|
||||
│ │ ├── ProfileBadge.vue # tags rendered as editorial labels
|
||||
│ │ ├── ProfileEditor.vue # form for create/edit profile
|
||||
│ │ └── MediaGallery.vue
|
||||
│ ├── chat/
|
||||
│ │ ├── ChatBubble.vue
|
||||
│ │ ├── ChatInput.vue # text + media attach
|
||||
│ │ ├── VoiceRecorder.vue
|
||||
│ │ └── MediaMessage.vue
|
||||
│ ├── dates/
|
||||
│ │ ├── DateCard.vue
|
||||
│ │ ├── DateProposalForm.vue
|
||||
│ │ └── MapPicker.vue # Leaflet for lat/lng picking
|
||||
│ ├── common/
|
||||
│ │ ├── AppButton.vue
|
||||
│ │ ├── AppInput.vue
|
||||
│ │ ├── AppModal.vue
|
||||
│ │ ├── AppDrawer.vue # bottom sheet on mobile, side panel on desktop
|
||||
│ │ ├── AppToast.vue
|
||||
│ │ ├── LoadingSpinner.vue
|
||||
│ │ └── EmptyState.vue
|
||||
│ └── reports/
|
||||
│ └── ReportModal.vue
|
||||
└── styles/
|
||||
├── _variables.scss
|
||||
├── _typography.scss
|
||||
├── _animations.scss
|
||||
└── main.scss
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Integration — Full Coverage
|
||||
|
||||
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
|
||||
- `POST /refresh` — silent token refresh interceptor in Axios (auto-retry once on 401)
|
||||
- `POST /fcm-token` — call after login (stub FCM token for web; use Tauri plugin for desktop)
|
||||
|
||||
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
|
||||
- `PUT /:profileId` — profile editor
|
||||
- `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
|
||||
- `POST /:chatId/messages?profileId=` — send text/media
|
||||
- `DELETE /:chatId?profileId=` — close chat with confirmation
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
- Drag-to-like: GSAP Draggable on desktop; touch events on mobile
|
||||
- Color bleed on drag direction: terracotta tint for right (like), cool grey tint for left (dislike)
|
||||
- Discard animation: card flies off screen with rotation, next card scales up from behind
|
||||
- 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
|
||||
- Voice messages: custom waveform player (Web Audio API for visualization)
|
||||
- Video messages: inline `<video>` with poster frame
|
||||
- Photos: open in lightbox modal
|
||||
- Locked chat state: blurred message list + overlay "You have X other open chats. Close one to unlock."
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## Routing & Guards
|
||||
|
||||
```
|
||||
/ → redirect → /feed (if authed) or /login
|
||||
/login
|
||||
/register
|
||||
/setup → profile creation wizard (redirect here if user has no profiles)
|
||||
/feed
|
||||
/matches
|
||||
/chats
|
||||
/chats/:chatId
|
||||
/dates
|
||||
/profile/me
|
||||
/profile/:profileId → public view
|
||||
/admin/reports → role guard (admin only)
|
||||
```
|
||||
|
||||
Navigation guard: check auth store on every route change. Refresh token silently if access token expired.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tauri-Specific
|
||||
|
||||
- Use `@tauri-apps/api/window` for window controls in TauriTitlebar.vue
|
||||
- Detect Tauri context: `window.__TAURI__` exists → show custom titlebar, hide browser-nav elements
|
||||
- File upload: use Tauri `dialog.open()` for native file picker; fall back to `<input type="file">` in browser
|
||||
- Deep link handling skeleton (for future push notification routing)
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
4. Tailwind base/reset
|
||||
|
||||
```scss
|
||||
// _variables.scss
|
||||
:root {
|
||||
--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);
|
||||
|
||||
--font-display: 'Instrument Serif', Georgia, serif;
|
||||
--font-mono: 'DM Mono', 'Courier New', monospace;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 16px;
|
||||
|
||||
--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-spring: 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality Bar
|
||||
|
||||
Every component must meet these standards before considered done:
|
||||
|
||||
- [ ] Works in Chrome mobile emulation (375px) without horizontal scroll
|
||||
- [ ] Works in desktop browser at 1280px+
|
||||
- [ ] Works in Tauri window
|
||||
- [ ] Loading states: skeleton loaders (not spinners) for feed and chat history
|
||||
- [ ] Empty states: custom illustrated SVG + copy (no generic "Nothing here yet")
|
||||
- [ ] Error states: inline error with retry action
|
||||
- [ ] All forms validated client-side via Vuelidate before API call
|
||||
- [ ] API errors surfaced as toasts (not console.error)
|
||||
- [ ] Keyboard navigable (Tab order, Enter to submit, Escape to close modals)
|
||||
- [ ] No `any` TypeScript types — use types from the generated API client
|
||||
- [ ] GSAP animations respect `prefers-reduced-motion` (wrap in `matchMedia` check)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order (follow strictly)
|
||||
|
||||
1. Project scaffold + Tauri config + pnpm install
|
||||
2. Styles: variables, fonts, grain texture, AppShell skeleton
|
||||
3. Auth stores + Login/Register views + router guards
|
||||
4. API interceptor (Axios instance with token refresh)
|
||||
5. Pinia stores: auth, profile, ui
|
||||
6. Profile setup wizard (onboarding)
|
||||
7. Feed: store, FeedCard, FeedCardStack with swipe
|
||||
8. Feed filters drawer
|
||||
9. Matches view
|
||||
10. Chat: store (with polling), ChatsListView, ChatRoomView, media messages
|
||||
11. Dates: form with Leaflet map, list, status updates
|
||||
12. My Profile view + editor + media gallery
|
||||
13. Public profile view
|
||||
14. Reports modal
|
||||
15. Admin views (reports table, tags/cities management)
|
||||
16. Tauri titlebar + native file picker integration
|
||||
17. Polish pass: animations, transitions, empty states, error states
|
||||
18. `pnpm tauri build` smoke test
|
||||
|
||||
---
|
||||
|
||||
## Commands Reference
|
||||
|
||||
```bash
|
||||
pnpm dev # Vite dev server (browser)
|
||||
pnpm tauri dev # Tauri dev window
|
||||
pnpm build # Vite build
|
||||
pnpm tauri build # Tauri production build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final Note
|
||||
|
||||
Do not scaffold placeholder components. Every component you create must be fully implemented — real logic, real styles, real API calls. If a feature is complex, implement it incrementally but completely within each file. The goal is a shippable, visually distinctive dating app that feels nothing like a template.
|
||||
|
||||
Start with step 1 and work sequentially. Announce each step as you begin it.
|
||||
Reference in New Issue
Block a user