Azerbaijani exam-preparation application built with Next.js 16.3, React, strict TypeScript, Tailwind, Drizzle/PostgreSQL and Supabase Auth. Original content, deterministic grading, versioned attempts, and server authorization.
This is a working first demo, not a launch-ready education business. All seed content and mock scoring are explicitly demonstrations, not official DİM questions or predicted DİM scores. No default passwords or fabricated student activity are included.
Install Node.js 24 LTS, version 24.15 or newer, and Git. Node 22.22.2+ is also supported by the dependencies. Use the committed lockfile.
git clone git@github.com:yusifm9/dim.git
cd dim
npm install
Copy the example environment file:
# Windows PowerShell
Copy-Item .env.example .env.local
# macOS / Linux
cp .env.example .env.local
Then run:
npm run db:migrate
npm run db:seed
npm run dev
Open http://localhost:3000 (the example environment and dev server use this host). Register your own account, choose I qrup Rİ / 2026 / Azərbaycan dili, then open Riyaziyyat. The initial dashboard is intentionally empty until you complete activities.
npm run dev. Local mode needs no external service, credentials or internet connection once dependencies are installed..local/, excluded from Git.db:migrate executes the actual PostgreSQL migration and policies in an embedded PostgreSQL instance to validate them. The demo itself uses the atomic file adapter; it does not need a database server.db:seed is safe to repeat and does not erase existing accounts or progress. On an existing local store it inserts only demonstration content that store has never held, matched by identifier, so pulling new seed content (flashcards, key-concept sheets, papers) needs no reset and an edited or archived item is never overwritten..local/mail-outbox.ndjson. Open the latest matching URL in your browser. They are never returned by the public API. Local registration can be used before email confirmation; hosted Supabase can require confirmation.Register a separate account normally, then grant its role from your terminal:
npm run db:set-role -- your-admin@example.com ADMIN
# Optional separate editor:
npm run db:set-role -- your-editor@example.com CONTENT_EDITOR
Open /admin. An editor can create a question, preview it, save a draft and submit it for review. An administrator can publish it. Existing attempts retain their original question snapshot. Administrator pages also manage curriculum, new exam tracks, per-subject question-type allocations, topics, lessons, mocks, roles, prices, manual entitlements and audit records. Question and lesson editors share safe rich-block controls; both support subtopic assignment.
For import, export an existing original question to see the schema. JSON accepts an array or { "questions": [...] }; CSV encodes nested arrays/answer keys as JSON cells. Imported rows must be drafts. Preview validation must pass before transactional commit.
npm run lint
npm run typecheck
npm test
npm run build
npm run test:e2e
Browser tests use installed Microsoft Edge on Windows. On other systems install Chromium once with npx playwright install chromium. Browser tests launch their own server on port 3100 and use isolated .local-e2e/ data. Stop a manually started dev server before testing/building to avoid competing Next.js output writes.
To preview the compiled build locally (terminal only; never configure this on Vercel):
$env:ALLOW_LOCAL_PRODUCTION='true'
npm start
ALLOW_LOCAL_PRODUCTION=true npm start
The repository includes vercel.json; select the Next.js framework and Node.js 24.x. Build command is npm run build, install command is npm ci. No migration or destructive seed runs automatically during deployment.
.env.local temporarily to the hosted configuration below and run npm run db:migrate, then npm run db:seed. The scripts use a migration checksum ledger and normalized seed data. Keep real credentials out of Git.main branch.https://YOUR-DOMAIN/auth/callback. Set the email confirmation/recovery templates and email delivery settings for your project. Verify registration, verification and password reset with a real mailbox before public use.npm run db:set-role -- EMAIL ADMIN from a trusted terminal configured for the hosted database.Required Vercel variables:
| Variable | Value |
|---|---|
APP_MODE |
supabase |
DATABASE_URL |
Supabase PostgreSQL URI; server secret, SSL required for remote database |
NEXT_PUBLIC_SUPABASE_URL |
https://PROJECT.supabase.co |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase anon/public key; access still enforced by server and RLS |
APP_URL |
Exact production origin, e.g. https://example.vercel.app |
NEXT_PUBLIC_APP_URL |
Same exact production origin, without trailing slash |
EPOINT_ENABLED |
false for this demo |
Leave ALLOW_LOCAL_PRODUCTION false/unset and do not deploy local mode. Hosted storage fails closed if credentials are missing; local filesystem data is never a production fallback. .env* and local stores are excluded from Git and server output tracing. Editor media uploads require server-only SUPABASE_SERVICE_ROLE_KEY and SUPABASE_MEDIA_BUCKET; follow private media setup and verify bucket policies before deployment.
The GitHub Actions workflow runs migrations/RLS, isolated seed, lint, route type generation, typecheck, tests, production build and the browser journeys on pushes to yusif-dev/main and pull requests. It uses Node 24, pinned action commits, read-only repository permissions and no production credentials. Browser failure diagnostics are retained for seven days; local stores and environment files are never uploaded.
The PostgreSQL role used by the server can read private answer keys and auth state; never expose DATABASE_URL to clients. Student-facing tables use explicit RLS; answer keys, raw snapshots, credentials and operational state live in a private schema. No dashboard/admin route is publicly indexed.
Working locally: homepage/catalogue, register/login/logout, local verification/reset flow, onboarding, truthful dashboard, subject/topic lessons, completion/bookmarks, all seven answer controls, server grading/solutions, custom sample practice, mistakes, mastery/recommendations, timed mocks with autosave/refresh recovery, result breakdowns, and editorial draft/review/publish/import/export.
The study tools added on top of that core. Navigation is subject-first: a subject owns its own tools at
/dashboard/subjects/<subject>/<tool>, so no surface mixes subjects together. Opening a tool from the sidebar
without a subject shows a subject picker with per-subject counts rather than a combined list. Mistakes stay
deliberately cross-subject, since that list is about your own wrong answers rather than one syllabus.
CUSTOM_TESTS.The question bank now applies answered, unanswered, incorrect and bookmarked filters to both the displayed list/count and generated sessions. An unsubmitted draft or blank response remains unanswered; a submitted written response awaiting review counts as answered.
Boundaries of this demo:
official: false.See docs/implementation-plan.md, docs/progress.md, docs/security.md, docs/database.md and docs/production-checklist.md for the implementation details and operational limits.
APP_URL and NEXT_PUBLIC_APP_URL identical to the browser origin (http://localhost:3000 by default). If upgrading from the first demo, change both old 127.0.0.1 values to localhost. Restart after changing env values. Production still requires an exact configured HTTPS origin./dashboard/account..local/*.lock is no longer running, then remove only the stale lock. Never remove a live writer’s lock.npm.cmd in place of npm if the shell refuses npm.ps1.git push -u origin yusif-dev. No force push is needed.