← All work
EdTech · language schoolSole architect and engineerDeployed and live

Dialogue LMS

The operating system for a language school

Next.js 15
React 19
PostgreSQL
Drizzle ORM
LiveKit / WebRTC
iyzico payments
S3 / MinIO
Docker · Traefik
The teacher's day — attendance proposed from signed LiveKit webhooks against a 50%-of-duration threshold, confirmed in one click.
The teacher's day — attendance proposed from signed LiveKit webhooks against a 50%-of-duration threshold, confirmed in one click.

Dialogue LMS is a custom learning platform for Dialogue Dil Okulları that runs the school's commercial funnel and its classroom delivery in the same system — including a synchronised digital coursebook that every student in a live lesson sees turn, zoom and get annotated in real time.

Client context

Dialogue Dil Okulları is an English and German language school in Fatih, İstanbul, teaching for 27 years. Its operation had grown into parallel systems that did not talk to each other: enquiries in one place, the timetable in another, attendance on paper, instalment plans in a spreadsheet, and the coursebook as a physical object that only works when everyone is in the same room.

The platform was built by Fenext as a customer deployment of its FenLMS core, and runs today at lms.dialogue.com.tr.

The operational challenge

Two problems, and they turned out to be the same problem.

The commercial side leaked. A prospect who filled in a form and never got a call back was indistinguishable from one who had been called twice. Nobody could see, on one screen, how many enquiries had become paying students, which callbacks had blown past a reasonable response time, or which instalments were 30 days overdue rather than 3.

The delivery side was unevidenced. Once a lesson moved online, the school lost what a physical classroom gives you for free: who turned up, who left early, what was covered, and — most acutely for a language school — the shared page of the coursebook that a teacher points at. Screen-sharing a PDF is not that. It is blurry, the teacher's marks do not survive the lesson, and the student gets no page of their own.

The link between the two is that a school cannot bill confidently for delivery it cannot evidence.

Product strategy

Two decisions shaped everything else. Build one system with role-specific surfaces rather than a classroom tool plus a CRM plus a billing add-on: five roles — student, teacher, manager, admin, super admin — share one PostgreSQL schema, one authentication boundary and one design system, each seeing a different door into the same facts. And treat the coursebook as a first-class data type, not an uploaded file — which is what made real-time page synchronisation, per-page audio and persistent annotation possible at all.

The core solution

A public front door that produces qualified leads. The marketing site carries a free CEFR placement exam, scored server-side, over a 236-question bank across three exams (adult English: 6 sections, 116 questions; junior English and German: 4 sections, 60 each). A visitor who finishes it has told the school their level, their target language and when they can be called — a lead with a level attached, not an email address.

A sales funnel that shows its own leaks. The manager's funnel counts every stage — enquiry received, called, placement taken, awaiting payment, enrolled — and shows the drop-off between each pair. Beside it sits a callback queue ordered oldest-first, with an explicit badge on anything past 48 hours and an owner on each row.

Collections with ageing, not just a total. Payments are recorded through iyzico's hosted checkout (with the callback token re-verified against iyzico before anything is marked PAID) or manually at the school with an instalment plan. Overdue instalments are bucketed 1–9 / 10–29 / 30+ days, so a manager triages instead of scrolling.

A live classroom with the book inside it. Lessons run on LiveKit, in a custom tabbed stage — Book, Screen, Camera — rather than an off-the-shelf video grid, because the book needed to be a peer of the video, not a screen-share hidden behind it.

Attendance that fills itself in. Signature-verified LiveKit webhooks record every join and leave. The system proposes an attendance state per student against a 50%-of-duration threshold, pre-filling "left 22 minutes early" before the teacher has typed anything. The teacher confirms with one click, then signs off a class-book entry that moves DRAFT → SUBMITTED → REVIEWED and locks against later edits.

The part that is genuinely hard: the synchronised book

