# GBP Dashboard — System Architecture

> **📋 WHY THIS FILE EXISTS**: This is the single source of truth for all DESIGN DECISIONS.
> It defines the 3-tier user hierarchy, database schema (exact SQL), API endpoint contracts,
> permission matrix, and tech stack. Flash reads this when it needs to know HOW something
> should work — the schema to create, the endpoints to build, or who can access what.
> **Update this file if the design changes.**

## 1. Product Vision

**GBP Manager** is an agency-grade SaaS tool built by **Hashtag Orange** that enables a 3-tier hierarchy (Agency → Client → Dealer) to manage and monitor Google Business Profile performance data.

---

## 2. User Hierarchy & Permissions

```text
┌───────────────────────────────────────────────────────────┐
│                     AGENCY TIER                           │
│                  (Hashtag Orange)                          │
│                                                           │
│  • Add/remove Clients                                     │
│  • View ALL client data (read-only oversight)             │
│  • Configure Google API credentials per client            │
│  • Manage platform settings                               │
│  • View OWN "My Location" GBP data (internal tracking)    │
└───────────────┬───────────────────────────────────────────┘
                │ has_many
                ▼
┌───────────────────────────────────────────────────────────┐
│                     CLIENT TIER                           │
│                    (e.g. Fenesta)                          │
│                                                           │
│  • Add/remove their own Dealers                           │
│  • View GLOBAL dashboard (all locations aggregated)       │
│  • View NETWORK dashboard (sub-dealers only)              │
│  • View HQ LOCATION dashboard (own store metrics)         │
│  • View INDIVIDUAL dealer dashboards (drill-down)         │
│  • Create Google Posts on behalf of dealers                │
│  • CANNOT see other clients' data                         │
└───────────────┬───────────────────────────────────────────┘
                │ has_many
                ▼
┌───────────────────────────────────────────────────────────┐
│                     DEALER TIER                           │
│              (e.g. Fenesta Gurugram South)                 │
│                                                           │
│  • View OWN dashboard only (single location)              │
│  • Create Google Posts for their location                  │
│  • View their own review summary + 6-month trends         │
│  • CANNOT see other dealers' data                         │
│  • CANNOT add/remove users                                │
└───────────────────────────────────────────────────────────┘
```

### Role Summary Table

| Capability | Agency | Client | Dealer |
| --- | :---: | :---: | :---: |
| Add/manage Clients | ✅ | ❌ | ❌ |
| Add/manage Dealers | ❌ | ✅ | ❌ |
| View own location data (My Location / HQ) | ✅ | ✅ | ✅ |
| View global dashboard (all locations) | ❌ | ✅ | ❌ |
| View network dashboard (sub-dealers only) | ❌ | ✅ | ❌ |
| View individual dealer dashboard | ❌ | ✅ | ✅ (own only) |
| Cascade deactivation (client → dealers) | ✅ | ❌ | ❌ |
| Create Google Posts | ❌ | ✅ (any dealer) | ✅ (own only) |
| Configure API credentials | ✅ | ❌ | ❌ |

---

## 3. Authentication Architecture

### Primary Path: Email + Generated Password (MVP — Phase 1)

```text
┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│ Agency Admin │────▶│ Adds Client  │────▶│ System sends │
│ logs in      │     │ (email)      │     │ temp password│
└─────────────┘     └──────────────┘     │ via email    │
                                          └──────┬───────┘
                                                 │
                                          ┌──────▼───────┐
                                          │ Client logs  │
                                          │ in first time│
                                          │ → FORCED     │
                                          │ password     │
                                          │ change       │
                                          └──────────────┘
```

**Flow:**

1. Agency/Client creates a new user → system generates a random temporary password
2. System sends onboarding email with login credentials
3. On first login, user is redirected to a **mandatory password change** screen
4. Passwords stored hashed (bcrypt) in the database
5. JWT tokens used for session management (stateless auth)

### Alternate Path: Google OAuth2 Login (Phase 2)

```text
User clicks "Sign in with Google"
    │
    ▼
Redirect to Google OAuth consent screen
    │
    ▼
Google redirects back with authorization code
    │
    ▼
Backend exchanges code for user info (email, name)
    │
    ▼
Backend checks if email exists in users table
    │
    ├── YES → Issue JWT, log user in
    │
    └── NO  → Reject ("You are not registered. Contact your admin.")
```

**Key**: Google OAuth is **only used for identity verification**, not for GBP API access. The user must already be registered in the system by their Agency/Client admin. We do NOT allow self-registration via Google.

### Google OAuth2 Technical Requirements

| Requirement | Detail |
| --- | --- |
| Google Cloud Console | Create OAuth 2.0 Web Application credentials |
| Authorized redirect URI | `http://localhost:8000/auth/google/callback` (dev) |
| Scopes needed | `openid`, `email`, `profile` (user identity only) |
| Python libraries | `authlib`, `httpx` |
| Client credentials | Stored in `.env`, NOT in `client_secret.json` |

