Saltar al contenido
LDC App

Full Stack · SaaS · Operations Management

Comprehensive Management System for Dance Academy

From physical sheets and Excel to a system that manages 240+ students, 12 dance genres and automatic teacher payroll — built in two weeks, in production since March 2026

Build: 2 weeks — Production: Mar 2026

240+
Active students
12+
Dance genres
12+
Teachers with different contracts
2 wks
From design to production

#The problem

The academy had over 240 active students — between external students and competition team members with daily classes —, 12 dance genres and more than a dozen teachers. Each with a different contract: some charge per number of students in class, others by percentage of revenue, others per fixed class. The process before the system:

Attendance on physical sheets — paper, pen, and hoping nobody forgot to write someone down
Transcription to Excel — every week, someone manually transferred the sheets to a spreadsheet
Teacher payment calculation — cross-referencing monthly attendance with each teacher's contract, one by one
If something didn't add up, you had to find the physical sheets for that month and hope they hadn't been lost

Calculating a single teacher's payment took over an hour. With multiple teachers, month-end closing was a days-long process, full of uncertainty and room for error. The owner couldn't trust the numbers because the numbers depended on sheets of paper.

#The decision

The client — a friend — explained the disorder and I asked for two things: to understand how each teacher's contract worked, and to understand the complete operation flow. From there, the design was a natural consequence.

The challenge wasn't just to digitize — it was to standardize an operation full of exceptions: discounts to retain students, specially negotiated packages, teacher contracts mixing modalities. All of that had to fit in a system simple enough for everyone to use. It went into production in two weeks.

#The complete flow in one view

LDC App manages the complete academy cycle: from when a student buys a pack, to month-end with automatic teacher payroll.

🛒

01 / 08

Student buys a class pack (variable quantity per genre)

#Core Modules: The Ecosystem

The application is structured in modules accessible from the main dashboard. Each module solves a specific operational friction point in the academy:

🔫
Reception
QR Access Control

Invisible input with permanent focus. Captures UUID from USB gun, validates active pack and records attendance in < 2 seconds.

🎓
Students
Student Management

Registration, editing, attendance history, active pack balance and downloadable PDF cards.

🛒
Sales
Packs & Cart

Flexible packs with shopping cart. One payment can include multiple packs of different genres in a single atomic transaction.

🗓️
Schedule
Class Calendar

Integrated FullCalendar. Classes by genre, schedule and teacher with explicit America/Santiago timezone.

💼
Teachers
Contracts & Payroll

Configurable payroll engine per teacher and per class: fixed, commission, mixed or negotiated. Automatic closing with audited breakdown.

📊
Reports
Export

XLSX and PDF reports with attendance and payment breakdown by period. Student cards with QR included.

#The technical decisions that matter

1. Configurable payroll engine by modality

The central problem was that each teacher had different payment schemes — and the same teacher could have different schemes depending on the genre they taught. The system allows separate configuration: Fixed (fixed amount per class, regardless of attendance), Commission (percentage of proportional revenue), Mixed (fixed amount plus percentage) and Negotiated (manually agreed amount). At month-end, the system automatically calculates each teacher's payment, broken down by class, with the calculation detail stored in JSON for complete audit. What used to take over an hour per teacher now takes seconds, with full traceability.

2. Real-time QR access control

The access module maintains an invisible input with permanent focus that captures the UUID sent by a USB reader gun. The complete flow takes less than 2 seconds: QR scan → student lookup → active pack validation → balance deduction → visual confirmation → next student. If the pack is expired or has no classes, the system alerts immediately with an option to renew on the spot. Attendance registration is automatic — no manual intervention.

3. Standardization of operational exceptions

The biggest challenge wasn't technical in the classic sense — it was modeling the reality of a business full of special cases. Discounts to retain students, packs with non-standard class quantities, mixed modalities in a single transaction. The solution was a flexible pack system with a shopping cart: one payment can include multiple packs of different genres, each with its price and modality, processed in a single atomic transaction via PostgreSQL RPC.

Flexible Packs

Multi-genre cart

A student can buy packs of different genres in a single operation. Each pack has independent price, class quantity and expiration.

Atomic

Row Level Security

Policy: auth.uid() = user_id

RLS on all tables. Academy data is not accessible without authentication. Admin and reception with differentiated roles.

Secure

Timezone

America/Santiago explicit

With classes scheduled in Chilean local time and DB in UTC, the entire system uses America/Santiago to handle UTC-4 (summer) and UTC-3 (winter) without errors.

Accurate

The result: the owner can make exceptions — discounts, special packs, custom contracts — without breaking system consistency. Everything is recorded and auditable.

Payroll engine: calculation by modality

The calculation is broken down by class and contract modality. The JSON detail allows auditing exactly how each number was reached — no paper sheets or manual Excel formulas.

