diff --git a/CLAUDE.md b/CLAUDE.md index 236fec4..8bffdfe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ > **Audience:** AI assistants and developers working on this codebase. > **Purpose:** Authoritative reference for architecture, conventions, gotchas, and decisions. -> **Last reviewed:** June 2026 (Phase 19 complete + mobile API gap-fill Phases A–E + customer UI refinements + Phase 22 comment visibility + Phase 23 support chat/tickets + inspector performance Excel export + inspection list filters + customer issue logging + AI chatbot) +> **Last reviewed:** June 2026 (Phase 19 complete + mobile API gap-fill Phases A–E + customer UI refinements + Phase 22 comment visibility + Phase 23 support chat/tickets + inspector performance Excel export + inspection list filters + customer issue logging + AI chatbot + dashboard grouped sections + issues/inspections PDF export + date/ID filters + Reports expansion Phases R1–R4) --- @@ -41,7 +41,7 @@ - **Issue** tracking with SLA enforcement, follower subscriptions, and verification workflow - **Customer portal** with scoped facility visibility and invitation-based onboarding - **Notification** system (in-app + email) driven by an admin-controlled matrix -- **Reports** — on-demand PDF/CSV scorecards and scheduled email digests +- **Reports** — on-demand PDF/CSV/Excel scorecards, scheduled email digests, Issues Aging, SLA Compliance, Follow-up Closure Rate, and per-facility Customer PDF Summary - **Audit trail** — immutable log of every create/update/delete action - **Support chat** — Groq AI chatbot for customers with preset FAQ chips; escalation to admin via ticketing system; customers can view and reply to their own tickets; admins manage tickets at `/support/admin/tickets` - **Mobile API** — JWT-authenticated REST layer for the iPad native app @@ -110,10 +110,20 @@ lt_janitorial_quality_control/ │ │ ├── issue_photos/ # photo_path and mobile_photo_paths files │ │ └── issue_result_photos/ # result_photos files (web-added resolution photos) │ ├── templates/ -│ │ └── issues/ -│ │ ├── view.html # Shows photo_path + mobile_photo_paths under "Photo Evidence" -│ │ └── issues_view.html # Same photo evidence logic -│ ├── templates/ +│ │ ├── issues/ +│ │ │ ├── view.html # Shows photo_path + mobile_photo_paths under "Photo Evidence" +│ │ │ └── issues_view.html # Same photo evidence logic +│ │ ├── reports/ +│ │ │ ├── _subnav.html # Shared sub-nav include for all report pages +│ │ │ ├── index.html # Overview & Trends (score trend, facility scores, charts) +│ │ │ ├── facility.html # Per-facility detail report +│ │ │ ├── scorecard.html # Per-facility scorecard (trend, area scores, SLA, open issues) + PDF Summary button +│ │ │ ├── inspector_performance.html # Inspector KPI table + drill-down chart +│ │ │ ├── issues_aging.html # Open issues grouped by age bucket (R1) +│ │ │ ├── sla_compliance.html # SLA compliance by severity and facility (R2) +│ │ │ └── followup_closure.html # Follow-up re-inspection closure rate (R3) +│ │ ├── scheduled_reports/ +│ │ │ └── index.html # Includes _subnav.html for Reports sub-nav │ │ └── support/ │ │ ├── chat.html # Customer AI chatbot + FAQ chips + submit-ticket modal │ │ ├── my_tickets.html # Customer: list of own tickets @@ -346,8 +356,8 @@ api_device_tokens: id, user_id, device_id, apns_token, device_name, app_version | `issues` | `/issues` | list, view, create, update, verify, comment, follow/unfollow, verification queue, bulk-verify, delete, quick-assign | | `notifications` | `/notifications` | list, mark-read, preferences, send-digest (cron), check-sla (cron), cleanup-tokens (cron) | | `audit` | `/audit` | list (admin only), view, purge | -| `reports` | `/reports` | index, facility report, scorecard, CSV/PDF export | -| `scheduled_reports` | `/scheduled-reports` | CRUD + manual trigger | +| `reports` | `/reports` | index, facility report, scorecard, CSV/PDF/Excel export, issues-aging, sla-compliance, followup-closure, facility summary PDF | +| `scheduled_reports` | `/scheduled-reports` | CRUD + manual trigger (accessible via Reports sub-nav) | | `support` | `/support` | `GET /chat`, `POST /chat/message` (AJAX→Groq), `POST /tickets`, `GET /my-tickets`, `GET/POST /my-tickets/`, `GET /admin/tickets`, `GET/POST /admin/tickets/` | | `api` | `/api/v1` | parent blueprint | | `api_auth` | `/api/v1` | `/auth/login`, `/auth/refresh`, `/auth/logout`, `/auth/me`, `/devices/register` | @@ -386,6 +396,16 @@ All WTForms classes. `AreaForm.area_type` includes `floor`. `UserForm` excludes ### `pdf_export.py` ReportLab-based. 12-column grid must be preserved — never collapse in PDF views. +**Public functions:** +- `generate_inspection_pdf(inspection, form_fields, form_data, issues, static_folder)` — per-inspection PDF +- `generate_issues_list_pdf(issues, filter_summary)` — landscape issues list PDF (from issues list export) +- `generate_inspections_list_pdf(inspections, filter_summary)` — landscape inspections list PDF +- `generate_facility_summary_pdf(facility, days, start, now, total_inspections, avg_score, area_scores, open_issues, resolved_count)` — customer-facing one-page facility summary PDF (Phase R4) + +**`_build_styles()` registered style names:** `ReportTitle`, `ReportSub`, `SectionHead`, `FieldLabel`, `FieldValue`, `MetaLabel`, `MetaValue`, `IssueDesc`, `FooterStyle`, `SummaryTitle`, `ReportSubtitle`, `Meta`, `ScoreValue`, `ScoreLabel`, `SectionHeader`, `TableHeader`, `TableCell` + +The last eight styles (`SummaryTitle` through `TableCell`) were added for the facility summary PDF and are available for any future customer-facing PDF functions. + --- ## 9. Mobile API (Phase 7 / Phase A–E) @@ -740,13 +760,96 @@ Rendered in `dashboard.html` for `current_user.role == 'customer'`. Uses a Boots `inspections.index()` accepts five additional query params: `date_from`, `date_to` (ISO date strings), `score_min`, `score_max` (0–100 floats), `inspector_id` (int). Inspector filter is suppressed when the viewer has the `inspector` role (they always see their own only). The `inspectors` variable is passed to the template only for non-inspector roles so the dropdown is conditionally rendered. +### Inspection List — PDF Export & Filter State Preservation + +`GET /inspections/export-list-pdf` — same filter logic as `index()`, passes current filters as `filter_summary` string to `generate_inspections_list_pdf()`. Logs `ACTION_EXPORT`. + +**Filter state on back-navigation:** `list.html` adds class `insp-list-link` to every View/Continue button. On click, JS saves `window.location.href` to `sessionStorage['insp_list_back_url']`. `view.html` reads this key on load and updates the back button `href` so returning from a detail view restores the previous filter state. + +### Issues List — ID Filter, Date Filter & PDF Export + +`issues.index()` accepts three additional query params: `issue_id` (exact match on `Issue.id`), `date_from`, `date_to` (ISO date strings applied to `Issue.reported_at`). The `date_to` end is expanded to `23:59:59` so the whole day is included. + +`GET /issues/export-list-pdf` — same scope + filter logic as `index()`, applies SLA post-filter for `?sla=` param (SLA is computed in Python, not stored). Calls `generate_issues_list_pdf()`. + +Both `index()` and `export_list_pdf()` carry `date_from` / `date_to` in pagination links and the unfollow-next URL. + ### Inspector Performance — Excel Export `GET /reports/export/inspector-performance` generates a `.xlsx` with two sheets: - **Performance Summary** — all inspector KPIs, color-coded cells, totals row - **Inspection Detail** — individual inspection records for the period -Accepts `date_from`, `date_to`, `inspector_id` query params matching the HTML report page. Logs an `EXPORT` audit action. Uses `openpyxl`. +Accepts `start`, `end`, `inspector_id` query params. Logs an `EXPORT` audit action. Uses `openpyxl`. + +### Dashboard — Grouped Sections + +The dashboard cards are organised into two labelled sections separated by a divider rule: + +**Inspections section** (all roles see first 2; staff see all 4): +1. Today's Inspections — links to `inspections.index` filtered by today +2. Submitted Today — links to `inspections.index` with `status=completed` + today's date +3. Stale In-Progress — inspections with `status=in_progress` AND `inspection_date < now - 24h`; links to `inspections.index?status=in_progress` +4. Pending Follow-ups — inspections with `follow_up_required=True`; links to `inspections.index?status=follow_up` + +**Issues section** (customers see first 3; staff see all 5): +1. Open Issues — `status=open` with severity breakdown badges +2. Issues Opened Today — links to `issues.index` with `date_from=today&date_to=today` (uses the date filter added to `issues.index`) +3. Resolved Today — `status=resolved` + today's date range +4. Pending Verification — `status=pending_verification` +5. Unassigned Open — `status=open` issues with no `assigned_to` + +Each card has a subtitle line explaining what it counts. Section dividers use `d-flex align-items-center gap-2` with a `
` rule. + +**Inspector Activity table** follows the cards for admin/director/PM: all active inspectors, today's completed inspection count per inspector, progress bar scaled to `max_count`. Green row highlight if count > 0. + +### Reports — Navigation & New Pages + +The **Reports** main-nav item is positioned second (right after Dashboard). **Scheduled Reports** was removed from the main nav and is now a sub-nav tab inside Reports (visible to admin/director/PM). + +All report pages include `{% include 'reports/_subnav.html' %}` as the first element inside `{% block content %}`. The sub-nav tab visibility is role-gated: + +| Tab | Roles | +|---|---| +| Overview & Trends | All | +| Issues Aging | All | +| SLA Compliance | All | +| Follow-up Closure | admin, director, project_manager | +| Inspector Performance | admin, director | +| Scheduled Reports | admin, director, project_manager | + +### Reports — Phase R1: Issues Aging (`/reports/issues-aging`) + +Loads all non-resolved issues scoped by role, groups into five age buckets (`<24h`, `1–3 days`, `3–7 days`, `1–4 weeks`, `>4 weeks`). SLA status computed per-issue via `sla_status()`. Filters: severity, facility (both applied in Python after the main query to avoid double-outerjoin conflicts with customer scope). + +Excel export: `GET /reports/export/issues-aging` — one sheet, color-coded severity and SLA columns. + +Helper: `_load_open_issues_scoped(customer_facility_ids, severity_filter, facility_id_filter)` — extracted so both the HTML route and the Excel export share identical query logic. + +### Reports — Phase R2: SLA Compliance (`/reports/sla-compliance`) + +Loads resolved issues in the date range, computes `within_sla()` per issue (compares elapsed hours to `SLA_HOURS[severity]`). Produces: +- `overall_pct` — org/scope-wide compliance % +- `by_severity` — dict with `total`, `met`, `pct`, `sla_hours` per severity tier +- `by_facility` — list sorted by compliance % descending + +Helper: `_sla_within(issue)` — used by both the HTML route and the Excel export. + +Excel export: `GET /reports/export/sla-compliance` — 2 sheets: **By Severity** (with totals row) and **By Facility**. + +### Reports — Phase R3: Follow-up Closure (`/reports/followup-closure`) + +`@supervisor_required`. Loads inspections with `follow_up_required=True` in date range. Determines which have been re-inspected via a separate `SELECT parent_inspection_id FROM inspections WHERE parent_inspection_id IN (...)` query — avoids iterating the dynamic `follow_ups` relationship. + +Annotates each inspection with `insp._has_followup = insp.id in followed_up_ids` (transient Python attribute, not an ORM column). + +Excel export: `GET /reports/export/followup-closure` — one sheet with color-coded "Followed Up?" column (green/red). + +### Reports — Phase R4: Customer Facility PDF Summary + +`GET /reports/facility//summary-pdf` — available to all roles with facility access (customer scope enforced). Accepts `days` param (30/60/90/180/365; default 90). Calls `generate_facility_summary_pdf()` from `pdf_export.py`. Downloads directly as a PDF attachment. + +A **PDF Summary** button was added to `reports/scorecard.html` alongside the existing Full Report and period selector buttons. ### Support Chat — Customer UX diff --git a/README.md b/README.md index 5223b1d..a909cb0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,14 @@ A production-grade web application for managing janitorial service contracts, fa - **Issue Tracking** — Full lifecycle management (open → in-progress → pending verification → resolved) with SLA enforcement, follower subscriptions, resolution photo uploads, and inline quick-assign from the issues list - **Customer Portal** — Scoped facility visibility for client accounts with invitation-based onboarding; admin enters name and email only, customer chooses their own username and password via a 72-hour emailed link; expired invitation warnings surface on the admin dashboard - **Notification System** — In-app + email notifications driven by an admin-controlled routing matrix; per-user preferences including digest mode and a one-click "Pause All Emails" toggle -- **Reports** — On-demand PDF/CSV scorecards and scheduled recurring email reports (daily/weekly/monthly) +- **Reports & Analytics** — Comprehensive reporting suite accessible from the second nav position: + - *Overview & Trends* — facility scorecards, score trend charts, and PDF/CSV exports + - *Issues Aging* — open issues grouped into age buckets (`<24h` → `>4 weeks`) with SLA status per issue; Excel export + - *SLA Compliance* — resolved-issue SLA compliance % broken down by severity tier and facility, with progress bars; Excel export (2 sheets) + - *Follow-up Closure Rate* — tracks which flagged inspections received a re-inspection; per-facility closure rates; Excel export + - *Customer Facility PDF Summary* — downloadable PDF report for a facility covering KPIs, area scores, and open issues; accessible from the scorecard page + - *Inspector Performance* — per-inspector KPI summary with Excel export (admin/director only) + - *Scheduled Reports* — recurring email delivery of summary, facility, or issues reports (daily/weekly/monthly) - **Audit Trail** — Immutable log of every create, update, and delete action with actor and IP capture - **Support Chat** — Groq AI-powered chatbot for customers with preset FAQ quick-replies; automatic escalation to a ticketing system when the AI cannot resolve the issue; admins manage and reply to tickets at `/support/admin/tickets` with in-app and email notifications on every state change - **Inspector Performance Export** — Excel (`.xlsx`) export of the inspector performance summary with a color-coded KPI sheet and a detailed inspection log sheet