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

Attendance

PreviousNext

Multi-method attendance — manual, QR, barcode, bulk upload, geofence — with parent engagement, early warning, interventions, and reports.

The attendance module tracks student attendance across multiple capture methods, with role-based access, parent notifications, US DoE risk levels, intervention tracking, and CSV / Excel / PDF export. Teachers see only their assigned classes; students and guardians see only their own records.

Ship tracker: databayt/hogwarts#1 — consolidated attendance + compliance master tracker. See also Aldar Epic 01.

Status

FeatureStatusNotes
Quick Attendance (teacher landing) + staff/teacher clockProductionAbsent-oriented one-tap marking; check-in/out feeds timesheets
Manual / Period / QR / Barcode / Bulk uploadProductionMultiple capture methods (see Capture methods table)
Parent notifications + excuse submissionProductionIn-app + SMS + email; staff reviews
Early warningProductionUS DoE risk levels, intervention tracking
Reports exportProductionCSV, Excel (5 sheets), PDF (branded)
Soft delete + restoreProductiondeletedAt
Auto-excuseProductionApproved AbsenceIntention → EXCUSED
Locale-aware namesProductiongetText() translates on demand
Geofence / Master gate / Policy engine / Compliance reportsWiredFunctional, surface still maturing
SettingsProductionPersists to the school-wide AttendancePolicy row
AI predictions / Gamification / Hall pass / Kiosk / Compliance lettersProductionRouted; surfaced via the overview Quick-access grid
Absence intentions / Geofence UIWiredDeep-link only until internals are localized
ADEK eSIS daily submissionProductionGeneric connector at /compliance, DRY_RUN default
2-hour guardian SLA cronProduction/api/cron/absence-followup, every 30 min, UTC

On a phone

Below the md breakpoint the overview and every sub-page follow the school dashboard's phone pattern — the same vocabulary as the phone dashboard, library and live pages: figures as one grey two-across panel, grey borderless cards with pill buttons, section doors as app tiles, rows whose details wrap under the title instead of crushing it. Tablets and desktops are unchanged. Manual marking, the kiosk and the data tables look the same at every width.

Routes

The attendance root is role-aware: TEACHER lands on Quick Attendance (mobile-first, absent-oriented — everyone defaults present, taps cycle absent → late → present, one save submits the roster and notifies guardians of absentees), ADMIN/STAFF land on the overview, students/guardians on their self-view. All under src/app/[lang]/s/[subdomain]/(school-dashboard)/attendance/. Tabs: /, /manual, /qr-code, /records, /excuses, /early-warning, /interventions, /analytics, /reports, /settings. The staff overview's Quick-access grid links /recent, /bulk-upload, /barcode, /hall-pass, /interventions/tiers, /gamification, /ai (+ /kiosk, /letters for admins). Deep-link only: /geo, /intentions, /bulk. /analysis permanently redirects to /analytics; /parent/attendance redirects to /attendance/records. See the Capture methods table below for per-route purpose; see RBAC for per-role access.

RBAC

ActionDEVELOPERADMINTEACHER (own sections)STAFFSTUDENTGUARDIAN (children)
Mark attendanceYYYY——
Read own recordsYY——YY
Read section recordsYYY———
Read school-wideYY—Y——
DeleteYYYY——
RestoreYY————
Bulk uploadYYY———
ExportYYYY——
AnalyticsYYYY——
Compliance / SettingsYY————
Excuse reviewYYYY——
Excuse submitYY———Y

A teacher's sections are their homeroom, any section they hold a timetable period with, and any section they are assigned a subject in — getTeacherSectionIds() (actions/helpers.ts). sectionScopeWhere() applies them to a read: a section filter is intersected with them, never allowed to widen the scope. A grade filter goes through the section (section.gradeId).

Manual attendance

Flat inline toolbar (PlatformToolbar): section selector, date picker, bulk status buttons, Save. getCurrentPeriod() auto-selects the section matching the current timetable period; names translate on demand via getText(). Keyboard: P / A / L for present / absent / late, Cmd/Ctrl+S save. Absent-student guardians are auto-notified on save; an approved AbsenceIntention converts ABSENT → EXCUSED.

The roster unit is the section — Section.students defines who is in the class. Each section has exactly one main classroom (homeroom) via Section.classroomId; non-homeroom rooms (labs, halls, fields) are reached per-period through the timetable slot (Timetable.classroomId), which is also what getCurrentPeriod() resolves for period-aware attendance. See Classrooms › Integration.

Capture methods

MethodRouteNotes
Manual/manualPer-student dropdown + bulk buttons
Period/manual (per-period)getCurrentPeriod(), period analytics
QR/qr-codeGenerated for a section — only its students can scan; default 30-min validity, dedup
Barcode/barcodeLooks up StudentIdentifier; the scan records the student's own section, so the desk picks nothing first
Kiosk/kioskCheck-in records the student's own section
Bulk upload/bulk-uploadCSV: studentId, date, status, checkInTime, checkOutTime, notes
Geofence/geoLocation tracker (students), zones (admins)
Master gateserver actionsrecordMasterAttendance(), getPrefillFromMaster()

