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

Application

PreviousNext

Public applicant flow — login, campaign selection, 5-step wizard, session management, offer + registration fee, status tracking, tour booking, and inquiry capture.

The applicant-facing portal runs at school.databayt.org. Applying requires a login (the wizard is auth-gated, and the actions enforce it server-side); status tracking stays account-less via OTP. After choosing a campaign, the applicant enters a 5-step wizard with auto-save (every 30 s + localStorage, with a server-session fallback when localStorage is lost), and submits on the Fees step. Applying is always free — the only payment in the whole journey is the registration fee, after an offer is accepted.

Steps

1. Visit /admissions
2. Log in (auth-gated) → choose campaign / start application
3. Overview — drafts + new application
4. Wizard — 5 steps (3 phases)
5. Submit (on the Fees step — free, no payment)
6. Success dialog (inline) → application appears under "Your applications"
7. Offer (post-decision) → registration fee to secure the seat

Wizard

Single shared FormFooter with three progress bars (one per phase). 30 s auto-save + localStorage backup.

#StepRequired fieldsRoute
1AttachmentsOptional by default (photo, degree, transcript, ID, resume, other) — triggers AI extraction. When the school sets requireDocuments, this step blocks Next with a visible hint until ≥ 1 document is uploaded/{lang}/application/{id}/attachments
2PersonalStudent name, dateOfBirth, gender, nationality + guardian (Father/Mother) in tabs/{lang}/application/{id}/personal
3Locationaddress, city, state, country (Mapbox if NEXT_PUBLIC_MAPBOX_TOKEN)/{lang}/application/{id}/location
4AcademicapplyingForClass (14 grade levels: KG1, KG2, Grade 1–12)/{lang}/application/{id}/academic
5FeesInformational fee preview by grade (applying is free — no payment UI) — submits the application/{lang}/application/{id}/fees

The Personal step uses single "Full Name" or split fields based on the school's nameFormat, and folds the former Guardian step into Student / Father / Mother tabs. The former standalone Contact step was removed — email is back-filled at submit from the session / logged-in user. Fees is the final step: it previews the tuition for the chosen grade, then its button submits the application and shows the success dialog inline. Each step's Next button is gated by the same rules the real validation enforces (phone length, ≥ 2-character parent name), and any save/validation failure surfaces as an inline alert — never a silent no-op.

Phase grouping

PhaseLabelSteps
1Basic InformationAttachments, Personal
2AddressLocation
3AcademicAcademic, Fees

Submit flow

// Server-side
1. Resolve school from subdomain
2. Validate via applicationFormDataSchema (Zod)
3. Verify session belongs to this school
4. Check campaign OPEN and not expired
5. Check duplicate email + campaign (unless allowMultipleApplications)
6. Generate application number `APP-{year}-{random6}`
7. Generate access token (32 chars, 6-month expiry)
8. Create Application (status: SUBMITTED)
9. Mark session as converted
10. Notify admins + send applicant confirmation email (Resend)

The caller must be signed in (NOT_AUTHENTICATED otherwise), documents are required at this point only as a backstop (DOCUMENTS_REQUIRED — the attachments step already gates it), and duplicates are blocked both in code and by the @@unique([schoolId, campaignId, userId]) DB constraint (APPLICATION_DUPLICATE). There is no payment step — the retired application-fee leg was removed entirely.

Success dialog

Submission opens the success dialog inline on the fees step (the standalone success/payment pages were deleted):

  • Application Tracking Code (application number + access token for the OTP tracker)
  • Next steps timeline (email → review → decision)

After submitting, the application stays visible under "Your applications" on the authenticated /{lang}/application dashboard — status badge, application number, submitted date, and a View offer link once SELECTED.

Offer & registration fee (post-decision)

Once an admission decision is made, /{lang}/application/{id}/offer lets the applicant accept or decline and pay the registration fee to secure the seat — the only payment in the flow. It is reached via an emailed offer-token link or the dashboard's View-offer link. Methods (gated by what the school's currency/region supports): card checkout (webhook-confirmed), cash and bank transfer (admin-confirmed). Amounts render via locale-aware formatCurrency. Offers lapse: a daily job flips SELECTED applications past offerExpiryDate to EXPIRED; an admin can re-offer (EXPIRED → SELECTED).

Application status banner

Server-rendered banner on the school site for logged-in applicants. Dismissible per application via localStorage.

