0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • Introduction
  • Help center
  • Common questions
  • 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
  • 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
  • Bulk import
  • Playwright
  • Prettier
  • Block Rebound
  • Transportation

Listings

PreviousNext

Unified pattern for data tables, grids, and CRUD operations — optimistic updates, URL-persisted views, infinite scroll, type-safe queries.

Listings are the backbone of data-driven features. 11 entities follow the same shape — students, teachers, classrooms, assignments, etc. — with consistent UX and optimistic updates.

Features

FeatureBehaviour
Optimistic updatesAdd / update / remove instantly with automatic rollback on errors
URL-persisted viewsTable / grid view toggle via URL query params (nuqs)
Infinite scrollLoad-more pagination, no page reloads
Centralised queriesType-safe query builders with role-based authorisation per module
Name displaySchool picks first + second or first + last name (School → Configuration → Name Display); compound Arabic names (عبد الله, أبو بكر) stay whole, names are bidi-isolated
Login credentials6-digit username (year + school serial) and an easy 4-digit PIN; first sign-in asks for a new password; admins can always reset

File patterns

Every listing follows the mirror pattern — the URL produces two directories: one in app/ for routing, one in components/<feature>/ for everything else.

FilePurpose
content.tsxServer component — data fetching, tenant context
table.tsxClient component — interactive table with state
columns.tsxColumn definitions (client, uses hooks)
form.tsxCreate / edit form with validation
actions.tsServer actions — validate, scope tenant, mutate
queries.tsQuery builders with Prisma
authorization.tsRBAC permission checks
validation.tsZod schemas + refinements
types.tsTypeScript interfaces
config.tsConstants, options, labels
list-params.tsURL params (nuqs)
README.mdFeature documentation
ISSUE.mdKnown issues tracker

Page composition

PageTitle              src/components/atom/page-title.tsx
  Abc Management — "Manage all abc records in your school"

PageNav                src/components/atom/page-nav.tsx
  All | Active | Archive | Settings

Toolbar                src/components/atom/toolbar.tsx
  [search] [filters]  [view ≡/⊞] [export ↓] [create +]

DataTable              src/components/table/data-table.tsx
OR GridContainer       src/components/atom/grid-container.tsx

Modal (when open)      src/components/atom/modal/modal
  ModalFormLayout
    Form (listings/abc/form.tsx)
  ModalFooter
    Step 1 of 2: Basic Info     [Cancel] [Next]

Component flow

Route (page.tsx)
  → Content (content.tsx) ── Server
      • getTenantContext()
      • Parse URL params
      • Fetch via queries.ts
  → Table (table.tsx) ── Client
      • useModal()
      • usePlatformView()
      • useDataTable()
      ├─ Toolbar (Search, ViewToggle, Export, Create)
      ├─ DataTable / GridContainer (Columns, row actions, pagination)
      └─ Modal (Form, ModalFormLayout, ModalFooter)

Reference implementation

Grades is the canonical reference. All paths under src/components/school-dashboard/listings/grades/:

PatternFile
Server actionsactions.ts
Queriesqueries.ts
Authorizationauthorization.ts
Validationvalidation.ts
Tabletable.tsx
Columnscolumns.tsx
Formform.tsx
Contentcontent.tsx

Core hooks

HookPathPurpose
usePlatformDatasrc/hooks/use-platform-data.tsData fetching with optimistic updates and infinite scroll
usePlatformViewsrc/hooks/use-platform-view.tsView mode (table / grid) with URL persistence
useDataTablesrc/components/table/use-data-table.tsTanStack Table state management
useModalsrc/components/atom/modal/context.tsxModal open / close state

Modules

All 11 listings ship table, grid, search, and export:

Announcements, Assignments, Classes, Classrooms, Events, Grades, Parents, Staff, Students, Subjects, Teachers.