lib/liquidacion/calcularPagoProfesor.ts
"text-slate-500">// lib/liquidacion/calcularPagoProfesor.ts
"text-purple-400">export "text-purple-400">async "text-purple-400">function calcularPagoProfesor(
  profesorId: string,
  mes: number,
  anio: number
): Promise {
  "text-purple-400">const { data: clases } = "text-purple-400">await supabase
    ."text-purple-400">from('clases_mes')
    .select(`
      id, nombre, asistencia_count,
      ingreso_proporcional,
      contrato_profesor!inner(modalidad, monto_fijo, porcentaje)
    `)
    .eq('profesor_id', profesorId)
    .eq('mes', mes)
    .eq('anio', anio);

  "text-purple-400">const desglose = clases."text-purple-400">map((clase) => {
    "text-purple-400">const { modalidad, monto_fijo, porcentaje } = clase.contrato_profesor;

    "text-purple-400">const pago =
      modalidad === 'FIJO'      ? monto_fijo :
      modalidad === 'COMISION'  ? clase.ingreso_proporcional * porcentaje :
      modalidad === 'MIXTO'     ? monto_fijo + clase.ingreso_proporcional * porcentaje :
      /* CONVENIO */              monto_fijo;

    "text-purple-400">return { claseId: clase.id, nombre: clase.nombre, pago, detalleJSON: { modalidad, monto_fijo, porcentaje } };
  });

  "text-purple-400">const total = desglose."text-purple-400">reduce((sum, d) => sum + d.pago, 0);
  "text-purple-400">return { profesorId, mes, anio, total, desglose };
}

QR access control: complete flow

The invisible input maintains permanent focus to capture the USB reader gun. The Server Action validates the pack, deducts the balance and records attendance — all in a single atomic operation.

app/acceso/page.tsx
"text-slate-500">// app/acceso/page.tsx — Input invisible con foco permanente
'use client';
"text-purple-400">export "text-purple-400">default "text-purple-400">function AccesoPage() {
  "text-purple-400">const inputRef = "text-purple-400">useRef(null);
  "text-purple-400">const [resultado, setResultado] = "text-purple-400">useState(null);

  "text-slate-500">// Mantener foco permanente para capturar pistola USB
  "text-purple-400">useEffect(() => {
    "text-purple-400">const mantenerFoco = () => inputRef.current?.focus();
    document.addEventListener('click', mantenerFoco);
    mantenerFoco();
    "text-purple-400">return () => document.removeEventListener('click', mantenerFoco);
  }, []);

  "text-purple-400">async "text-purple-400">function handleScan(uuid: string) {
    "text-purple-400">const res = "text-purple-400">await validarAcceso(uuid); "text-slate-500">// Server Action
    setResultado(res);
    "text-slate-500">// Auto-reset para siguiente alumno
    setTimeout(() => { setResultado(null); inputRef.current?.focus(); }, 2000);
  }

  "text-purple-400">return (
    
"sr-only" onKeyDown={/* captura UUID */} /> {resultado && }
); }

#The impact in numbers

Before vs. after: the same team, the same operation volume, completely different results.

Before
After
Attendance on physical sheets
Automatic QR registration in < 2 seconds
Manual transcription to Excel every week
Data captured in real time
+1 hour per teacher to calculate payment
Automatic payroll in seconds
Sheets that got lost
Complete traceability, everything audited in JSON
Month-end closing with uncertainty and days of work
Exportable report with complete breakdown
Owner managing the operation
Owner focused on growing the academy
240+
Active students
12+
Teachers with different contracts
~98%
Reduction in payroll time
2 wks
From idea to production

#Tech Stack

Layer
Technology
Why
Framework
Next.js 16 (App Router)
SSR for fast initial load. Server Actions for mutations without an additional API layer.
Language
TypeScript 5 (strict mode)
Strict typing throughout the application. Errors caught at build time, not in production.
UI
React 19 + Tailwind CSS v4
Fast iteration. Own UI components without opinionated dependencies.
Database
Supabase (PostgreSQL + Auth + Storage)
Auth + DB + Storage + RLS on one platform. RPC for atomic transactions.
Mutations
Server Actions + selective revalidation
No additional API layer. Surgical revalidation by path for immediate UX.
Calendar
FullCalendar 6
Weekly and monthly view of classes by genre and teacher. Drag & drop to reschedule.
QR
qrcode.react + USB HID gun
QR generation on client. USB gun reading via HID input — no additional drivers.
Export
XLSX + PDF (student cards)
Payroll reports in XLSX. Student cards with integrated QR in PDF.

#What's next

🔔

Automatic notifications

Email or SMS when a pack is about to expire, to reduce student loss before they stop attending.

📱

Student portal

Check balance, attendance history and class booking from the phone.

💳

Online payment

Integration with Webpay or MercadoPago for students to pay for their packs without going to reception.

📈

Predictive retention analysis

Identify students at risk of dropping out before they stop attending, using attendance patterns.

#Reflection

This project was built in two weeks because the problem was clear from day one. When you understand the operation before opening the editor, the design becomes obvious. The most valuable thing wasn't the code — it was the initial conversation where I understood that the owner didn't need a generic system.

He needed one that followed exactly how his academy worked, with all its exceptions included. Today the owner trusts the numbers. He has real-time information, and can focus on what really matters: improving the academy so more students come.

— Jaime Arias · Design, architecture and complete implementation · Build: 2 weeks · In production since March 2026 · Continuous evolution

Interested in the technical architecture or a similar solution?

Contact me