The coursebook is a 162-page volume, imported by a pipeline that renders each PDF page to three WebP variants under content-addressed storage keys, choosing the render DPI per page. The source turned out to be a hybrid — the first three units native vector text, everything after them flat 144-ppi scans — so a single global DPI would either have blurred half the book or fabricated resolution for the other half. Unit codes (1A, 1B, …) are attached per page, and the audio programme is bound by extracting each page's printed CD1 12 references and matching them to files, so opening page 8 surfaces exactly the four tracks printed on page 8.

In a live lesson, the teacher's page turns, zoom, pan, laser pointer and ink are published over the LiveKit data channel. Two rules make it trustworthy rather than merely fast:

Recording was the last piece: the lesson is captured through a custom room-composite egress template that renders the book alongside the camera strip, so the replay shows the page the class was actually on.

My role

Sole architect and engineer across the stack: data model and 35 migrations, authentication and role boundaries, the LiveKit integration and its webhook pipeline, the book render/annotation/synchronisation system, the operations and finance surfaces, the design system, the CI gates and the deployment.

Technical approach worth a buyer's attention

Next.js 15 (App Router, server actions), React 19, TypeScript, Drizzle ORM over PostgreSQL (100 tables), NextAuth, Tailwind, LiveKit, S3/MinIO, Redis/BullMQ, iyzico — deployed as a single Docker image behind Traefik on one VPS via GitHub Actions, with a concurrency group so two deploys cannot race each other. The schema is multi-tenant-ready; the deployment runs single-tenant.

The part I would point at in a technical conversation is the CI gates, which are project-specific and catch failures that neither type-checking nor tests can see:

Gate Catches
Turkish localisation (5 scanners) Missing diacritics, wrong I/İ casing, broken suffix agreement, text that overflows its control
WCAG contrast Resolves the real Tailwind config and measures the ratio an element actually paints, per theme, on a ratchet baseline that fails on regression and on unclaimed progress
Dead dark: classes and dead routes A dark-mode class naming a colour the palette does not define (Tailwind emits nothing, no error, the dark theme silently never happens), and menu entries pointing at routes that no longer resolve or that cross a role boundary

Outcomes and evidence

The platform is deployed and live at lms.dialogue.com.tr, with the marketing site and placement exam open to anyone. The capability is demonstrable end to end: placement exam to scored lead, lead to callback queue to payment, payment to scheduled lesson, lesson to webhook-derived attendance to a signed class-book entry, and a 162-page coursebook that synchronises across a live room.

I am deliberately not quoting adoption or revenue figures. The school had not switched the delivery modules on at the time of writing, so any percentage improvement would be invented. The honest claim is capability delivered, not usage measured.


Need a similar product or workflow? If you run an institution where the sales pipeline, the timetable and the classroom are three different systems that disagree with each other, that is the problem this platform was built to remove.

Screens

The coursebook as a first-class object — 162 rendered pages, unit codes, annotation tools and the audio tracks printed on that exact page. The publisher's page content is pixelated here.
The coursebook as a first-class object — 162 rendered pages, unit codes, annotation tools and the audio tracks printed on that exact page. The publisher's page content is pixelated here.
Every funnel stage with its own drop-off, beside a callback queue that flags anything past 48 hours. Lead names and phone numbers are redacted.
Every funnel stage with its own drop-off, beside a callback queue that flags anything past 48 hours. Lead names and phone numbers are redacted.
Collections bucketed into 1–9 / 10–29 / 30+ day ageing bands, so a manager triages instead of scrolling.
Collections bucketed into 1–9 / 10–29 / 30+ day ageing bands, so a manager triages instead of scrolling.
The public CEFR placement exam — a 236-question bank across adult English, junior English and German, scored server-side.
The public CEFR placement exam — a 236-question bank across adult English, junior English and German, scored server-side.
The student's surface, with the CEFR level the placement exam produced.
The student's surface, with the CEFR level the placement exam produced.
The public front door, in production.
The public front door, in production.

Captured from a development environment seeded with representative data — no real student data appears. Coursebook pages remain their publisher's property and are shown pixelated.

Architecture

System contextscroll to explore
Enquiry to evidenced lessonscroll to explore
← Back to all work