0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • 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
  • Marketing Brief
  • Sales
  • Go-to-market
  • Marketing
  • Admission — Feature Spotlight
  • Pilot Program
  • Leads
  • Proposal
  • Outreach Templates
  • Case Study
  • Competitors
  • Landing-page teardown
  • Competitor FAQ
  • Business model
  • Shared economy
  • Traction
Fundraising & Ecosystem
  • Get Support
  • Investor Deck
  • Data Room
  • Investors
  • Accelerators
  • Incubators
  • Grants
  • Sponsors
  • Partners
  • Competitions & Hackathons
  • Universities & Training Centers

Onboarding

PreviousNext

Multi-step wizard for school registration, configuration, and business setup. 16 steps in 3 groups, ~166 files.

The onboarding system guides new schools through registration, configuration, and business setup. Mirror pattern, ~166 files, 16 active steps in 3 groups, driven by WizardLayout. Typical completion: 10–30 minutes.

Steps

Group 1 — Basic information

StepPathRequiredDatabase
About school/about-schoolNostatic welcome
Title/titleYesSchool.name
Description/descriptionYesschoolType, schoolLevel, planType
Location/locationYesaddress, city, state, country
Stand out/stand-outNostatic

Group 2 — School setup

StepPathRequiredDatabase
Capacity/capacityYesmaxStudents, maxTeachers, maxClasses
Branding/brandingNoprimaryColor, borderRadius, shadow
Import/importNobulk insert (CSV / Excel)
Finish setup/finish-setupNostatic review

The schedule step is defined in OnboardingStep with its own component directory but is not in active navigation. Lives at order: 5.5 in config.ts for future activation.

Group 3 — Business and legal

StepPathRequiredDatabase
Join/joinYesUser
Visibility/visibilityNoSchool.isPublished
Price/priceYestuitionFee, registrationFee, applicationFee, currency
Discount/discountNoDiscount model
Legal/legalYesLegalConsent
Subdomain/subdomainNoSchool.domain

After all steps the user lands on /congratulations for a smart redirect (school subdomain dashboard or main domain depending on session state).

Active step order is ONBOARDING_CONFIG in src/components/form/footer.tsx. Authoritative metadata (labels, validation, group, order) is ONBOARDING_STEPS in src/components/onboarding/config.ts.

Auth handshake

Four-stage flow:

  1. Start — Unauthenticated user visits /get-started or /onboarding. start-onboarding action persists intent in a cookie and redirects to /login.
  2. Authenticate — Standard login. Callback URL routes back to /onboarding.
  3. Provision — initializeSchoolSetup() calls ensureUserSchool() (src/lib/school-access.ts) — atomic db.$transaction creates the School row, SchoolMember (binds user as ADMIN), seeds default YearLevels. Idempotent.
  4. Refresh + redirect — _sessionRefreshRequired: true triggers client refetch of /api/auth/session; _redirect sends to next step.

Ownership enforced by requireSchoolOwnership(id) on every step's load. Cross-tenant access raises TenantError.

Multi-tenant integration

  • Subdomain routing — /onboarding and children live on a main domain (ed.databayt.org or balqalam.com), not on a subdomain. Subdomain assignment happens at the Subdomain step, and the suffix shown there follows the root the visitor actually arrived on (rootDomainFromLocation()), so a school signing up via balqalam.com is quoted a balqalam.com URL.
  • Locale in step URLs — every onboarding URL must carry its [lang] segment. A bare /onboarding/... path is treated as locale-less by proxy.ts, which then re-derives the locale from the NEXT_LOCALE cookie or Accept-Language header rather than the page the user was on — flipping Arabic users to English mid-wizard. FormFooter navigates correctly on its own; steps overriding onNext should save only, or prefix params.lang themselves.
  • Database scoping — every read uses requireSchoolOwnership(id) first, then queries scoped by schoolId.
  • Session schoolId — JWT carries schoolId after step 1; subsequent steps trust it after re-verification.

Validation

Each step has a Zod schema co-located with its component. Schemas use the i18n factory pattern — same schema produces Arabic or English error messages depending on the dictionary slice.