StatusMessageCTA
SUBMITTED"Application #N — Submitted"—
UNDER_REVIEW"Application #N — Under Review"—
SHORTLISTED"Application #N — Shortlisted"—
SELECTED"Congratulations! You've been accepted"View Offer
SELECTED + paid"Accepted — Payment received"Go to Dashboard
EXPIRED"Application #N — Offer expired"—
ADMITTED"Welcome! Enrollment confirmed"Go to Dashboard
WAITLISTED"Application #N — Waitlisted"—
REJECTED"Application #N — Not Selected"—

Banner includes a 4-dot timeline: Submitted → Review → Decision → Enrolled.

Sessions

Storage key hogwarts_apply_session_{campaignId}_{userId} (scoped per user to prevent cross-account draft leakage). Session token is server-generated nanoid(32), 7-day expiry. Auto-save every 30 s when dirty. Browser warns on unsaved navigation.

Three resume paths: localStorage (auto), email link /{lang}/application/continue?token=xxx, authenticated user drafts on the landing page. The first save triggers a "resume application" email.

Status tracking (no account)

/{lang}/application/status
  → enter application number + email
  → 6-digit OTP via email (10-min expiry)
  → max 3 OTPs / hour, 5 verify attempts
  → view timeline

For the full status flow, see Admission → Status flow. Branches: WAITLISTED, REJECTED, WITHDRAWN.

Tour booking

Self-service at /{lang}/tour. Calendar → time slots → booking form (visitor name, email, phone, student name, grade interest, attendees, notes) → confirmation with calendar invite.

Slot typePurpose
TOURCampus tour
INTERVIEWAdmission interview
ORIENTATIONNew student orientation
OPEN_HOUSEOpen house event

View / reschedule / cancel at /{lang}/tour/{bookingId}.

Inquiry form

Lead capture at /{lang}/inquiry for families not ready to apply. Required: parent name, email. Optional: phone, student name, DOB, target grade, source (website / social / referral / open house / ad), message, newsletter opt-in.

Status flow: NEW → CONTACTED → QUALIFIED → CONVERTED → CLOSED.

Server actions

The wizard itself is auth-gated (login required to apply). Status tracking, tour booking, and inquiry capture remain public (no account needed).

ActionParameters
getActiveCampaigns()subdomain
saveApplicationSession()subdomain, formData, step, email, campaignId, sessionToken?
resumeApplicationSession()sessionToken
submitApplication()subdomain, sessionToken, formData
requestStatusOTP()subdomain, appNumber, email
verifyStatusOTP()subdomain, appNumber, otp
getApplicationStatus()accessToken
getAvailableSlots()subdomain, slotType, date
createTourBooking()subdomain, bookingData
submitInquiry()subdomain, inquiryData

Troubleshooting

SymptomLikely cause
Application not savingNetwork error, expired session (7 d), subdomain not resolving
Submit fails validationMissing required field across steps; check error message field names
OTP not receivedSpam folder, wrong email, rate limit (3 / h)
Offer page shows no methodsSchool currency/region unsupported by every gateway — cash/bank should always remain

Key files

PurposePath
Routessrc/app/[lang]/s/[subdomain]/application/
Step configsrc/components/school-marketing/application/config.client.ts
Contextsrc/components/school-marketing/application/application-context.tsx
Submitsrc/components/school-marketing/application/submit-action.ts + admission/actions/application.ts
Validationsrc/components/school-marketing/admission/validation.ts
Offer + feesrc/components/school-marketing/application/offer/{actions.ts, gateways.ts, content.tsx} (registration fee — the only payment)
Stripe webhooksrc/app/api/webhooks/stripe/route.ts
Schemaprisma/models/admission.prisma
Testsschool-marketing/admission/__tests__/{status-banner.test.tsx, status/__tests__/status-display.test.tsx, actions/__tests__/status-tracking.test.ts} + app/api/webhooks/stripe/__tests__/route.test.ts

See also

  • Admission — admin-side flow
  • Multi-tenancy — subdomain resolution
  • Notifications — fee_paid dispatch
AdmissionAttendance

On This Page

StepsWizardPhase groupingSubmit flowSuccess dialogOffer & registration fee (post-decision)Application status bannerSessionsStatus tracking (no account)Tour bookingInquiry formServer actionsTroubleshootingKey filesSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.