Appearance
DoCurious — Architecture Cheat Sheet
One-pager for engineers joining the project.
Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Framework | React | 19 |
| Language | TypeScript | 5.9 |
| Build | Vite | 7 |
| Styling | Tailwind CSS 4 | @tailwindcss/vite |
| UI Primitives | shadcn-style src/components/ui/ | Button, Card, Dialog, etc. |
| State | Zustand | 5 |
| Routing | React Router | 7 |
| Forms | React Hook Form | 7 |
| Animation | Framer Motion | 12 |
| i18n | react-i18next | 14 |
| Testing | Vitest + Testing Library | 4 |
| Backend | Express + TypeScript | 5 |
| ORM | Prisma | 6 |
| Database | PostgreSQL | 17 |
| Auth | JWT (access + refresh tokens) | Custom |
How Data Flows
PostgreSQL
|
v
Prisma (server/prisma/schema.prisma)
|
v
Service Layer (server/src/services/*.ts)
| Business logic, validation, AppError
v
Controller Layer (server/src/controllers/*.ts)
| HTTP contract, req/res handling
v
Route Layer (server/src/routes/*.ts)
| Express routes, middleware, Zod validation
v
────── HTTP / JSON ──────
|
v
Real API Layer (src/api/*.real.api.ts)
| fetch() calls to /api/portal/...
v
Adapter Layer (src/adapters/*.ts)
| UUID→id promotion, status mapping, field renaming
v
Zustand Store (src/store/use*Store.ts)
| State management, selectors, actions
v
React Components (src/pages/ + src/components/)Mock vs Real
Every domain has TWO API files:
src/api/challenge.api.ts— Mock (in-memory, instant, used in dev)src/api/challenge.real.api.ts— Real (calls Express backend)
Stores can switch between them. The mock layer is powered by mockDb.ts + seed.ts (3,008 lines of seed data).
Project Structure
src/
├── adapters/ # SQL→FE type mapping (idAdapter, statusMaps, entity adapters)
├── api/ # Mock APIs (*.api.ts) + Real APIs (*.real.api.ts)
├── components/
│ ├── ui/ # shadcn-style primitives (Button, Card, Dialog, Input...)
│ ├── challenge/ # ChallengeCard, ChallengeGrid, ChallengeDiscussion
│ ├── community/ # CommunityCard, FeedPost, MemberList, GoalCard
│ ├── explore/ # DealersChoice, HorizontalRow, FilterPanel, CalendarView
│ ├── filters/ # FilterSidebar, FilterModal, AppliedFilters
│ ├── gamification/ # BadgeCard, LevelProgress, StreakDisplay, Leaderboard
│ ├── gift/ # GiftCard, GiftModal
│ ├── layout/ # AppLayout, Header, Sidebar, AdminNav
│ ├── notifications/ # NotificationCenter, grouped rendering
│ ├── search/ # SearchFilterBar, LocationFilter
│ ├── trackRecord/ # EntryCard, MediaUpload, TrackRecordView
│ ├── common/ # ErrorBoundary, EmptyState, CookieConsent
│ └── debug/ # Debug Panel (Ctrl+Shift+D), dev-only
├── hooks/ # Custom hooks (useFeatureFlag, etc.)
├── i18n/ # react-i18next config + en/es/fr locales
├── lib/ # Pure utility functions
├── pages/
│ ├── auth/ # Login, Register, ForgotPassword, Onboarding (10 pages)
│ ├── public/ # Legal pages: ToS, Privacy, DPA, DMCA, Cookie (9 pages)
│ ├── challenges/ # ChallengeDetail, MyChallenges, TrackRecord (6 pages)
│ ├── explore/ # SavedList, CategoryView, DealersChoice, MapView (4 pages)
│ ├── communities/ # Communities, CommunityDetail, Goals (6 pages)
│ ├── school/ # SchoolDashboard + 20 school admin pages
│ ├── admin/ # 65+ admin toolkit pages
│ ├── vendor/ # VendorDashboard + 10 vendor pages
│ ├── parent/ # ParentDashboard
│ ├── profile/ # Profile, JourneyMap, Badges, XPHistory, Reflections
│ ├── portfolio/ # Portfolios, PortfolioView
│ ├── account/ # Deletion, DataExport, Appeal, LinkedAccounts
│ └── *.tsx # Dashboard, Explore, Account, Notifications (top-level)
├── routes/
│ ├── index.tsx # Router definition (150+ routes, lazy loaded)
│ └── guards/ # AuthGuard, ContextGuard, TierGuard, ParentGuard, AgeGuard
├── store/ # 24 Zustand stores
├── theme/ # CSS variable theme system, ThemeProvider
└── types/ # 27 TypeScript type definition files
server/
├── prisma/
│ └── schema.prisma # 60+ models (40 legacy + 20 modern)
├── src/
│ ├── controllers/ # 19 controllers
│ ├── routes/ # 23 route files
│ ├── services/ # 20 service files
│ ├── middleware/ # auth, requireRole, validate, errorHandler
│ ├── utils/ # jwt, hash, email stubs
│ └── __tests__/ # 19 test files (vitest + supertest)
└── package.jsonKey Patterns
1. Zustand Stores
typescript
// Stores follow this pattern:
export const useChallengeStore = create<ChallengeState>((set, get) => ({
challenges: [],
isLoading: false,
fetchChallenges: async () => { /* calls API, sets state */ },
}))
// Access in components:
const { challenges, fetchChallenges } = useChallengeStore()2. Adapter Layer
typescript
// Adapters convert SQL snake_case → FE camelCase, promote uuid→id
import { adaptChallenge } from '../adapters/challengeAdapter'
const feChallenge = adaptChallenge(sqlRow) // uuid→id, status mapping, etc.3. Route Guards
typescript
// Nested guard pattern in router:
<AuthGuard> // Must be logged in
<ContextGuard // Must have correct context (school/business/platform)
context="school"
roles={['school_admin', 'teacher']}
>
<SchoolDashboard />
</ContextGuard>
</AuthGuard>4. Backend Middleware Stack
typescript
// Routes use this pattern:
router.post('/:id/classes',
requireAuth, // JWT check
requireRole('school_admin'), // Role check
validate(createClassSchema), // Zod validation
schoolCtrl.createClass // Handler
)User Roles
| Role | Context | Access |
|---|---|---|
| General User | personal | Explore, challenges, communities, gamification |
| Student Tier 1 | school | School-only, no private communities/gifts |
| Student Tier 2 | school + personal | Parent-linked, full access |
| Parent | personal | View child activity, consent controls |
| Teacher | school | Class management, assignments, reviews |
| School Admin | school | Full school management, roster, settings |
| Vendor | business | Challenge authoring, analytics, events |
| Platform Admin | platform | Everything + 65 admin tools |
Database: Dual Schema
Legacy models (40+): Direct from MySQL dump. BigInt IDs, snake_case. Used by prodData.service.ts for read-only access to real production data.
Modern models (20+): UUID-based, camelCase. AppUser, AppChallenge, UserChallenge, TrackRecord, XPEvent, Badge, etc. Used by all active services.
Status mapping: ACTIVE→approved, INACTIVE→draft, PENDING→pending_review, archived (new status).
Dev Tools
| Tool | How to Access |
|---|---|
| Debug Panel | Ctrl+Shift+D — 6 tabs: state, network, flags, theme, routes, logs |
| Storybook | npm run storybook — 55 component stories |
| VitePress Docs | npm run docs:dev — knowledge base + dev guide |
| TypeDoc API | npm run docs:build — auto-generated from source |
| Tests | npm test (FE) / cd server && npm test (BE) |