- 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
util.ts holds pure feature helpers — formatters, validators, sort/filter functions, calculations. No React, no hooks, no side effects. Server-safe and testable.
Categories
| Category | Purpose | Reference |
|---|---|---|
| Formatting (~6 files) | Currency, date, time, period strings | billing/util.ts |
| Validation (~4 files) | Domain, email, URL format checking | domains/util.ts |
| Status / health (~3 files) | Map status enums to labels, variants, colors | domains/util.ts |
| Navigation (~2 files) | Wizard step progression, progress calculation | onboarding/util.ts |
| Sorting / filtering (~3 files) | Pure array transformations | domains/util.ts |
| Calculations (~4 files) | Grade calculation, accounting totals | finance/lib/accounting/utils.ts |
| Storage (~2 files) | Draft persistence, localStorage helpers | onboarding/util.ts |
Naming
| Name | Scope | Location |
|---|---|---|
util.ts | Feature-scoped (singular) | src/components/<feature>/util.ts |
utils.ts | Standalone / library (plural) | src/components/<feature>/lib/utils.ts |
Rules
| # | Rule |
|---|---|
| 1 | Pure functions only — no React, no hooks, no state |
| 2 | Import constants from config.ts, types from types.ts |
| 3 | No side effects — no DB, no API, no console.log |
| 4 | JSDoc on every exported function |
| 5 | Stay under ~200 lines — split into lib/ subdirectory by concern |
Canonical example
import {
DOMAIN_STATUS_LABELS,
RESERVED_DOMAINS,
VALIDATION_RULES,
} from "./config"
import type { DomainStatus, DomainValidationResult } from "./types"
/**
* Get domain status label
*/
export function getDomainStatusLabel(status: DomainStatus): string {
return DOMAIN_STATUS_LABELS[status]
}
/**
* Get domain health indicator
*/
export function getDomainHealth(
status: DomainStatus
): "healthy" | "warning" | "critical" {
switch (status) {
case "verified":
return "healthy"
case "approved":
case "pending":
return "warning"
case "rejected":
return "critical"
default:
return "warning"
}
}
/**
* Validate domain with detailed error messages
*/
export function validateDomain(domain: string): DomainValidationResult {
const errors: string[] = []
const warnings: string[] = []
if (!domain || domain.trim() === "") {
errors.push("Domain is required")
return { isValid: false, errors, warnings }
}
const trimmed = domain.trim().toLowerCase()
if (trimmed.length < VALIDATION_RULES.DOMAIN_MIN_LENGTH)
errors.push("Too short")
if (!VALIDATION_RULES.DOMAIN_PATTERN.test(trimmed))
errors.push("Invalid format")
return { isValid: errors.length === 0, errors, warnings }
}
/**
* Normalize domain (remove protocol, www, trailing slash)
*/
export function normalizeDomain(domain: string): string {
return domain
.trim()
.toLowerCase()
.replace(/^https?:\/\//, "")
.replace(/^www\./, "")
.replace(/\/$/, "")
}Server-only utilities
For helpers that import server-only modules:
import "server-only"
export function getSchoolSecret(schoolId: string): string {
return process.env[`SCHOOL_${schoolId}_SECRET`] || ""
}Anti-patterns
- 6 different
formatCurrencyimplementations — consolidate tosrc/lib/formatting.ts. - Sort-by-date / sort-by-name duplicated across 4+ files — generic
sortByField<T>insrc/lib/sorting.ts. - Bloated files —
accounting/utils.ts(507 lines) — split by concern (formatting, calculations, validation). - Hooks in
util.ts— move touse-*.ts. useState,useEffect— never in util.
What belongs where
| Content | In | Not in |
|---|---|---|
| Pure formatting | util.ts | card.tsx, form.tsx |
| Validation helpers | util.ts | validation.ts (Zod schemas only) |
| Status label mapping | util.ts | config.ts |
| Sort / filter | util.ts | table.tsx, all.tsx |
| Navigation helpers | util.ts | form.tsx |
| Constants / enums | config.ts | util.ts |
| React hooks | use-*.ts | util.ts |
| Side effects (DB, API) | actions.ts | util.ts |