# Ders İlan Sitesi

A Turkish private-tutor listing marketplace connecting students ("öğrenci") with tutors ("eğitmen") across all 81 provinces — a listing/discovery platform, not a video-lesson platform.

## Run & Operate

- `pnpm --filter @workspace/api-server run dev` — run the API server
- `pnpm --filter @workspace/ders-ilan run dev` — run the web frontend
- `pnpm run typecheck` — full typecheck across all packages
- `pnpm run build` — typecheck + build all packages
- `pnpm --filter @workspace/api-spec run codegen` — regenerate API hooks and Zod schemas from the OpenAPI spec
- `pnpm --filter @workspace/db run push` — push DB schema changes (dev only)
- `pnpm --filter @workspace/db run seed` — seed 81 cities/regions, an admin user, 8 demo instructors (with generated photos), one pending instructor, and one demo student
- Required env: `DATABASE_URL` — Postgres connection string; object storage secrets (`DEFAULT_OBJECT_STORAGE_BUCKET_ID`, `PRIVATE_OBJECT_DIR`, `PUBLIC_OBJECT_SEARCH_PATHS`) are already provisioned

## Stack

- pnpm workspaces, Node.js 24, TypeScript 5.9
- API: Express 5, custom JWT auth (bcryptjs + jsonwebtoken, signed with `SESSION_SECRET`) — not Clerk/Replit Auth, chosen because of the CV-style registration + admin approval workflow
- DB: PostgreSQL + Drizzle ORM
- Object storage: Replit App Storage (GCS-backed) for instructor photo/CV/student-document uploads, via `@workspace/object-storage-web` (Uppy-based) on the client
- Frontend: React + Vite, wouter routing, TanStack Query, shadcn/ui, Tailwind
- Validation: Zod (`zod/v4`), `drizzle-zod`
- API codegen: Orval (from OpenAPI spec)

## Where things live

- DB schema: `lib/db/src/schema/` (`users.ts`, `cities.ts`, `contactMessages.ts`), seed data in `lib/db/src/seed.ts`
- API contract: `lib/api-spec/openapi.yaml`
- API server routes: `artifacts/api-server/src/routes/` (auth, instructors, geo, contact, admin, storage)
- Web app: `artifacts/ders-ilan/src/` — pages in `pages/`, auth context in `lib/auth.tsx`
- Demo instructor photos: `artifacts/ders-ilan/public/demo/`

## Architecture decisions

- Single `users` table with a `role` enum (`student`/`instructor`/`admin`) covers both signup roles' fields; `approvalStatus` (`pending`/`approved`/`rejected`) gates instructor visibility; `isVip` flags featured instructors. Admin is seeded, not signed up publicly.
- Turkey is modeled as 7 traditional geographic regions (Marmara, Ege, Akdeniz, İç Anadolu, Karadeniz, Doğu Anadolu, Güneydoğu Anadolu) plus a full 81-row `cities` table (name + region), so every province shows even with 0 instructors.
- No real payment integration — the 120₺ instructor membership fee is informational only (Pricing page + signup note).
- File URLs: real uploads are stored as `/objects/...` and served through `/api/storage${path}`; demo/seed photos are plain public paths (e.g. `/demo/foo.jpg`) served directly by the web app. The frontend's `getFileUrl()` helper in `src/lib/utils.ts` picks the right prefix — always use it instead of hardcoding `/api/storage${x}`.

## Product

- Public: home (hero, VIP instructors, region breakdown, all-province instructor counts, stats), instructor browse/search/filter, instructor detail, "Öğrenciler" explainer page, About, Contact, Pricing.
- Auth: role-choice registration (student vs. instructor, with CV-style fields + instructor document uploads), login, profile (shows instructor approval status).
- Admin: moderation dashboard to approve/reject pending instructors and toggle VIP status.

## User preferences

_None recorded yet._

## Gotchas

- When adding fields to the OpenAPI spec, do not use `format: email` / `format: uri` — orval v8's zod client emits Zod-v4-only methods (`z.email()`, `z.url()`) that break the workspace's pinned zod v3. Use plain `type: string` instead.
- Frontend imports from `@workspace/api-client-react` should come from the package root (`.`), not subpaths like `/custom-fetch` or `/api.schemas` — everything (hooks, types, `setAuthTokenGetter`) is re-exported from the main barrel.

## Pointers

- See the `pnpm-workspace` skill for workspace structure, TypeScript setup, and package details
- See the `object-storage` skill for the presigned-upload flow used in instructor registration
