From 9aa7971a28fdafd15f929b2e08b5257f0d001c1f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 23 Jul 2026 19:01:26 +0000 Subject: [PATCH] feat: Sprint 3 frontend dashboards with Alpine.js + Jinja2 - Login page with JWT auth and role-based redirect - CS Dashboard: KPI cards, priority breakdown, recent tickets - FM Dashboard: tech workload, aging analysis, emergency alerts - CEO Dashboard: executive KPIs, charts, risk indicators - All Issues: filterable/sortable ticket table with pagination - Create Issue: form with category tree, property/unit selector, photo uploads - Issue Detail: full timeline, photo gallery, SLA status, action modals - Role-based nav bar: CS/FM/Executive links adapt to user role - Base template: shared Alpine.js app state, toast notifications, loading overlay --- AGENTS.md | 13 + app/main.py | 3 +- app/routers/pages.py | 50 ++++ app/templates/base.html | 356 +++++++++++++++++++++++ app/templates/dashboard/ceo.html | 283 ++++++++++++++++++ app/templates/dashboard/cs.html | 244 ++++++++++++++++ app/templates/dashboard/fm.html | 248 ++++++++++++++++ app/templates/login.html | 74 +++++ app/templates/tickets/detail.html | 456 ++++++++++++++++++++++++++++++ app/templates/tickets/list.html | 211 ++++++++++++++ app/templates/tickets/new.html | 279 ++++++++++++++++++ 11 files changed, 2216 insertions(+), 1 deletion(-) create mode 100644 app/routers/pages.py create mode 100644 app/templates/base.html create mode 100644 app/templates/dashboard/ceo.html create mode 100644 app/templates/dashboard/cs.html create mode 100644 app/templates/dashboard/fm.html create mode 100644 app/templates/login.html create mode 100644 app/templates/tickets/detail.html create mode 100644 app/templates/tickets/list.html create mode 100644 app/templates/tickets/new.html diff --git a/AGENTS.md b/AGENTS.md index 8fb0dcc..61805f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,19 @@ Users, units, and categories are auto-seeded on first startup via lifespan hook: | POST | `/api/whatsapp/mock` | No | Mock WhatsApp | | GET | `/api/whatsapp/mock-log` | No | Recent mock submissions | +### Pages (Sprint 3) — Jinja2 templates at `app/templates/` +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/login` | No | Login page | +| GET | `/dashboard/cs` | Client | CS dashboard | +| GET | `/dashboard/fm` | Client | FM dashboard | +| GET | `/dashboard/ceo` | Client | CEO dashboard | +| GET | `/tickets` | Client | All Issues filterable table | +| GET | `/tickets/new` | Client | Create Issue form | +| GET | `/tickets/{id}` | Client | Issue detail with timeline | + +Frontend: Alpine.js (CDN) + Tailwind CSS (CDN). Auth state in localStorage. Role-based nav routing in `base.html`. + ### Tickets (Sprint 2) | Method | Path | Auth | Description | |--------|------|------|-------------| diff --git a/app/main.py b/app/main.py index 1d82e9c..9947821 100644 --- a/app/main.py +++ b/app/main.py @@ -12,7 +12,7 @@ from fastapi.staticfiles import StaticFiles from app.core.config import settings from app.core.database import Base, async_session_factory, engine -from app.routers import auth, health, tickets, whatsapp +from app.routers import auth, health, pages, tickets, whatsapp from app.services.seed import seed_categories, seed_units, seed_users logger = logging.getLogger(__name__) @@ -61,3 +61,4 @@ app.include_router(health.router) app.include_router(auth.router) app.include_router(whatsapp.router) app.include_router(tickets.router) +app.include_router(pages.router) diff --git a/app/routers/pages.py b/app/routers/pages.py new file mode 100644 index 0000000..1123105 --- /dev/null +++ b/app/routers/pages.py @@ -0,0 +1,50 @@ +"""Frontend page routes — serve Jinja2 HTML templates for the SPA-like dashboard.""" + +from __future__ import annotations + +from __future__ import annotations + +from pathlib import Path + +from fastapi import APIRouter, Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +router = APIRouter(tags=["pages"]) + +templates = Jinja2Templates(directory=str(Path(__file__).resolve().parent.parent / "templates")) + + +@router.get("/login", response_class=HTMLResponse) +async def login_page(request: Request): + return templates.TemplateResponse(request, "login.html") + + +@router.get("/dashboard/cs", response_class=HTMLResponse) +async def cs_dashboard(request: Request): + return templates.TemplateResponse(request, "dashboard/cs.html") + + +@router.get("/dashboard/fm", response_class=HTMLResponse) +async def fm_dashboard(request: Request): + return templates.TemplateResponse(request, "dashboard/fm.html") + + +@router.get("/dashboard/ceo", response_class=HTMLResponse) +async def ceo_dashboard(request: Request): + return templates.TemplateResponse(request, "dashboard/ceo.html") + + +@router.get("/tickets", response_class=HTMLResponse) +async def ticket_list(request: Request): + return templates.TemplateResponse(request, "tickets/list.html") + + +@router.get("/tickets/new", response_class=HTMLResponse) +async def create_ticket_page(request: Request): + return templates.TemplateResponse(request, "tickets/new.html") + + +@router.get("/tickets/{ticket_id}", response_class=HTMLResponse) +async def ticket_detail_page(request: Request, ticket_id: int): + return templates.TemplateResponse(request, "tickets/detail.html", context={"ticket_id": ticket_id}) diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..c25cfab --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,356 @@ + + + + + + Denya OneCare + + + + + + + + + + +
+ + +
+ + +
+ {% block content %}{% endblock %} +
+ + +
+
+ + + + + +
+
+ + +
+ +
+ + + + diff --git a/app/templates/dashboard/ceo.html b/app/templates/dashboard/ceo.html new file mode 100644 index 0000000..4315a64 --- /dev/null +++ b/app/templates/dashboard/ceo.html @@ -0,0 +1,283 @@ +{% extends "base.html" %} +{% block content %} +
+
+

