dim

Dayaq — first DİM preparation demo

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.

Run locally (no external accounts required)

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.

Try the content/admin demo

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.

Verify the build

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

Deploy to Vercel with Supabase

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.

  1. Create a Supabase project. Enable email/password authentication.
  2. Obtain its PostgreSQL connection string (server-side owner connection; Supabase pooler is supported with prepared statements disabled), project URL, and anon/publishable legacy API key.
  3. Set .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.
  4. Configure the same hosted variables in Vercel; deploy the main branch.
  5. In Supabase Auth URL Configuration, set Site URL to the exact production HTTPS origin, and allow 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.
  6. Register the operator account and assign ADMIN using npm run db:set-role -- EMAIL ADMIN from a trusted terminal configured for the hosted database.
  7. Smoke-test a full student journey and admin publication on the deployment. Local browser tests do not prove live Supabase delivery or hosting configuration.

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.

Current scope and limitations

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.

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:

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.

Troubleshooting