- Introduction
- Pitch
- Hogwarts
- Live Demo
- MVP
- Roadmap
- Launch Sprint
- PRD
- Get Started
- Localhost
- Architecture
- Structure
- Pattern
- Page
- Layout
- Content
- Types
- Config
- Actions
- Queries
- Authorization
- Validation
- Form
- Table
- Detail
- Card
- Util
- Hooks
- List Params
- Views
- README.md
- ISSUE.md
- Technology Stack
- Database
- File
- CDN Assets
- Entry Points
- Dashboard
- Authentication
- Credentials
- OAuth
- Flow Diagrams
- Multi-Tenancy
- Offline
- Onboarding
- Onboarding Videos
- Add Values
- Admission
- Application
- Attendance
- Compliance
- Profile
- Exams
- Exam Wizard
- Timetable
- Classrooms
- Notifications
- Conference
- LMS (Lumos)
Finance
- Finance
- Fee Management
- Invoice
- Wallet
- Salary
- Payroll
- Timesheet
- Expenses
- Budget
- Receipt
- Accounts
- Banking
- Reports
- Dashboard
- Permissions
- Messages
- Integration Flow
- Provision
- AI Document Processing
- Document Intelligence
- Internationalization
- Translation
- Translation Guide
- Icons
- Docs Factory
- Inspiration
- Listings
- Teachers
- Students
- Catalog
- Library
- Contributing
- Code of conduct
- GitHub Workflow
- Database Seeds
- Database Safety
- Test Accounts
- Playwright
- Prettier
- Block Rebound
Sales & GTM
Consistency is the foundation. Every feature follows the same shape; every file has a canonical role. New code that matches these patterns is reviewable in minutes; new code that doesn't is friction.
Naming
Files and folders
- Components, files — kebab-case (
button.tsx,user-profile.tsx). - Routes — kebab-case segments (
/user-profile,/sign-in). - Hooks —
use-prefix (use-leads.ts). - Types — PascalCase identifiers (interfaces, type aliases, enums).
- Constants —
UPPER_SNAKE_CASEfor primitives,camelCasefor object literals.
Identifiers in code
- Components — PascalCase (
export function UserCard()). - Functions — camelCase (
formatCurrency). - Variables — camelCase (
const userData = ...). - Constants —
UPPER_SNAKE_CASE(const API_BASE_URL = "..."). - Types and interfaces — PascalCase (
interface UserData,type ApiResponse).
Database and API
- Tables — snake_case (
user_profiles). - API routes — kebab-case (
/api/user-profile). - Environment variables —
SCREAMING_SNAKE_CASE(DATABASE_URL).
Function patterns
Use function declarations for components, API route handlers, and exported functions — they hoist and produce better stack traces. Use arrow functions for short utility callbacks.
// utility — arrow is fine
const formatPrice = (amount: number) =>
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount)
// component — function declaration
export function UserProfile({ userId }: Props) {
const { user, loading } = useUser(userId)
if (loading) return <Skeleton />
return <Card>{user.name}</Card>
}Server actions and data fetching
- Mutations live in
actions.tswith"use server"at the top. - Reads live in
queries.ts. Pure server functions, no"use server"needed unless called from client. - Every server action runs the 5-step flow: auth → tenant → permission → validate → execute + revalidate.
- Server actions return
ActionResponse<T>from@/lib/action-response. - Server components fetch via
queries.tsand pass data to client components as props.
Component hierarchy
| Level | Name | Description | Example |
|---|---|---|---|
| 1 | UI | Radix primitives | Dialog, Tabs |
| 2 | Atom | 2+ primitives composed | LabeledInput |
| 3 | Template | Full-page layout | WizardLayout |
| 4 | Block | UI + business logic | StudentsTable |
| 5 | Micro | Mini service / standalone widget | BellIcon |
Mirror pattern
Every URL produces two directories — one in app/ (routing, layouts, page.tsx), one in components/<feature>/ (everything else). Read the architecture doc for the full mapping.
src/app/[lang]/s/[subdomain]/(school-dashboard)/students/page.tsx
→ imports from src/components/school-dashboard/listings/students/content.tsx
Standard file patterns per feature
| File | Purpose | Doc |
|---|---|---|
page.tsx | Route entry. Auth + tenant + render <XxxContent />. Thin. | page |
content.tsx | Server (data fetching) or client (interaction) — composes the feature. | content |
client.tsx | Single "use client" boundary; receives server data as props. | content |
actions.ts | Server actions — mutations only. | actions |
queries.ts | Server-side database reads. | queries |
authorization.ts | RBAC permission checks (canCreate, canRead, …). | authorization |
validation.ts | Zod schemas + z.infer types. | validation |
types.ts | Domain and UI types. | types |
config.ts | Enum option arrays, labels, defaults. | config |
form.tsx | Client. Renders inputs, runs RHF + Zod, submits to a server action. | form |
table.tsx | Client. Wraps the DataTable atom with feature columns. | table |
columns.tsx | Client. Column definitions; uses useMemo if hooks are involved. | table |
card.tsx | KPI / summary cards. | card |
detail.tsx | Detail page composition. | detail |
views.tsx | View toggles (grid / list / kanban). | views |
list-params.ts | URL search-param cache (nuqs). | list-params |
util.ts | Pure feature helpers. | util |
hooks.ts / use-<x>.ts | Feature hooks. | hooks |
README.md | Block-level context (decisions, danger zones). | readme |
ISSUE.md | Open issues, completed log. | issue |
The full canonical content per file lives in the linked doc — this page is the index.
Multi-tenant rules
- Every business model carries
schoolId. - Every database query is scoped by
schoolId. MissingschoolId= data leak. getTenantContext()is the source of truth for the currentschoolId.- Subdomain detection happens in middleware; tenant context resolves it.
See Multi-Tenancy for the full architecture.
TypeScript
- Strict mode on. No
any, noas any. import typefor type-only imports.- Derive types from Zod via
z.infer. Derive Prisma row types viaPrisma.XxxGetPayload. - Discriminated unions for state machines (
ActionResponse<T>, wizard step state). enumonly when the values cross runtime boundaries; otherwise prefer string literal unions.
Styling
- Tailwind 4 with OKLCH tokens.
- Semantic HTML — no hardcoded
text-*/font-*in feature code; trust the renderer. - Theme-aware tokens only —
text-foreground,bg-card,border-border. No hex. - Logical properties for RTL —
ms-,me-,ps-,pe-,start-,end-. Neverml-,pl-,left-,right-.
Error handling
- Server actions return
errorCode, never English strings. The dictionary maps them on the client. - Never
throwto the client. Catch in the action and return a structured response. error.tsxboundaries handle component-tree exceptions.console.errorfor server-side diagnostics; neverconsole.login shipped code.
Performance
- Server components by default;
"use client"only when needed. - Parallelise independent awaits with
Promise.all. revalidatePathafter mutations.- Wrap expensive computations in
useMemo; wrap stable callbacks inuseCallbackonly when measured. - Lumos where possible —
Suspenseboundaries inside server components.
Adoption
When adding new code:
- Pick the closest existing feature directory and copy its shape.
- Match the naming and file set exactly.
- Resist the urge to invent a new file (
utils.ts,helpers.ts); use the canonical names. - If something genuinely doesn't fit, add a note in
README.mdso the next person knows why.