Hall passes (/hall-pass) are issued on the student's own section, so issuing one asks for the student and destination only.

Parent engagement

triggerAbsenceNotification() fires on absent: in-app (high priority), SMS via Twilio (if configured), email. Uses school preferredLanguage.

Excuse reasons: Medical, Family Emergency, Religious, School Activity, Transportation, Weather, Other. Approved → EXCUSED; rejected → unexcused. A submitted excuse — and an absence intention with "notify teachers" on — reaches the section's homeroom teacher and the teachers assigned its subjects this term (sectionTeacherUserIds). A teacher's review queue is their own sections' excuses.

Student / guardian view: percentage + trend, calendar history, status breakdown, excuse submission for unexcused absences.

Early warning

US DoE risk levels:

RiskAttendance rateAbsence rate
Satisfactory≥95%<5%
At-risk90 – 94.9%5 – 9.9%
Moderately chronic80 – 89.9%10 – 19.9%
Severely chronic<80%≥20%

Intervention tracking at /attendance/interventions — 14 intervention types (parent contact, home visit, counsellor / social worker / administrator referral, attendance contract, truancy referral, community resource, academic support, mentorship, incentive, other) with statuses Scheduled → In Progress → Completed / Cancelled / Escalated. Policy engine in actions/policy.ts evaluates triggers and manages exemptions.

Analytics & reports

7-tab analytics at /analytics: Summary, Calendar, Trends, Patterns, Methods, Classes (sections ranked by rate), At Risk. Filters: date range and section.

Reports at /reports: filter by section, student, status, date range, period. Export: CSV, Excel (5 sheets — summary, detailed, by status, by section, by date), PDF (branded). The export follows the section filter.

Bulk upload at /bulk-upload is for one section: every student in the file must be in it.

Compliance: getComplianceDashboard(), getComplianceReport(), getScheduledReports().

Server actions

Actions are split across per-concern modules under src/components/.../attendance/actions/.

ModuleFileSurface
Corecore.tsMark, list, check-out, bulk delete + restore
Analyticsanalytics.tsStats, trends, method usage, calendar, section comparison, at-risk, recent activity
Periodsperiods.tsPer-period mark, current-period detection, period analytics
QRqr.tsSession generate, scan, active sessions
Identifiersidentifiers.tsStudent barcode / card identifier CRUD + lookup
Excusesexcuses.tsSubmit, review, list pending / unexcused
Interventionsinterventions.tsCreate, update, escalate, list, stats, assignees
Bulkbulk.tsCSV / Excel upload, report builder, CSV export, recent uploads
Dashboarddashboard.tsToday's overview, by-risk, early warning, unmarked sections, parent summary
Policypolicy.tsEvaluate policies, triggers, exemptions, dismissals
Mastermaster.tsSchool-gate entry / exit, prefill from master
Compliancecompliance.tsCompliance dashboard + scheduled reports
Recordsrecords.tsStudent own + guardian children

Sample signatures:

markAttendance(input: {
  sectionId: string
  date: string
  records: Array<{ studentId: string; status: 'present' | 'absent' | 'late' }>
})
 
markPeriodAttendance(input: {
  sectionId: string     // a teacher's own section; every student must be in it
  date: string
  periodId: string
  timetableId?: string  // must be the section's slot
  records: Array<{ studentId: string; status: 'PRESENT' | 'ABSENT' | 'LATE'; notes?: string }>
})
 
getAttendanceList({ sectionId, date, lang? })  // lang triggers translation
deleteAttendance(id: string)                  // soft delete
restoreAttendance(id: string)

Schema

model Attendance {
  id           String           @id @default(cuid())
  schoolId     String
  studentId    String
  sectionId    String?          // the section the mark is kept on
  date         DateTime         @db.Date
  status       AttendanceStatus
  method       AttendanceMethod @default(MANUAL)
  checkInTime  DateTime?
  checkOutTime DateTime?
  notes        String?
  markedBy     String?
  markedAt     DateTime         @default(now())
  deletedAt    DateTime?
 
  periodId    String?
  timetableId String?
  periodName  String?
 
  @@unique([schoolId, studentId, sectionId, date, periodId])
  @@index([schoolId, date, periodId])
}

Soft-delete semantics. deletedAt is a tombstone, not a hard delete. A removed record is excluded from every analytics, dashboard, export and regulator-compliance read, and from the student / guardian mobile views — yet the row is retained for audit and an admin can restore it (restoreAttendance). Because the @@unique tuple still reserves a deleted row's key, re-marking a student for the same day/period revives the original record (clears deletedAt) instead of creating a duplicate — this holds across manual, period, QR, barcode, geofence and kiosk capture.

