# Web Application Documentation ## Overview The FUJ Management web application is a Flask-based dashboard that provides real-time visibility into club finances. It renders server-side HTML with embedded CSS and JavaScript — no build tools, no npm, no framework. The UI follows a distinctive **terminal-inspired aesthetic** with monospace fonts, green-on-black colors, and dashed borders. ## Routes ### `GET /` — Index (Redirect) Redirects to `/fees` via an HTML meta refresh tag. This exists so the root URL always leads somewhere useful. ### `GET /fees` — Attendance & Fees Dashboard **Template**: `templates/fees.html` Displays a table of all adult members with their calculated monthly fees based on attendance. Each cell shows the fee amount (in CZK), the number of practices attended, or a dash for months with zero attendance. **Data pipeline**: ``` attendance.py::get_members_with_fees() → Filter to tier "A" (adults) match_payments.py::fetch_exceptions() → Check for fee overrides → Format cells with override indicators → Render fees.html with totals row ``` **Visual features**: - Fee overrides shown in **orange** with the original amount in parentheses - Empty months shown in muted gray - Monthly totals row at the bottom - Performance timing in the footer (click to expand breakdown) **Template variables**: | Variable | Type | Content | |----------|------|---------| | `months` | `list[str]` | Month labels like "Jan 2026" | | `results` | `list[dict]` | `{name, months: [{cell, overridden}]}` | | `totals` | `list[str]` | Monthly total strings like "3750 CZK" | | `attendance_url` | `str` | Link to the attendance Google Sheet | | `payments_url` | `str` | Link to the payments Google Sheet | ### `GET /reconcile` — Payment Reconciliation **Template**: `templates/reconcile.html` (802 lines — the most complex template) The centerpiece of the application. Shows a matrix of members × months with payment status, plus summary sections for credits, debts, and unmatched transactions. **Data pipeline**: ``` attendance.py::get_members_with_fees() → All members + fees match_payments.py::fetch_sheet_data() → All payment transactions match_payments.py::fetch_exceptions() → Fee overrides match_payments.py::reconcile() → Match payments ↔ fees → Render reconcile.html ``` **Cell statuses**: | Status | CSS Class | Display | Meaning | |--------|-----------|---------|---------| | `empty` | `cell-empty` | `-` | No fee expected, no payment | | `ok` | `cell-ok` | `OK` | Paid in full (green) | | `partial` | `cell-unpaid` | `300/750` | Partially paid (red) | | `unpaid` | `cell-unpaid` | `UNPAID 750` | Nothing paid (red) | | `surplus` | — | `PAID 200` | Payment received but no fee expected | **Interactive features**: 1. **Member detail modal** — Click the `[i]` icon next to any member name to see: - Status summary table (month, attendance count, expected, paid, status) - Fee exceptions (if any, shown in amber) - Full payment history with dates, amounts, senders, and messages 2. **Keyboard navigation** — When a member modal is open: - `↑` / `↓` arrows navigate between members (respecting search filter) - `Escape` closes the modal 3. **Name search filter** — Type in the search box to filter members. Uses diacritic-insensitive matching (e.g., typing "novak" matches "Novák"). 4. **QR Payment** — Hover over an unpaid/partial cell to reveal a "Pay" button. Clicking it opens a QR code modal with: - A Czech SPD-format QR code (scannable by Czech banking apps) - Pre-filled account number, amount, and payment message - The QR image is generated server-side via `GET /qr` **Client-side data**: The template receives a full JSON dump of member data (`member_data`) embedded in a ` ``` There is no shared base template (no Jinja2 template inheritance). CSS is duplicated across templates with small variations. --- *Web application documentation generated from comprehensive code analysis on 2026-03-03.*