尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗48Date is a dating product built around one idea: a mutual like opens a 48-hour window to agree on a real date. This repository is the user web frontend:
It is frontend only. Every screen talks to a typed service whose current implementation is a clearly marked mock, so the real API can be plugged in later without touching components (see Connecting the real API).
The design source is the Figma file 48Date (L4MtWjAVlPSRAjJSzI1lrf), sections W00–W11. The project rules every change follows live in AGENTS.md.
| Area | Choice |
|---|---|
| Framework | Next.js 16 (App Router, cacheComponents / Partial Prerendering, Turbopack) |
| UI | React 19 with the React Compiler |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4, design tokens in src/app/globals.css |
| Primitives | Radix UI (Dialog, Accordion, Slot) wrapped as cleaned-up shadcn components |
| Font | Manrope via next/font |
| Icons | Hugeicons glyphs exported from Figma as SVG (public/icons) |
Next.js 16 differs from older versions. Its bundled docs in
node_modules/next/dist/docs/are the reference.
Requirements: Node.js 20 or newer (developed on Node 24) and npm.
npm install # install dependencies
cp .env.example .env.local # optional until a real API exists
npm run dev # http://localhost:3000
Production build:
npm run build
npm run start # or: npx next start -p 3417
Sign in to reach the portal:
/signin.000000 and 999999 (see mocks).| Command | What it does |
|---|---|
npm run dev | Development server with hot reload |
npm run build | Production build; prerenders every route (≈150 pages) and type-checks |
npm run start | Serves the production build |
npm run lint | ESLint (Next.js, React Compiler rules); must pass with no warnings |
npx tsc --noEmit | Type-check only |
npm audit | Dependency vulnerability check |
| Name | Purpose |
|---|---|
NEXT_PUBLIC_API_BASE_URL | Base URL of the 48Date API including /api/v1 (read only in src/lib/api/config.ts). Empty = every service runs on its mock; set = the integrated services call the API. Inlined at build time, so rebuild after changing it. |
NEXT_PUBLIC_APP_VERSION | Optional; sent as device.appVersion on sign-in. |
The backend's default port (3000) is also next dev's, so run one of them elsewhere, for example NEXT_PUBLIC_API_BASE_URL=http://localhost:4000/api/v1.
.env* files are git-ignored except .env.example. Never commit secrets; the frontend holds none.
public/
icons/ Hugeicons SVGs (one per slug), coloured with CSS masks
images/ landing · onboarding · portal photos (WebP / PNG)
brand/ badges/ flags/
src/
app/ Routes only: layouts, metadata and route files
[[...section]]/ Landing (sections are URL segments, e.g. /how-it-works)
terms/ privacy/ Legal pages
signup/ signin/ Authentication
onboarding/ Guided profile setup (/onboarding/<step>)
discover/ matches/ chats/ dates/ games/ stories/ Member portal
profile/ settings/ notifications/ report/ membership/ Account, safety, Premium
components/
ui/ shadcn primitives (button, dialog, accordion)
pieces/ Reusable, page-agnostic building blocks, by kind:
actions/ buttons and links that carry a data Action
brand/ the 48Date logo
cards/ content cards and rows (match, plan, place, facts…)
content/ Text, Icon, Chip, headings, rich text
feedback/ status notes, badges, summaries, progress, countdown
forms/ fields, choices, pickers, form header
illustrations/ non-interactive landing art built in HTML
layout/ Section, Container, PortalCard, Reveal…
media/ avatars, photos, upload slots, map
overlays/ PortalDialog
shells/ page frames, headers, footers, session guard
sections/ One file per Figma section or screen, composed from pieces
landing/ legal/ auth/ onboarding/
portal/
shared/ workspace, status screen, route factory, guard, view switch
discover/ matches/ chats/ dates/ games/ stories/
profile/ settings/ membership/
data/ All copy, image paths, icon names and actions (typed)
stores/ The only way components read data/ (getLanding(), getPlans()…)
hooks/ Shared React hooks (useLoaded, useNow, useMatchClock…)
lib/
routes.ts Screen codes → URLs (hrefFor, to, toItem, notFunctional)
type-scale.ts control-sizes.ts tones.ts Design-token scale maps
api/ HTTP layer: config, endpoints, client, cache, tokens, types/, modules/
auth/ onboarding/ portal/ Service contracts, their API implementations and mocks/
Data → stores → components.
src/data.src/stores/* and never import src/data directly.Design tokens.
src/app/globals.css.src/lib (type-scale.ts, control-sizes.ts, tones.ts).variant, size, tone) instead of raw classes.Screen codes are the navigation map.
DISCOVER, CHAT-01, PAY-REVIEW…).src/lib/routes.ts maps codes to URLs, and every link is built with hrefFor / to / toItem.notFunctional(reason). It renders as a disabled button carrying the reason.One URL per screen; Figma states are client states.
/membership/checkout/<plan>.#hash).Services, not fetches, in components.
src/lib/<feature>/*-service.ts and a marked mock in its mocks/ folder.fetch or storage directly.The portal frame.
PortalShell: the header, a session guard, and PortalGuard (offline / error screens).sections/portal/shared/portal-page.tsx.Workspace: a white panel plus the 352px "Flow support" column.StatusScreen.Run locally and open http://localhost:3000 + the path. In the paths, <member> is a sample member id: wildflower, goldenhour, wildrose, riverstone or northpeak.
| Area | Paths |
|---|---|
| Landing | /, /how-it-works and other sections as segments |
| Legal | /terms, /terms/<section>, /privacy, /privacy/<section> |
| Sign up | /signup, /signup/verify, /signup/verified, /signup/google, /signup/google/error |
| Sign in | /signin, /signin/verify, /signin/verified, /signin/google, /signin/google/error, /signin/session-ended |
| Onboarding | /onboarding/location, nickname, private-name, birthday, identity, lifestyle, details, interests, goals, personality, communication, photos, verify, notifications, ready |
| Discover | /discover, /discover/filters, /discover/profile/<member>, /discover/compatibility/<member> |
| Matches | /matches, /matches/cancelled, /matches/complete, /matches/match/<member> |
| Chats | /chats, /chats/<member> |
| Dates | /dates/venues/<member>, /dates/place/<member>, /dates/time/<member>, /dates/review/<member>, /dates/plan/<member>, /dates/manage/<member>, /dates/feedback/<member>, /dates/complete/<member> |
| Games | /games/<member> |
| Stories | /stories, /stories/story/first-coffee, /stories/new, /stories/mine |
| Profile | /profile, /profile/edit, , , , |
Unknown paths show the 404 page. Portal and onboarding pages send signed-out visitors to /signin; the sign-in / sign-up pages send signed-in members to /discover.
All state lives in sessionStorage for the current tab (keys start with 48date.) and is validated on read. Logging out clears it. Every mock is marked MOCK at the top of its file.
| Feature | Mock behaviour |
|---|---|
Auth (lib/auth/mocks) | Any six-digit code verifies except 000000 (wrong code) and 999999 (expired); the fourth resend hits the limit. |
Onboarding (lib/onboarding/mocks) | Nickname "GoldenHour" is taken. Area search uses a starter list. Uploads take ~2.5s and fail offline. A selfie is reviewed in ~6s; a very dark photo is sent back for a retry. |
Discovery (lib/portal/mocks/mock-portal-service.ts) | Filters apply to the sample deck. A like on GoldenHour is mutual. Match deadlines count down from the first visit. |
| Chats | Messages fail while offline (retry / edit). Every third AI suggestion request fails. Blocking hides the conversation. |
| Dates | WildRose has invited you. RiverStone's date has passed and asks about attendance. A sent invitation is answered after ~6s. |
| Games / stories | The match answers after ~2.5s. A story containing an email or phone number is sent back by moderation. |
| Profile / settings | Saves validate like the server would and fail offline. Reports, reviews and deletion are accepted, not stored. |
| Membership | Everyone starts free with one new conversation a week; a second first-hello opens the limit screen. Paying takes ~3s and fails offline. Restore finds only a purchase made earlier in the tab. |
To see offline states, switch the browser to offline in DevTools. A page opened offline shows "You're offline". An action taken offline shows that screen's own failure state.
The contract is the Postman collection in postman/ (User folder; Admin endpoints belong to the admin panel). Integration happens in phases; phase 1 is in place (44 of 129 user endpoints).
component → service contract (lib/<feature>/*-service.ts)
→ API implementation (api-*-service.ts) or the marked mock (mocks/)
→ module (lib/api/modules/*-api.ts) → client (lib/api/client.ts) → API
Each contract picks its implementation once:
export const authService: AuthService = apiEnabled ? apiAuthService : mockAuthService;
| Piece | File | Does |
|---|---|---|
| Config | lib/api/config.ts | Base URL from the env, apiEnabled, the device sent on sign-in |
| Endpoints | lib/api/endpoints.ts | Every path as a constant; dynamic segments encoded |
| Client | lib/api/client.ts | Bearer token, one shared refresh-and-retry on 401 (refresh tokens rotate), normalised ApiError, AbortSignal, upload progress |
| Sharing | lib/api/cache.ts | Deduplicates concurrent GETs and keeps values for a TTL; writes invalidate their keys; cleared on sign-out |
| Tokens | lib/api/tokens.ts | Access token in memory only; refresh token in this tab's sessionStorage |
| Types | lib/api/types/*.ts | Request and response shapes, field for field with the collection |
| Modules | lib/api/modules/*-api.ts | One typed function per endpoint, per module |
| Module | Endpoints | Screens |
|---|---|---|
| Auth | request OTP, verify, refresh, logout | sign up / sign in, OTP, log out, session guard |
| Onboarding | state, options, nickname suggestions / availability, file upload, 19 step saves | all onboarding steps (answers restored on return) |
| Verification | start, snapshot (polled every 3 s) | VERIFY-* |
| Users | me, update profile, update photos, update location, delete | PROFILE, PROFILE-EDIT, PHOTO-MANAGE, DELETE-ACCOUNT, profile cards |
| Trust | score, history | TRUST, TRUST-HISTORY, profile cards |
| Notification settings | get, update | NOTIFY-PREFS |
| Subscriptions | plans, me, entitlements, start (dummy), cancel | PLANS, checkout, MEMBERSHIP, manage, quota |
Still on the mocks until their phase: Google sign-in and account linking, discovery, matches, chat and the Socket.IO gateway, games, dates, AI suggestions, reports and blocks, the notifications inbox, success stories, private account details and trust reviews (no endpoints yet).
| Service | File | Covers |
|---|---|---|
authService | lib/auth/auth-service.ts | Phone codes, session, sign-out — API |
onboardingService | lib/onboarding/onboarding-service.ts | Steps, options, nickname, uploads, selfie review — API |
accountService | lib/portal/account-service.ts | Profile, photos, trust, notification settings, deletion — API; account details, trust reviews, reports — mock |
membershipService | lib/portal/membership-service.ts | Plans, membership, checkout, renewal, quota — API |
portalService | lib/portal/portal-service.ts | Preferences, discovery deck, decisions, matches — mock |
chatService | lib/portal/chat-service.ts | Conversations, messages, AI suggestions, block / unblock — mock |
dateService | lib/portal/date-service.ts | Venues, date plans, invitations, attendance, feedback — mock |
gameService | lib/portal/game-service.ts | This-or-That rounds — mock |
storyService | lib/portal/story-service.ts | Stories, comments, submissions — mock |
To connect another module: add its types, endpoints and modules/<module>-api.ts, write api-<feature>-service.ts implementing the existing contract, and switch the binding line. Components need no change.
Backend notes taken from the design handoff:
expiresAt) are server-owned; the client only counts down.lib/portal/account-rules.ts and return field errors in the same shape.h1.Every responsive decision is recorded under "Decisions" in AGENTS.md.
dangerouslySetInnerHTML with user content; React escapes every name, bio and message.localStorage: sign-out clears every 48date.* key and in-memory media.rel="noopener noreferrer". There are no redirect parameters, so no open redirects.npm audit reports 0 vulnerabilities.header, nav, main, footer) and exactly one h1 per page.aria-describedby.prefers-reduced-motion.Before calling work done:
npm run lint && npx tsc --noEmit && npm run build && npm audit
Then check the changed screens in the browser at the widths above and compare them with their Figma frames.
The full rule set, the Pieces registry (every reusable piece with its props) and the record of design decisions are in AGENTS.md. In short:
"use client" only on interactive leaves.pieces/<kind>/ folder that matches what they are, and are added to the registry.variant, size, tone); call sites pass className only for placement./profile/photos/profile/trust/profile/trust/activity/profile/trust/review| Settings | /settings, /settings/account, /settings/notifications, /settings/privacy, /settings/blocked, /settings/delete |
| Notifications | /notifications |
| Safety | /report, /report/<member>, /report/first-coffee |
| Membership | /membership, /membership/plans, /membership/checkout/<weekly|monthly|yearly>, /membership/restore, /membership/manage, /membership/limit |