import { createTitleSchema } from "@/components/onboarding/title/validation"
 
const schema = createTitleSchema(dictionary)
const result = schema.safeParse(input)

Root validation.ts exports validateStep(step, data, dictionary) and getRequiredFieldsForStep(step) for shared step-aware validation.

Auto-provisioning

When completeOnboarding() runs at the legal step, the school is marked active and a 5-step provisioning chain creates academic infrastructure. Each step is wrapped in try/catch so one failure doesn't block the rest.

Source: src/components/onboarding/legal/actions.ts → src/components/catalog/setup.ts.

StepCreates
setupDefaultsForSchool()YearLevels (8 / 6 / 14 by school level), 6 Departments, 9 ScoreRanges
setupCatalogForSchool()Subject / chapter / lesson bridges from the global catalog
setupTermsForSchool()Default terms for the academic year
setupRolesForSchool()Permission templates for the 8 roles
setupNotificationsForSchool()Default notification preferences for ADMIN role

setupCatalogForSchool resolves curriculum via inferCurriculum():

PriorityStrategyExample
1International school type → USany country + international
2Country exact matchUS → US, SD → SD
3Unmapped country → USJP, KE, …

API routes

PathMethodPurpose
/api/onboarding/create-schoolPOSTIdempotent atomic school creation; handles P2002 unique violation race
/api/onboarding/validate-accessPOSTPre-flight access check; auto-provisions; returns redirect on mismatch
/api/onboarding/extractPOSTAI document extraction (10 MB max; JPEG / PNG / WebP / PDF)

Server actions

In src/components/onboarding/actions.ts:

ActionPurpose
initializeSchoolSetup()Creates or retrieves school for current user; returns _redirect
getListing(id)Fetch school with ownership verification
updateListing(id, data)Update fields for a step
getSchoolSetupStatus(schoolId)Completion percentage (6 checks)
reserveSubdomainForSchool(id, sub)Reserve subdomain (delegates to src/lib/subdomain-actions.ts)
getCurrentUserSchool()Returns current user's schoolId
getUserSchools()Lists schools owned by or associated with current user
proceedToTitle(schoolId)Validates ownership and redirects to about-school step

Key files

PathPurpose
src/app/[lang]/onboarding/[id]/[step]/page.tsxEach step page (16 routes)
src/components/onboarding/actions.tsServer actions
src/components/onboarding/config.tsStep config (authoritative)
src/components/onboarding/types.tsOnboardingStep, OnboardingSchoolData
src/components/onboarding/validation.tsRoot Zod schemas
src/components/onboarding/use-listing.tsxListingProvider context
src/components/onboarding/use-onboarding.tsNavigation + validation hook
src/components/onboarding/auth-helpers.tsgetAuthContext, requireSchoolOwnership
src/components/form/footer.tsxFormFooter + ONBOARDING_CONFIG
src/lib/school-access.tsensureUserSchool() atomic transaction
src/components/catalog/setup.tsProvisioning chain

Troubleshooting

SymptomCauseFix
Build hangs at "Environments: .env"TypeScript errors in onboarding componentsRun pnpm tsc --noEmit
Session missing schoolIdSession not refreshed after atomic transactionFetch /api/auth/session after _sessionRefreshRequired: true
Cross-tenant access deniedUser accessing a school they don't ownrequireSchoolOwnership(id) throws TenantError
Subdomain already takenAnother school reserved itAction returns errorCode: "DOMAIN_TAKEN"
Form data not persistingMissing revalidatePath() after mutationAll update actions call revalidatePath("/onboarding")
Dictionary property not foundMissing translation keyUse optional chaining: dictionary?.onboarding?.title?.label

See also

  • Multi-tenancy — auth handshake, tenant context
  • Pattern — mirror pattern, file conventions
  • Authentication — login flow
OfflineOnboarding Videos

On This Page

StepsGroup 1 — Basic informationGroup 2 — School setupGroup 3 — Business and legalAuth handshakeMulti-tenant integrationValidationAuto-provisioningAPI routesServer actionsKey filesTroubleshootingSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.