# ✅ Hashtag Orange GBP Dashboard — Progress Tracker

> **Project**: Hashtag Orange GBP Dealer Dashboard
> **Last Updated**: May 4, 2026 — 10:10 AM IST
> **Overall Status**: Phases 0–5.1 Complete ✅ | Security Audit Applied ✅ | Phase 6 In Queue 🟡

---

## Quick Status Board

| Phase | Name | Status | Completed On |
| --- | --- | --- | --- |
| Phase 0 | Cleanup & Restructure | ✅ **COMPLETE** | April 27, 2026 |
| Phase 1 | Database Schema & Seed | ✅ **COMPLETE** | April 27, 2026 |
| Phase 2 | Authentication System | ✅ **COMPLETE** | April 27, 2026 |
| Phase 3 | Agency Dashboard | ✅ **COMPLETE** | April 27, 2026 |
| Phase 4 | Client Dashboard | ✅ **COMPLETE** | April 27, 2026 |
| Phase 5 | Dealer Dashboard | ✅ **COMPLETE** | April 27, 2026 |
| Phase 5.1 | Security Audit & Hardening | ✅ **COMPLETE** | May 4, 2026 |
| Phase 6 | Real Google API Integration | 🟡 **QUEUED** | Awaiting Credentials |
| Phase 7 | Polish & Production | ⬜ **PENDING** | — |

---

## PHASE 0 — Cleanup & Restructure ✅

> **Goal**: Transform prototype into a modular, production-ready codebase.

- [x] **0.1** Removed V1 commented-out code from `frontend/index.html` and `frontend/app.js`
- [x] **0.2** Created modular folder structure (`auth/`, `agency/`, `client/`, `dealer/`, `google/`, `middleware/`)
- [x] **0.3** Created `backend/app/config.py` — loads all `.env` values with defaults
- [x] **0.4** Updated `backend/.env` — added `JWT_SECRET`, `USE_MOCK_DATA=true`, blank Google fields
- [x] **0.5** Created `backend/app/main.py` — FastAPI app with CORS + all 4 router mounts + `/api/health`
- [x] **0.6** Updated `backend/requirements.txt` — includes `PyJWT`, `bcrypt`, `httpx`, `python-multipart`, `email-validator`
- [x] **0.7** Moved mock data → `backend/app/google/mock_api.py`, added `direction_clicks`
- [x] **0.8** Added `__init__.py` files to all submodules
- [x] **0.9** Google OAuth stub (`google_oauth.py`) mounted in `main.py` at `/api/auth`

---

## PHASE 1 — Database Schema & Seed ✅

> **Goal**: Set up 5-table schema and seed the agency superuser.

- [x] **1.1** Created `backend/schema.sql` — canonical DDL for all 5 tables
- [x] **1.2** Created `users` table: `id`, `email`, `password_hash`, `full_name`, `role`, `parent_id`, `needs_password_change`, `is_active`, `created_at`, `updated_at`
- [x] **1.3** Created `clients` table: `id`, `agency_id`, `user_id`, `name`, `gbp_account_id`, `is_active`
- [x] **1.4** Created `dealers` table: `id`, `client_id`, `user_id`, `location_id`, `name`, `city`, `state`, `address`, `is_active`
- [x] **1.5** Created `metrics_cache` table: key-value design (`location_id`, `metric_name`, `metric_value`)
- [x] **1.6** Created `trend_cache` table: monthly historical views (`location_id`, `month`, `total_views`)
- [x] **1.7** Created `backend/seed_admin.py` — seeds `admin@hashtagorang.in` / `Admin@123` with bcrypt hash
- [x] **1.8** Created `backend/seed_test_data.py` — seeds Fenesta client + 3 dealers (Gurugram, Noida, Jaipur)
- [x] **1.9** Mock metrics + 6-month trend data seeded for all 3 locations

> ⚠️ **Known Divergence**: Schema uses key-value `metrics_cache` (flexible) instead of columnar design in `architecture.md`. This is intentional — no migration needed to add new metrics.

---

## PHASE 2 — Authentication System ✅

