For the school
The staff application
- DirectorEverything, including accounts, backups and system health
- AdminRuns the school day to day
- Team leaderSupervises their teachers and groups
- TeacherAttendance, progress and evaluations for their own groups
Featured projects / Case study
An Arabic, right-to-left platform that runs a school day to day, with a separate portal where parents follow their own child.
A school runs on many small records kept by many people. On paper and in spreadsheets, those records disagree, and nobody can tell who changed what.
Who is in which group, under which teacher, and who left.
Every session's attendance, and how each student is progressing.
Monthly subscriptions, partial payments, and who is behind.
Staff who should only see their part, and reports leadership can trust.
Staff and families use the same data through two separate applications. Staff run the school. A parent sees one child and nothing else.
For the school
For parents
Every module has its own API endpoints, stored procedures and screens. The interface is Arabic only, and student and staff names, and the school's own branding, are hidden in every screenshot because the data is real.
Attendance · Progress tracking · Curriculum and exams · Evaluations and medals · Weekly assignments
Students and groups · Transfers and archiving · Supervision · Staff accounts
Monthly subscriptions · Partial payments · Financial reports
School-wide reports with PDF and Excel export · Planning and notes · Audit log · Backups and health
Teacher, on a phone
Built for one hand during the session. Everyone starts present, so the teacher only taps the exceptions, and the counters update with each tap.
Director and admins
The dashboard points at gaps, not only totals: groups with no attendance since Saturday, students with no progress recorded this week, or no curriculum follow-up.
Admins and teachers
One page per student: study, guardian and health details, with attendance, progress, evaluations and the family account below. An archived student stays in the archive with no time limit and can be restored.
Subscriptions account
Payments are recorded against months, not as loose amounts. A partial payment is split evenly over the months it covers, so every student's balance can be explained.
Parent, on a phone
A parent signs in with the student's number and a 4-digit PIN, nothing else. After that they see only their own child: attendance, progress, evaluations and medals, subscriptions and teachers' notes.
The weekly assignment appears as one small alert. The parent marks it read, then done, and only that student's teachers and the administration see those steps.
Every request follows the same path. There is no ORM: the API calls stored procedures directly.
Browser
Two kinds of token, with different keys and audiences, so a family token can never act as staff
API
Each request passes through these layers, in order
Endpoints name a policy or require a signed-in account, for example
A login that may only run stored procedures
Database
Rules sit in a procedure or a single service, never in the screens, so two screens cannot disagree.
Controllers handle HTTP, services handle rules, repositories handle data. Each is easy to read and to test.
Transactions, checks and audit rows run in the database, in one step, without an ORM in between.
A teacher tries to correct attendance a little over three days after recording it. Teachers may correct their own records for 72 hours. This is the path the request takes, and where it stops.
A staff token, not a family one. The account is re-checked on every request, with a short cache.
401
Staff: one of the four staff roles.
403
Each status is absent, late or excused, and an excused absence needs a reason.
400
Admins reach every class, a teacher only their own classes, a team leader only the classes of the teachers they supervise.
403
In one transaction, in this order: the class is active, the session is this teacher's own, and it was recorded less than 72 hours ago.
400
409
403
The session and each student's status are written in the same transaction.
200
Request
PUT /api/v1/classes/{classId}/attendance
Authorization: Bearer <staff token>
Content-Type: application/json
{
"date": "yyyy-mm-dd",
"overrides": [
{ "studentId": "…", "status": "late" }
]
}
Response
HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=utf-8
{
"title": "غير مسموح",
"status": 403,
"detail": "انتهت مهلة تعديل هذا السجل. يمكن للإدارة تعديله.",
"instance": "/api/v1/classes/…/attendance"
}
In English: "Not allowed. The time allowed to edit this record has passed. The administration can edit it."
From the browser suite e2e-windows: the same correction is accepted at 71 hours and refused at 73.
An excerpt of the schema: the tables that decide who may touch a class, and the two columns that close the edit window.
Scroll sideways to see the whole diagram.
The four choices that shaped the system most.
01
02
03
04
What protects the data and keeps the system running.
Accounts are re-checked on each request, with a short cache
Different keys, audiences and clients
Endpoints name a policy or require a signed-in account
Checked for each group and each student
The API can only execute procedures
Who did what and when. Sign-ins are kept 90 days, other actions 12 months
Per-address request limits, lockouts that grow longer after wrong passwords or PINs, and the same answer whether an account exists or not
Daily and monthly backups, each checked after it is written
Read-only answers kept in memory per person for a few seconds, thrown away the moment any change succeeds
Monthly reports and subscription months can run again, or catch up after downtime, without duplicates
800+ automated checks in 58 suites. They run against a real local database and clean up the test data they create. On every push, GitHub Actions builds the API, runs the C# unit tests, and lints, type-checks and builds the site.
Node, against the running API
Roles, scoping, validation and time windows are tested at their exact boundaries. For example, a teacher can correct attendance at 71 hours and is refused at 73.
Playwright, end to end
Real pages, signed in as each kind of account, on desktop and phone widths, including exports, dialogs and page speed.
xUnit, no database
Logic that does not need a database, such as sign-in throttling, the server cache and scheduled cleanup jobs.
From .github/workflows/ci.yml. It runs on every push and every pull request, and needs no database and no secrets.
Ubuntu, .NET 10
Ubuntu, Node 24
Not in CI: the 58 check suites, with their 800+ checks. They need signed-in accounts and real data, so I run them on my machine, one suite at a time, against the running API and site and a local database.
The same command the API job runs.
$ dotnet test tests/unit/••••••.Api.Tests.csproj -c Release --nologo
A total of 1 test files matched the specified pattern.
Passed! - Failed: 0, Passed: 27, Skipped: 0, Total: 27, Duration: 1 s - ••••••.Api.Tests.dll (net10.0)
78deecb, with .NET 10. The browser and API suites run separately, against a local database. The private project name is masked as ••••••.The question "who can correct this record, and until when?" shaped the database more than any screen did.
Two teachers can share a role without sharing students, so access has to be checked against the data itself.
When real people depend on a system, verified backups, health checks and clear errors matter as much as features.
I'm happy to walk you through the live system and the code.