---

## 4. Google Business Profile API Architecture

### APIs Required

| API | Purpose | Endpoint |
| --- | --- | --- |
| **Account Management API** | List accounts, manage admins | `mybusinessaccountmanagement.googleapis.com` |
| **Business Information API** | List locations under an account | `mybusinessbusinessinformation.googleapis.com` |
| **Performance API** | Fetch metrics (views, clicks, calls, directions) | `businessprofileperformance.googleapis.com` |
| **My Business API v4** | Create local posts | `mybusiness.googleapis.com/v4` |

### Available Metrics (from Performance API)

| Metric Enum | Dashboard Label | Currently Using |
| --- | --- | --- |
| `BUSINESS_IMPRESSIONS_DESKTOP_SEARCH` | Search Impressions (Desktop) | ✅ (aggregated) |
| `BUSINESS_IMPRESSIONS_MOBILE_SEARCH` | Search Impressions (Mobile) | ✅ (aggregated) |
| `BUSINESS_IMPRESSIONS_DESKTOP_MAPS` | Map Views (Desktop) | ✅ (aggregated) |
| `BUSINESS_IMPRESSIONS_MOBILE_MAPS` | Map Views (Mobile) | ✅ (aggregated) |
| `WEBSITE_CLICKS` | Website Clicks | ✅ |
| `CALL_CLICKS` | Calls Made | ✅ |
| **`BUSINESS_DIRECTION_REQUESTS`** | **Direction Clicks** | **❌ ADD THIS** |
| `BUSINESS_CONVERSATIONS` | Messages | ❌ (future) |
| `BUSINESS_BOOKINGS` | Bookings | ❌ (future) |

> ✅ **CONFIRMED**: `BUSINESS_DIRECTION_REQUESTS` is a supported daily metric. We can absolutely add "Direction Clicks" to the dashboard.

### API Auth for GBP Data (Separate from User Login!)

This is **different** from user authentication. To access GBP data:

#### Option A — Service Account (Recommended for Agency Model)

- Create a Service Account in Google Cloud
- Add the service account email as Manager on the GBP Organization/Location Group
- No user interaction needed — fully server-to-server
- Best fit since Hashtag Orange manages the GBP accounts

#### Option B — OAuth2 with Refresh Token

- One-time OAuth consent from the account owner
- Store refresh token in database
- Use refresh token to fetch new access tokens automatically
- Needed if agency does NOT have direct management access

### API Access Approval Process (HUMAN REQUIRED)

| Step | Action | Who |
| --- | --- | --- |
| 1 | Create Google Cloud Project | Developer |
| 2 | Enable Business Profile APIs | Developer |
| 3 | Create OAuth consent screen | Developer |
| 4 | Submit API access request form to Google | Agency (must own/manage verified GBP 60+ days) |
| 5 | Wait for Google approval | Google (can take days-weeks) |
| 6 | Verify quota increase in Cloud Console | Developer |

---

## 5. Database Schema

### PostgreSQL — 5 Tables

```sql
-- ============================================
-- USERS TABLE (unified for all 3 roles)
-- ============================================
CREATE TABLE users (
    id              SERIAL PRIMARY KEY,
    email           VARCHAR(255) UNIQUE NOT NULL,
    password_hash   VARCHAR(255) NOT NULL,
    full_name       VARCHAR(255),
    role            VARCHAR(20) NOT NULL CHECK (role IN ('agency', 'client', 'dealer')),
    parent_id       INTEGER,
    needs_password_change BOOLEAN DEFAULT TRUE,
    is_active       BOOLEAN DEFAULT TRUE,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================
-- CLIENTS TABLE (brands managed by the agency)
-- ============================================
CREATE TABLE clients (
    id              SERIAL PRIMARY KEY,
    agency_id       INTEGER REFERENCES users(id),
    user_id         INTEGER UNIQUE REFERENCES users(id),
    name            VARCHAR(255) NOT NULL,
    gbp_account_id  VARCHAR(255),
    logo_url        TEXT,
    is_active       BOOLEAN DEFAULT TRUE,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================
-- DEALERS TABLE (locations under a client)
-- ============================================
CREATE TABLE dealers (
    id              SERIAL PRIMARY KEY,
    client_id       INTEGER REFERENCES clients(id) ON DELETE CASCADE,
    user_id         INTEGER UNIQUE REFERENCES users(id),
    location_id     VARCHAR(100) UNIQUE NOT NULL,
    name            VARCHAR(255) NOT NULL,
    city            VARCHAR(100),
    state           VARCHAR(100),
    address         TEXT,
    is_active       BOOLEAN DEFAULT TRUE,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================
-- CACHED METRICS (key-value design per location)
-- ============================================
CREATE TABLE metrics_cache (
    id              SERIAL PRIMARY KEY,
    location_id     VARCHAR(100) NOT NULL,
    metric_name     VARCHAR(100) NOT NULL,
    metric_value    FLOAT DEFAULT 0,
    updated_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(location_id, metric_name)
);

-- ============================================
-- TREND CACHE (monthly historical views)
-- ============================================
CREATE TABLE trend_cache (
    id              SERIAL PRIMARY KEY,
    location_id     VARCHAR(100) NOT NULL,
    month           DATE NOT NULL,
    total_views     INTEGER DEFAULT 0,
    UNIQUE(location_id, month)
);
```