Teachers are assigned to subjects in sections, not to classes: the wizard's Subjects & sections step and the Assign subjects row action share one editor, and saving moves the teacher onto those subjects' periods in the timetable (see timetable, "Assigning teachers to subjects"). The editor picks one grade at a time from G1…G12 chips and shows its subjects as a swipeable row of thumbnail cards; tapping a card takes every free section, and the section chips on the card hand over one section. Grades are entered per student and subject — the student's section, grade and term come with them — so a school without classes can still record a score. Assignments are set for a grade — one section or the whole grade — and a subject; students see and submit the ones set for their section or grade.

Subjects additionally carries a school-side customization layer over the platform catalog: on the subject detail page admins get a Customize panel (SchoolCatalogCustomization) to hide chapters / lessons / a specific instructor's video, hide a lesson's quiz, set the preferred instructor source, and contribute the school's own videos. It writes per-school ContentOverride / InstructorPreference rows enforced by the lumos (LMS) read paths — see the catalog docs (“School-side controls”).

Best practices

Multi-tenant safety

  • Always include schoolId in every database query.
  • Get from session: const { schoolId } = session.user.
  • Missing schoolId breaks tenant isolation.

Column definitions

  • Define handlers before the columns useMemo.
  • Columns with hooks (useModal) must be generated in client components.
  • Pass callbacks via getColumns(dictionary, lang, { onDelete, onEdit, onView }).

Optimistic updates

  • Call optimisticRemove(id) before server request.
  • Call refresh() on error to rollback.
  • Use optimisticUpdate(id, updater) for in-place updates.

Server actions

  • Start with "use server".
  • Validate with Zod on both client (UX) and server (security).
  • Call revalidatePath() or redirect() after mutations.
  • Return typed ActionResponse<T>.

Server action errors

  • Return an ACTION_ERRORS code (actionError(ACTION_ERRORS.SAVE_FAILED)), never error.message. A Zod or Prisma message is not a sentence for an admin.
  • Log the real error on the server (console.error) before returning the code.
  • In the form, toast actionErrorMessage(result.error, dictionary, fallback) from @/lib/resolve-action-error. It returns the translated sentence for the code, or the fallback instead of a raw SNAKE_CASE code.
  • In a catch, toast the translated fallback, never err.message.
  • Wizards finish through one function per entity (students/wizard/finish.ts, teachers/wizard/finish.ts), so the last step's button and the footer's skip-and-create behave the same way.

Wizard drafts

  • Opening an add wizard creates the row up front, because the URL carries its id.
  • Closing before entering anything deletes it. The lists never show an empty draft, and a nightly cron removes the ones a closed tab left behind (empty-drafts.ts holds the single rule).
  • A draft with any data is kept and can be resumed from the list.
  • The student, teacher and parent wizards open instantly. "+" mints the draft id in the browser, opens the wizard on an empty seed and writes the row in the background (useDraftLauncher).
  • Their steps switch without a request (WizardLayout steps, history.pushState), and Next saves in the background through the wizard runtime. Create waits for every save, and a failed save brings its step back.
  • A step stays mounted after you leave it. An effect that initialises from the loaded row must run once per record, not on every show.

Modal forms

  • Use ModalFormLayout for two-column header / form layout.
  • Use ModalFooter for progress bar and navigation.
  • Call onSuccess() after successful mutations.

Maturity levels

LevelFeatureStatus
1Basic CRUDComplete
2Relationship context (counts, quick links)In progress
3Rich detail pages with related data tabsIn progress
4Cross-listing actions (bulk operations)In progress
5Automated workflows (triggers, notifications)Planned

See also

  • Pattern — mirror pattern, naming conventions
  • Students — listing with wizard flow
  • Multi-tenancy — schoolId scoping
InspirationTeachers

On This Page

FeaturesFile patternsPage compositionComponent flowReference implementationCore hooksModulesBest practicesMulti-tenant safetyColumn definitionsOptimistic updatesServer actionsServer action errorsWizard draftsModal formsMaturity levelsSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.