> **Goal**: Unified login for all 3 roles with JWT + forced password change on first login.

- [x] **2.1** Created `backend/app/auth/jwt_handler.py` — `create_access_token()` + `verify_token()` (HS256, 24h)
- [x] **2.2** Created `backend/app/middleware/auth.py` — `get_current_user()` + `require_role()` dependency
- [x] **2.3** `POST /api/auth/login` — email + password → bcrypt verify → JWT + role + `needs_password_change` + `full_name`
- [x] **2.4** `POST /api/auth/change-password` — validates password strength (8+ chars, upper/lower/digit/special), verifies `old_password`, hashes new, clears `needs_password_change`
- [x] **2.5** Created `backend/app/auth/google_oauth.py` — working stub: returns redirect URL when configured, friendly message when not
- [x] **2.6** Google OAuth stub mounted at `GET /api/auth/google` and `GET /api/auth/google/callback`
- [x] **2.7** Frontend: unified dark-themed login page (gradient background, glassmorphism card, Inter font)
- [x] **2.8** Frontend: "Sign in with Google" button (shows friendly alert when not configured)
- [x] **2.9** Frontend: password change screen shown when `needs_password_change=true`
- [x] **2.10** Frontend: JWT stored in `localStorage`, sent as `Bearer` header on all requests
- [x] **2.11** Frontend: `logout()` clears localStorage and reloads

---

## PHASE 3 — Agency Dashboard ✅

> **Goal**: Hashtag Orange can manage brand clients.

- [x] **3.1** `GET /api/agency/clients` — returns clients with `dealer_count` via subquery, filtered by `agency_id`
- [x] **3.2** `POST /api/agency/clients` — auto-generates temp password via `secrets.token_urlsafe(8)`, creates `users` + `clients` rows atomically
- [x] **3.3** `DELETE /api/agency/clients/{id}` — cascade soft-delete: sets `is_active=FALSE` on client, user, AND all child dealers + users
- [x] **3.4** `PUT /api/agency/clients/{id}/reactivate` — cascade reactivation
- [x] **3.5** `GET /api/agency/clients/{id}/dealers` — view dealer list modal
- [x] **3.6** `POST /api/agency/my-location` — initialize agency's own internal location tracking
- [x] **3.7** `GET /api/agency/dashboard/my-location` — agency's own metrics + trend data
- [x] **3.8** Frontend: client table with Name, Email, Dealer Count, Status, Deactivate/Reactivate actions
- [x] **3.9** Frontend: "Add Client" modal with Name, Email, GBP Account ID fields
- [x] **3.10** Frontend: temporary password displayed in toast notification (15 second auto-dismiss)
- [x] **3.11** Frontend: "My Location" tab with setup screen + metrics dashboard
- [x] **3.12** Fenesta seeded as default test client via `seed_test_data.py`

> ⬜ **Missing** (not blocking): `PUT /api/agency/clients/{id}` (edit client name/GBP ID) — not yet implemented.

---

## PHASE 4 — Client Dashboard ✅

> **Goal**: Fenesta can manage dealers and see global, network, HQ, and drill-down metrics.

- [x] **4.1** `GET /api/client/dealers` — lists sub-dealers (excluding HQ) via `user_id → clients → dealers` chain
- [x] **4.2** `POST /api/client/dealers` — auto-generates temp password, creates `users` + `dealers` rows, triggers mock metric sync
- [x] **4.3** `DELETE /api/client/dealers/{id}` — soft-delete dealer + user
- [x] **4.4** `PUT /api/client/dealers/{id}/reactivate` — reactivates dealer + user
- [x] **4.5** `GET /api/client/dashboard/global` — SUMs ALL metrics (dealers + HQ), computes weighted avg rating
- [x] **4.6** `GET /api/client/dashboard/network` — SUMs sub-dealer metrics only (excludes HQ)
- [x] **4.7** `GET /api/client/dashboard/hq` — HQ-only metrics from `metrics_cache` + `trend_cache`
- [x] **4.8** `POST /api/client/my-location` — links client's HQ location using `HqLocationRequest` (no dummy email)
- [x] **4.9** `GET /api/client/dashboard/dealer/{id}` — single dealer metrics with ownership verification (403 if wrong client)
- [x] **4.10** Frontend: 4-tab layout (Global Overview / Dealer Network / Add Dealer / HQ Location)
- [x] **4.11** Frontend: Global tab has 8 KPI cards + 6-month trend chart
- [x] **4.12** Frontend: Network tab has dealer table + drill-down modal with metrics + trend chart
- [x] **4.13** Frontend: Add Dealer tab with form + auto-generated temp password shown on success
- [x] **4.14** Frontend: HQ tab with setup form or metrics dashboard
- [x] **4.15** 3 test dealers seeded: Gurugram, Noida, Jaipur

