# Evening Class Platform — Functionality Inventory > Based on a full read of the codebase. Features are described as they actually exist in code. --- ## 1. Courses ### Create Admins create courses via `CourseForm` (`src/components/admin/CourseForm.tsx`). Required: title + URL slug (auto-slugified). Optional: short/long description, difficulty (all/beginner/intermediate/advanced), age guidance, materials, delivery method (in_class/online/hybrid), duration, QQI accreditation code. Surfaced at `src/routes/$collegeSlug.admin.courses.new.tsx`; list at `courses.index.tsx`. ### Edit Reuses `CourseForm` pre-populated (`courses.$id.tsx:37–47`). Also shows scheduled runs and the Form Assignment Panel. Hard-delete refuses if runs exist — admins are prompted to deactivate instead. ### Archive / Deactivate `is_active` toggle on every course ("Active (visible to learners)"). Inactive courses are hidden from public listings without data loss. No separate archive state — inactive is the effective archive. ### Categories Managed at `categories.tsx`. Name + slug + active toggle. CSV import auto-creates missing categories (`course-import.functions.ts:432–443`). Used to group public courses and as audience filters for communications. ### Images Two paths on the course form: - **Custom upload** — up to 8 MB, stored in Supabase Storage `branding/course-images//…` - **Stock library** — modal picker (`StockImagePicker.tsx`) of pre-seeded landscape photos Displayed on the "Starting soon" cards and public course listing. ### Pricing Set at the **run** level: `price_cents` + `currency` (`RunForm.tsx:27–31`). Plus itemised **Run Fees** (`RunFeesPanel.tsx`) — typed entries (Standard, Concession, Materials, etc.) linked to reusable **Fee Types** carrying accounting codes (GL, Centre, Analysis, VAT) managed at `fee-types.tsx`. ### Capacity Each run has a `capacity` integer. Enforced at checkout via a race-safe RPC `reserve_seat_for_booking`. When confirmed bookings reach capacity, run status flips to `full`. Admin dashboard shows live fill-rate progress bars per upcoming run. Over-capacity applicants route to the waitlist automatically. --- ## 2. Learners ### Register Public booking form at `book.$runId.tsx` — three steps: personal details (name/email/phone), emergency contact, accessibility needs, preferred contact method (email/phone/SMS/WhatsApp), marketing consent, terms acceptance. GDPR Article 7 consent evidence is logged to `consent_log` on submit. If a custom form is assigned, it replaces the standard fields and learner details are extracted by key/label pattern-matching. ### Book Bookings created with status `pending_payment` and a unique `booking_reference`; seat held while pending; flipped to `confirmed` on payment success. Admins can also create individual bookings (`bookings.new.tsx`) or **group bookings** for many learners on one run (`bookings.group.tsx`) with optional sponsor + PO reference per learner. ### Cancel Admin cancellation from booking detail page ("Mark as cancelled" → `status = 'cancelled'`). Learner self-cancellation is not implemented — cancellations are admin-driven. Confirmation page at `booking-cancelled.tsx`. ### Waitlist When a full run is booked, the booking is created with `status = 'waitlisted'` and a confirmation email is sent immediately. No payment taken. Admin **Waitlist Panel** (`WaitlistPanel.tsx`) on the run edit page lists waitlisted bookings in order. Staff can **offer a seat** (timed payment link via `offerWaitlistSeat`) or **void** an offer. Application Flow config supports two modes: `auto_offer` (system) or `manual` (staff action). ### History (My Bookings) Account-free **magic-link** flow. Learner submits email → `api.learner-access.ts` mints a 30-minute token via `request_learner_access` DB function → branded email link → `my-bookings.$token.tsx` shows all bookings with run dates, location, check-in QR, and per-session attendance status. Email sent silently even when no bookings exist (prevents enumeration). --- ## 3. Payments ### Stripe Adapter pattern (`registry.server.ts`, `router.server.ts`). The **Lovable-managed Stripe** adapter mounts Embedded Checkout inside the booking flow. Stripe Checkout Sessions are created server-side with the booking ID as `client_reference_id`. `confirmCheckoutReturn` confirms the session, guards against IDOR by matching session→booking, inserts a `payments` record, and flips the booking to `confirmed`. A **Stripe Connect** path exists for multi-college marketplaces — payments split between platform and each college's verified Stripe account via `application_fee_amount` + `transfer_data`. Courses sync to Stripe Products via `syncCoursesToStripe` / `ensureStripeProductForCourse`; tax codes backfilled via `backfillStripeProductTaxCodes`. Webhooks at `api/public/stripe/webhook.ts` and `api/public/payments/webhook.ts`. Configurable from `admin.payment-providers.tsx`: | Provider | Status | |---|---| | Stripe (Lovable-managed) | Fully implemented | | Stripe Connect | Fully implemented | | Cash / Bank Transfer / Manual | Fully implemented (admin marks paid) | | Opayo / Global Payments / GoCardless | Scaffolded; adapters not yet implemented | ### Refunds Issued from booking detail page. `refundBookingPayment` calls `stripe.refunds.create` with `reason: 'requested_by_customer'`, updates `payments.status = 'refunded'`, sets `bookings.status = 'refunded'`. Stripe-card payments only; cash/manual require manual handling. Admin-only. ### Discounts Not implemented — no discount or coupon code feature in the codebase. ### Instalments / Payment Plans Per-run **payment plans** configured in `PaymentPlanPanel.tsx` (sequence of instalments with label, percentage or fixed amount, due-offset days). When enabled, checkout charges only the first instalment; subsequent ones are issued as separate pay-link emails. The booking-level **Instalments Panel** (`InstalmentsPanel.tsx`) lets admins create ad-hoc instalments, record offline payments (cash, card in-person, bank transfer, cheque, Stripe external, other), waive individual instalments, or send a learner a Stripe-hosted pay link via `pay.$token.tsx`. The `mark_instalment_paid` DB function atomically settles and advances booking status. ### Sponsor Invoicing **Sponsors** (organisations that pay for learners) managed at `admin.sponsors.*`. Bookings can be set `payment_responsibility = 'sponsor'` and linked with a PO reference. **Sponsor invoices** (`admin.sponsor-invoices.*`) group bookings for a sponsor, generate a reference, track status (draft/sent/paid/overdue), generate PDF via `invoicePdf.ts`, and email via `api.sponsor-invoices.email.ts`. College banking details (IBAN, BIC, prefix, default due days) embedded in PDF. --- ## 4. Administration ### Dashboard At `admin.index.tsx`: - KPI cards: confirmed/pending bookings, cancellations, total payments (7/30/90 days or all-time) - Area chart — bookings & revenue over time (Recharts) - Pie chart — booking status distribution - Bar chart — revenue by payment method - Upcoming class fill-rate table with colour-coded capacity bars - Recent bookings table (last 10) - Catalogue summary — counts of active/inactive courses & runs by category ### Bulk Import CSV/XLSX import at `admin.courses.import.tsx` (`course-import.functions.ts`): 1. Parses an ExcelJS workbook against a fixed 12-column schema (Category, MIT Code, QQI Code, Title, Bio, Weeks, Day, Time, Cost, Start Date, End Date, Student Number) 2. **Preview** step fuzzy-matches against existing courses by MIT Code → QQI Code → title; flags rows as new / exists / exists-edited with per-row errors and warnings 3. **Commit** step lets admins pick per-row action (`create / skip / add_run / replace`); new categories auto-created; upserts in a single transaction. Up to 500 rows per import. ### User Management **College-level** (`admin.college-users.tsx`, `college-users.functions.ts`): - Invite staff/admin by email (Supabase `inviteUserByEmail`) - View MFA enrolment status + last sign-in - Change role between `admin` and `staff` - Resend invitations - Remove access Only college admins can manage users; super-admin bypasses. **Super-admin** (`super-admin.functions.ts`, `super-admin.colleges.$id.tsx`): `listCollegeAdmins`, `inviteCollegeAdmin`, `removeCollegeAdmin` across all colleges. Platform-wide user view at `super-admin.platform-users.tsx`. --- ## Additional Platform Capabilities ### Reports Three surfaces under `admin.reports.*`: 1. **Overview** — filterable bookings/payments/refunds/attempts summary with custom date ranges, XLSX export, and an **AI Insights** button (`generateReportInsights`) that produces a GPT-written narrative. 2. **Report Builder** — custom reports across `bookings`, `payments`, `refunds`, `payment_attempts`, `runs`. Column selection, filters, group-by. Save, name, describe, schedule. 3. **Catalogue overview** — tree of categories → courses → runs with search and active badges. 4. **Scheduled reports** — weekly (Mon 09:00) or monthly (1st 09:00) email delivery via `api/public/hooks/run-scheduled-reports.ts`. ### Communications - **Compose & send** — broadcast email or SMS to audiences segmented by run/course/category/all-consenting learners. Audience preview before send. `{{first_name}}` / `{{course_title}}` / `{{run_dates}}` / `{{booking_reference}}` placeholders with inline lint warnings. - **Activity log** — campaign history - **Templates** — editable email + SMS templates for booking confirmation, start reminder, end reminder; individually enable/disable - **Settings** — from_email, from_name, Twilio sender, daily SMS cost cap, channel toggles, reply-to, test send - **Automated reminders** — daily cron (`send-reminders.ts`) sends start/end reminders; idempotent per booking per day ### Security Dashboard at `admin.security.tsx`: - **Health checks** — live OWASP-aligned checks via `security_status` RPC (RLS, throttle triggers, purge lock-down, open data requests, audit counts, consent counts, retention config, auto-purge) - **Remediation log** — severity (info/low/medium/high/critical) + status (open/monitoring/accepted/fixed) with notes - **Monthly reviews** — structured review checklists - **Platform assurances** — Cloudflare edge, port scan expectations, OWASP coverage - **Pentest webhook** — rotatable secret token feeds scanner results to remediation log - **MFA** — TOTP enrolment + challenge at `admin.security.mfa.tsx` ### Audit Log At `admin.audit.tsx` via `audit_feed` RPC. Filters: category (data/auth/admin), free-text search, limit up to 200 rows. Shows actor, action, entity type/ID, timestamp, expandable raw JSON details. Mirrored as a Compliance tab. ### Settings `admin.settings.tsx`: college name, tagline, description, address, phone, contact email, website, logo upload, terms URL, cancellation policy URL, IBAN/BIC/bank name, payment instructions, invoice prefix, default invoice due days, AI knowledge text, Test Mode toggle, test-booking purge. ### Multi-tenancy / Super-Admin Fully multi-tenant — each college has a `slug` prefixing all routes (`/$collegeSlug/…`). Super-admin portal (`super-admin.*`, gated to `super_admin` role): - List/create/configure colleges - Per-college logo + Stripe Connect onboarding - Invite/remove college users across any college - Platform-wide user view - Edit master marketing site content ### QR Check-In Each run has a `checkin_code`. Printable QR poster at `admin.runs.$id.checkin-poster.tsx` encodes `/$collegeSlug/checkin/$code`. Public check-in page lists today's attendees via `list_run_attendees_for_checkin` and records attendance on tap. ### Magic-Link Learner Access Account-free access via 30-minute tokens (`request_learner_access` DB function + `api.learner-access.ts`). Branded email; silent send for unknown addresses (anti-enumeration). ### AI Help Assistant Floating widget (`HelpAssistant.tsx`) → `admin-help-chat` edge function → Lovable AI Gateway. System prompt encodes data model + setup order. JWT verified + role-checked (`admin`/`staff`) before forwarding. ### AI Documents (Knowledge Base) `AIDocumentsPanel.tsx` + `ai-documents.server.ts` — admins upload documents (PDF/text) used to enrich the AI assistant's college-specific knowledge. ### Test Mode / Test Booking Purge `test_mode` boolean per college shows site-wide `TestModeBanner`. `createTestBooking` generates plus-addressed test bookings for end-to-end payment tests. **Purge test data** button calls `purge_test_bookings` RPC to delete all test bookings, attendance, payments, and form responses in one operation. ### Home Page Carousel & Content Manager `admin.home.tsx` — hero title/subtitle/background, primary + secondary CTAs, three feature tiles (icon/heading/body), "Featured classes" toggle. **Home Carousel** (`HomeCarouselManager.tsx`) — sequenced slides (image/heading/description/link) shown as rotating banner. ### Forms Builder & Assignment `FormBuilder.tsx` / `FormEditor.tsx` at `admin.forms.*`. Field types: text, email, phone, textarea, select, checkbox, radio, date, number, section header, file. Each has key, label, required, options, help text. Assigned to a run or a course (applies to all its runs) via `FormAssignmentPanel.tsx`. Responses stored in `form_responses`, shown on booking detail. ### Application Flow Configuration `admin.application-flow.tsx` — configures lifecycle state machine per college: toggle pending-payment/waitlist/enquiry states, rename labels, waitlist mode (auto-offer/manual), waitlist offer expiry hours, pending-payment timeout hours. Visual flow chart (`ApplicationFlowChart.tsx`) + editable staff guidance notes. ### Enquiry / Interest Registration When enabled, public **Register Interest** form at `enquire.$courseSlug.tsx` captures name/email/phone/message. Creates a booking with `status = 'enquiry'` via `registerEnquiry`. Convertible to confirmed by staff. ### Tutors `admin.tutors.*` + `TutorForm.tsx` — full name, email, phone, bio, profile image. Linked to courses; selectable when scheduling runs. ### Locations `admin.locations.*` + `LocationForm.tsx` — name, address, town, Eircode, county, country, phone, website, map URL, active toggle. Selected on runs; displayed on public booking and My Bookings. ### Sponsors `admin.sponsors.*` + `SponsorForm.tsx` — name, type, contact, billing address, VAT number. Linked to bookings + invoices. `SponsorPicker` used in group booking creation. ### GDPR / Data Requests 1. **Learner self-service** at `my-data.tsx` — access (export), withdraw marketing consent, rectification, erasure. Stored in `data_requests` with user agent. 2. **Admin compliance** at `admin.compliance.tsx` — Legal pages (Privacy/Cookies/Terms/Accessibility/DPIA with versioned `effective_date`), Data requests, Audit log, Retention (booking/audit/consent retention months, auto-purge, data controller details). ### Session Management `SessionsPanel.tsx` on run edit page — per-session date, start/end time, location. Attendance status tracked per learner per session; visible on My Bookings and check-in. ### Consent Banner `ConsentBanner.tsx` — GDPR cookie consent on first visit, recorded before tracking applied. ### Lifecycle Tick `api/public/hooks/lifecycle-tick.ts` + `lifecycle.server.ts` — time-based transitions (expiring pending payments, advancing waitlist offers). ### Course Finder Keyboard-accessible dialog (`CourseFinderDialog.tsx`) + `course-finder.functions.ts` — live keyword search across courses. --- *Generated from codebase analysis — all references verified against live source tree.*