---

## 6. Backend API Endpoints

### Auth Routes

| Method | Endpoint | Role | Description |
| --- | --- | --- | --- |
| POST | `/api/auth/login` | All | Email + password login |
| POST | `/api/auth/google` | All | Google OAuth2 login |
| GET | `/api/auth/google/callback` | All | Google OAuth2 callback |
| POST | `/api/auth/change-password` | All | Mandatory first-login password change |
| POST | `/api/auth/logout` | All | Invalidate JWT |

### Agency Routes

| Method | Endpoint | Description |
| --- | --- | --- |
| GET | `/api/agency/clients` | List all clients with dealer count |
| POST | `/api/agency/clients` | Add a new client (creates user + client record) |
| DELETE | `/api/agency/clients/{id}` | Deactivate client + cascade to all dealers |
| PUT | `/api/agency/clients/{id}/reactivate` | Reactivate client + cascade to all dealers |
| GET | `/api/agency/clients/{id}/dealers` | View dealers of a specific client |
| POST | `/api/agency/my-location` | Initialize agency's internal location tracking |
| GET | `/api/agency/dashboard/my-location` | Get agency's own location metrics + trends |

### Client Routes

| Method | Endpoint | Description |
| --- | --- | --- |
| GET | `/api/client/dealers` | List sub-dealers (excludes HQ) |
| POST | `/api/client/dealers` | Add a new dealer |
| DELETE | `/api/client/dealers/{id}` | Deactivate dealer |
| PUT | `/api/client/dealers/{id}/reactivate` | Reactivate dealer |
| POST | `/api/client/my-location` | Link client's HQ location |
| GET | `/api/client/dashboard/global` | Aggregated metrics across ALL locations |
| GET | `/api/client/dashboard/network` | Aggregated metrics for sub-dealers only |
| GET | `/api/client/dashboard/hq` | HQ location metrics |
| GET | `/api/client/dashboard/dealer/{id}` | Single dealer's metrics (drill-down) |

### Dealer Routes

| Method | Endpoint | Description |
| --- | --- | --- |
| GET | `/api/dealer/dashboard` | Own metrics + 6-month trend data |
| POST | `/api/dealer/posts/create` | Create a Google Post (mock) |

### Background Sync (Internal)

| Process | Trigger | Description |
| --- | --- | --- |
| `sync_all_active_dealers()` | APScheduler nightly 2 AM | Updates metrics + trend data for all active dealers |
| `sync_location_metrics()` | BackgroundTask on dealer create | Seeds initial metrics + 6-month trend history |

---

## 7. Frontend Pages

| Page | Route | Visible To | Description |
| --- | --- | --- | --- |
| Login | `/login` | All | Unified login with role detection |
| Change Password | `/change-password` | All (first login) | Mandatory password reset |
| Agency Dashboard | `/agency` | Agency | Client list management |
| Client Dashboard | `/client` | Client | Cumulative metrics + dealer list |
| Client → Dealer View | `/client/dealer/:id` | Client | Individual dealer drill-down |
| Dealer Dashboard | `/dealer` | Dealer | Own metrics + post creation |

---

## 8. Tech Stack (Final)

| Layer | Technology | Reason |
| --- | --- | --- |
| Frontend | HTML + Vanilla JS + TailwindCSS CDN + Chart.js | Already in use, lightweight |
| Backend | Python FastAPI + Uvicorn | Already in use, async-capable |
| Database | PostgreSQL (pg8000) | Already in use |
| Auth | JWT (PyJWT) + bcrypt | Stateless, secure |
| Google Auth | authlib + httpx | For OAuth2 "Sign in with Google" |
| GBP API | google-api-python-client + google-auth | For fetching metrics + creating posts |
| Email | smtplib or SendGrid | For sending onboarding emails with temp passwords |

---

## 9. Deployment Architecture (Future)

```text
                    ┌─────────────┐
                    │   Nginx     │
                    │ (Reverse    │
                    │  Proxy)     │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
       ┌──────▼──────┐    │    ┌───────▼──────┐
       │  Frontend    │    │    │   FastAPI    │
       │  (Static)    │    │    │   Backend    │
       └─────────────┘    │    └──────┬───────┘
                          │           │
                    ┌─────▼─────┐     │
                    │ PostgreSQL│◄────┘
                    └───────────┘
```