---

## PHASE 5 — Dealer Dashboard ✅

> **Goal**: Dealers see only their own location data. 7 KPI cards + posting.

- [x] **5.1** `GET /api/dealer/dashboard` — fetches metrics + 6-month trend data for THIS dealer's `location_id` only
- [x] **5.2** `POST /api/dealer/posts/create` — accepts `content`, logs to console (mock), returns success message
- [x] **5.3** Frontend: 7 KPI cards — Total Views, Search Impressions, Map Views, Website Clicks, Calls Made, **Direction Clicks** (cyan border), Average Rating
- [x] **5.4** Frontend: 6-month trend chart (Chart.js line chart with fill, data from `trend_cache`)
- [x] **5.5** Frontend: "Create Post" button → modal → submit to API
- [x] **5.6** Security: Dealer cannot access `/api/agency/*` or `/api/client/*` — middleware returns 403
- [x] **5.7** Security: `user_id` from JWT is verified against `dealers` table on every request

---

## PHASE 5.1 — Security Audit & Hardening ✅

> **Goal**: Fix all bugs, clean dead code, harden security.

- [x] **5.1.1** Added `trend_cache` table to `schema.sql` and `database.py` (was missing — critical crash bug)
- [x] **5.1.2** Fixed cascade deactivation: disabling a client now cascades to all child dealers + users
- [x] **5.1.3** Fixed `agency-total-dealers` → `agency-total-reviews` element ID mismatch in `index.html`
- [x] **5.1.4** Fixed agency `my-location` with explicit idempotency checks (no more ON CONFLICT issues)
- [x] **5.1.5** Added `full_name` to `get_current_user()` middleware return dict
- [x] **5.1.6** Added password strength validation (8+ chars, upper/lower/digit/special)
- [x] **5.1.7** Added XSS protection via `sanitize()` helper on all template literal injections
- [x] **5.1.8** Created `HqLocationRequest` model — client HQ no longer requires dummy email
- [x] **5.1.9** Removed ~2,200 lines of dead commented-out code across 4 files
- [x] **5.1.10** Updated all documentation to match current 4-tab UI + security features

---

## Test Credentials (Seed Script Values — May 4, 2026)

| Role | Email | Password | First Login? |
| --- | --- | --- | --- |
| Agency (Hashtag Orange) | `admin@hashtagorang.in` | `Admin@123` | ❌ No |
| Client (Fenesta) | `fenesta@example.com` | `Client@123` | ✅ Yes → forced change |
| Dealer 1 (Gurugram) | `dealer.gurugram@fenesta.com` | `Dealer@123` | ✅ Yes → forced change |
| Dealer 2 (Noida) | `dealer.noida@fenesta.com` | `Dealer@123` | ✅ Yes → forced change |
| Dealer 3 (Jaipur) | `dealer.jaipur@fenesta.com` | `Dealer@123` | ✅ Yes → forced change |

---

## Known Gaps (To Fix in Phase 6/7)

| # | Gap | Priority |
| --- | --- | --- |
| 1 | Real Google Business Profile API integration | High (Phase 6) |
| 2 | Rate limiting on auth endpoints | High (Phase 6) |
| 3 | Email delivery of temp passwords (currently display-only) | Medium |
| 4 | `PUT /api/agency/clients/{id}` — edit client name | Low |
| 5 | Loading states / skeleton screens on dashboard | Low |