Executive Dashboard

+

Read-only strategic overview

+
+ + +
+
+

Open Tickets

+

+
+
+

Critical (Urgent)

+

+
+
+

SLA Compliance

+

+
+
+

Avg Resolution

+

+
+
+ +
+
+

Avg Response

+

+
+
+

Reopened This Week

+

+
+
+

Total Tickets

+

+
+
+

Completion Rate

+

+
+
+ + +
+ +
+

Complaints by Property

+
+
+ +
+ East +
+
+ +
+ West +
+
+
+ + +
+

Complaints by Category

+
+ +
No data yet
+
+
+
+ +
+ +
+

Tickets by Priority

+
+
+ 🔴 Urgent + +
+
+ 🟠 High + +
+
+ 🟡 Medium + +
+
+ 🟢 Low + +
+
+
+ + +
+

Monthly Trends

+
+ +
No data yet
+
+
+
+ + +
+

Risk Indicators

+
+
+
+
+ Open Emergencies +
+

+
+
+
+
+ Reopened This Week +
+

+
+
+
+
+ Overdue (Past SLA) +
+

+
+
+
+
+ Pending Verification +
+

+
+
+
+
+ + +{% endblock %} diff --git a/app/templates/dashboard/cs.html b/app/templates/dashboard/cs.html new file mode 100644 index 0000000..b5064b6 --- /dev/null +++ b/app/templates/dashboard/cs.html @@ -0,0 +1,244 @@ +{% extends "base.html" %} +{% block content %} +
+
+

Customer Service Dashboard

+

Welcome back,

+
+ + +
+
+
+
+

New Today

+

+
+
+ + + +
+
+
+ +
+
+
+

Open Tickets

+

+
+
+ + + +
+
+
+ +
+
+
+

Avg Response Time

+

+
+
+ + + +
+
+
+ +
+
+
+

SLA Compliance

+

+
+
+ + + +
+
+
+
+ + +
+ +
+

Open by Priority

