尚未检测到更新。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 |