Supporting models: AttendanceExcuse, AttendanceIntervention, QRCodeSession, StudentIdentifier, AbsenceIntention, PolicyTrigger, PolicyExemption, MasterAttendance, HallPass, KioskLog, AttendanceReward, StudentBadge, AttendanceStreak.

Seed

pnpm db:seed:single attendance

Regulator compliance (ADEK eSIS — UAE)

UAE schools (School.country === "AE") can opt into a daily-CSV upload to the ADEK eSIS portal at esis.adek.gov.ae plus the ADEK-mandated 2-hour parent contact for unreported absences. The framework is generic — a new regulator (SEC KSA, MoE Qatar, MoE Sudan) only adds a value to the ComplianceProvider enum and a sibling directory under src/lib/compliance/providers/<authority>/.

PiecePathPurpose
Settings UI/{lang}/complianceToggle, mode, SLA, daily-time, submissions list
Cross-tenant admin/{lang}/(saas-dashboard)/compliance (DEVELOPER)Shared credential groups for Aldar-style piggyback
Daily cron/api/cron/esis-submit (0 10 * * *)Enqueues + fans out per school
Followup cron/api/cron/absence-followup (*/30 * * * *)2h SLA evidence trail
Webhook/api/webhooks/adekAsync ACCEPTED/REJECTED receipts
Worker (out of repo)tools/adek-rpa-worker/ (Fly.io)Playwright fallback when no API

Connector modes — set per school × provider:

ModeWhat it does
DRY_RUNGenerate CSV, persist for audit, registrar uploads to eSIS manually
PIGGYBACKSubmit via shared-group eSIS credentials (Aldar pattern, 1 cred row × N schools, circuit breaker on cascading failures)
OFFICIAL_APIDocumented vendor API (awaiting ADEK developer access)
RPAExternal Playwright worker claims QUEUED rows and uploads via the eSIS web portal
DISABLEDSkip in cron

2-hour parent-contact SLA

When SchoolComplianceConfig.enabled = true, triggerAbsenceNotification dispatches on in_app + email + whatsapp channels (instead of in_app only) so the email + WhatsApp crons drain the notification row. Every 30 minutes, the absence-followup cron looks for ABSENT rows that are:

  • markedAt < now() - parentContactSlaMinutes (default 120, computed in UTC)
  • have no AttendanceExcuse
  • have no approved AbsenceIntention covering today
  • have no AttendanceIntervention of type PARENT_PHONE_CALL / PARENT_EMAIL in the past 24h

It dispatches a absence_unreported_followup notification to each guardian, then writes an AttendanceIntervention row (PARENT_EMAIL, parentNotified=true, contactMethod=in_app+email+whatsapp). That row is the regulator-facing audit evidence — getParentContactSlaReport() queries it to produce "contacted within SLA" counts for ADEK audits.

Schema (compliance side)

Five new entities in prisma/models/compliance.prisma — none of them ADEK-specific:

  • ComplianceProvider enum (currently ADEK_ESIS, CUSTOM)
  • ConnectorMode enum
  • ComplianceSubmissionStatus enum + CircuitBreakerState enum
  • SchoolComplianceConfig — one row per school × provider (opt-in)
  • SharedComplianceCredentialGroup — group-shared creds (Aldar pattern)
  • SchoolComplianceCredential — per-school creds (OFFICIAL_API mode)
  • ComplianceSubmission — one row per attempt, indexed by (schoolId, provider, submissionDate, attemptNumber). Retries chain via supersededBy. The CSV is persisted inline in csvArtifactContent and downloaded via /api/compliance/artifact/[submissionId] (signed S3 URL planned).

AuditLog.userId was loosened to nullable in the same epic so cron / worker / webhook system events can be recorded — calls pass userId: null.

A new NotificationType.absence_unreported_followup covers the 2h SLA path.

Offline marking

Quick attendance (/attendance for teachers) works without a connection. The mark goes to the device outbox (kind attendance, one pending item per section and day) and is replayed through POST /api/offline/sync → actions/quick-core.ts, the same code the online action runs. A newer mark already on the server wins and the replay is dropped as a duplicate. Guardian absence notifications fire when the replay lands. See Offline.

Try it on a phone: open Attendance → Quick, switch on airplane mode, mark a section and save — the panel confirms the marks are saved on the device and the sync banner shows one pending item. Switch the network back on and the banner drains; the marks are on the server within seconds. Marking the same section again replays as a duplicate and the newer mark wins. Installing the app first (the welcome sheet's Continue button) is not required for offline marking, but it is required on iPhone for push notifications.

See also

  • Multi-tenancy — RBAC + tenant scoping
  • Notifications — absence dispatch
  • Timetable — period auto-detection
  • Compliance — regulator-submission framework (ADEK + future authorities)
ApplicationCompliance

On This Page

StatusOn a phoneRoutesRBACManual attendanceCapture methodsParent engagementEarly warningAnalytics & reportsServer actionsSchemaSeedRegulator compliance (ADEK eSIS — UAE)2-hour parent-contact SLASchema (compliance side)Offline markingSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.