+
+
+ 🔴 Urgent + +
+
+ 🟠 High + +
+
+ 🟡 Medium + +
+
+ 🟢 Low + +
+
+
+ + +
+

Oldest Unassigned

+
No unassigned tickets
+ +
+ + +
+

Escalated Tickets

+
+
+ +
No escalated tickets
+
+
+
+ + +
+
+

Recent Tickets

+ View All → +
+
+ + + + + + + + + + + + + + + + +
TicketStatusPriorityDescriptionCreated
No tickets found
+
+
+
+ + +{% endblock %} diff --git a/app/templates/dashboard/fm.html b/app/templates/dashboard/fm.html new file mode 100644 index 0000000..94ab73c --- /dev/null +++ b/app/templates/dashboard/fm.html @@ -0,0 +1,248 @@ +{% extends "base.html" %} +{% block content %} +
+
+

Facilities Management Dashboard

+

Welcome back,

+
+ + +
+ + + +
+

Emergency Require Immediate Attention!

+

View in the table below

+
+
+ + +
+
+
+
+

Active Jobs

+

+
+
+ + + +
+
+
+ +
+
+
+

Due Today

+

+
+
+ + + +
+
+
+ +
+
+
+

Waiting Parts

+

+
+
+ + + +
+
+
+ +
+
+
+

Jobs East / West

+

/

+
+
+ + + +
+
+
+
+ + +
+ +
+

Technician Workload

+
+ +
No active assignments
+
+
+ + +
+

Aging Analysis

+
+
+ < 24h + +
+
+ 1-2 days + +
+
+ 3-5 days + +
+
+ Overdue > 5d + +
+
+
+
+ + +
+
+

Active Tickets

+ View All → +
+
+ + + + + + + + + + + + + + + + + +
TicketStatusPriorityTechnicianDescriptionAge
No active tickets
+
+
+
+ + +{% endblock %} diff --git a/app/templates/login.html b/app/templates/login.html new file mode 100644 index 0000000..9bed0d5 --- /dev/null +++ b/app/templates/login.html @@ -0,0 +1,74 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+
+ + + +
+

Denya OneCare

+

Sign in to your dashboard

+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+

+ Demo Accounts:
+ bella@denya.com / denya123 — CS Rep
+ nicholas@denya.com / denya123 — FM Dispatcher
+ scott@denya.com / denya123 — CEO
+ jerome@denya.com / denya123 — Admin +

+
+
+
+
+ + +{% endblock %} diff --git a/app/templates/tickets/detail.html b/app/templates/tickets/detail.html new file mode 100644 index 0000000..977b9ae --- /dev/null +++ b/app/templates/tickets/detail.html @@ -0,0 +1,456 @@ +{% extends "base.html" %} +{% block content %} +
+ +
+ + + + +
+ + + + +
+
+

Update Status

+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+

Assign Technician

+
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+

Add Note

+
+
+ + +
+
+ + +
+
+
+
+ + +
+ +
+
+ + +{% endblock %} diff --git a/app/templates/tickets/list.html b/app/templates/tickets/list.html new file mode 100644 index 0000000..cab1602 --- /dev/null +++ b/app/templates/tickets/list.html @@ -0,0 +1,211 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+

All Issues

+

+
+ + + New Issue + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ Page of +
+
+ + + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + +
+ Ticket + StatusPriorityDescriptionAssigned To + Created + SLA
+ + No tickets match your filters +
+
+
+
+ + +{% endblock %} diff --git a/app/templates/tickets/new.html b/app/templates/tickets/new.html new file mode 100644 index 0000000..f9aea87 --- /dev/null +++ b/app/templates/tickets/new.html @@ -0,0 +1,279 @@ +{% extends "base.html" %} +{% block content %} +
+
+

Create New Issue

+

Report a maintenance or customer service issue

+
+ +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+ + +
+ + +
+ +
+ + + + +
+
+ + +
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + + +

Click to upload photos

+ +
+
+ +
+
+ + +
+ + +
+ + Cancel +
+
+
+
+ + +{% endblock %}