# 🤖 Gemini Phase 6 & 7 Build Plan

> **For**: Pass this file to Gemini when ready to proceed with real Google API integration.
> **Prerequisites**: Phases 0–5 complete ✅ | Google Cloud credentials required 🔑
> **Last Updated**: April 27, 2026 — 5:00 PM IST

---

## 🔴 HUMAN CHECKLIST BEFORE HANDING TO GEMINI

Complete all of these **before** starting Phase 6. Gemini cannot proceed without them.

| # | Task | Who | Status |
| --- | --- | --- | --- |
| 1 | Create a Google Cloud Project | Developer | ⬜ Pending |
| 2 | Enable "Business Profile Performance API" | Developer | ⬜ Pending |
| 3 | Enable "My Business Account Management API" | Developer | ⬜ Pending |
| 4 | Enable "My Business Business Information API" | Developer | ⬜ Pending |
| 5 | Enable "Google My Business API v4" (for posts) | Developer | ⬜ Pending |
| 6 | Create OAuth 2.0 Web Application credentials | Developer | ⬜ Pending |
| 7 | Add `http://localhost:8000/api/auth/google/callback` as authorized redirect URI | Developer | ⬜ Pending |
| 8 | Submit GBP API access request form to Google | Agency | ⬜ Pending |
| 9 | Wait for Google API approval email | Google | ⬜ Pending |
| 10 | Create a Service Account (for server-to-server GBP access) | Developer | ⬜ Pending |
| 11 | Add service account email as Manager to Fenesta GBP Location Group | Agency | ⬜ Pending |
| 12 | Fill `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in `backend/.env` | Developer | ⬜ Pending |

> **⚠️ Key Risk**: Step 8 (API approval) can take **days to weeks**. Start this immediately even if code isn't ready.
> Apply here: <https://developers.google.com/my-business/content/prerequisites>

---

## 📋 CONTEXT FOR GEMINI

You are continuing the **Hashtag Orange GBP Dealer Dashboard** — a 3-tier SaaS tool (Agency → Client → Dealer) for monitoring Google Business Profile data. Phases 0–5 are complete and working with mock data. You are now implementing **Phase 6: Real Google API Integration**.

### Read These Files First (in order)

1. `claude.md` — Full project context and architecture
2. `PROGRESS_TRACKER.md` — Exactly what's done and what's still pending
3. `system_documentation.md` — File-by-file responsibilities and function map
4. `architecture.md` — Database schema (Section 5) and API contracts (Section 6)
5. `backend/.env` — Current config values (Google credentials should now be filled)
6. `human_inputs.md` — Sections 6 and 7 for Google credentials and dealer location IDs

### Current Stack (do not change)

- **Backend**: FastAPI + pg8000 (PostgreSQL) + PyJWT + bcrypt
- **Frontend**: Vanilla JS + TailwindCSS CDN + Chart.js CDN
- **Mock mode**: `USE_MOCK_DATA=true` in `.env` — KEEP this working as a fallback at all times

---

## PHASE 6 — Real Google API Integration 🟡

### 6A: Google OAuth2 Login (Activate the Stub)

The stub is already coded in `backend/app/auth/google_oauth.py`. When `GOOGLE_CLIENT_ID` is set in `.env`, it auto-activates. **No code changes needed for the stub logic itself.**

However, complete the callback to handle real token exchange:

**File**: `backend/app/auth/google_oauth.py`

- [ ] **6A.1** Implement `GET /api/auth/google/callback`:
  - Accept `code` parameter from Google redirect
  - Use `httpx` to exchange code for access token: `POST https://oauth2.googleapis.com/token`
  - Use access token to fetch user info: `GET https://www.googleapis.com/oauth2/v2/userinfo`
  - Extract `email` from response
  - Query `users` table: if email exists AND `is_active=TRUE` → issue JWT → return to frontend
  - If email NOT found → return `{"detail": "You are not registered. Contact your admin."}`
  - If `needs_password_change=TRUE` → flag it in the JWT response (same as email login)

```python
# Token exchange call
POST https://oauth2.googleapis.com/token
{
  "code": <code_from_query_param>,
  "client_id": settings.GOOGLE_CLIENT_ID,
  "client_secret": settings.GOOGLE_CLIENT_SECRET,
  "redirect_uri": "http://localhost:8000/api/auth/google/callback",
  "grant_type": "authorization_code"
}
```

- [ ] **6A.2** Update frontend `googleLogin()` in `app.js`:
  - On callback, the page URL will have `?code=...` — extract it and POST to `/api/auth/google/callback`
  - Handle the JWT response the same way as email login
  - Store token + role + name in `localStorage`, call `showDashboard(role)`

---

### 6B: Google Business Profile API Wrapper

**File**: `backend/app/google/gbp_api.py` (currently a stub — implement it)

- [ ] **6B.1** Implement `get_performance_metrics(location_id, start_date, end_date)`:

```python
# API endpoint
GET https://businessprofileperformance.googleapis.com/v1/locations/{location_id}:fetchMultiDailyMetricsTimeSeries

# Metrics to request
dailyMetrics = [
    "BUSINESS_IMPRESSIONS_DESKTOP_SEARCH",
    "BUSINESS_IMPRESSIONS_MOBILE_SEARCH",
    "BUSINESS_IMPRESSIONS_DESKTOP_MAPS",
    "BUSINESS_IMPRESSIONS_MOBILE_MAPS",
    "WEBSITE_CLICKS",
    "CALL_CLICKS",
    "BUSINESS_DIRECTION_REQUESTS"
]
```

Map API response to our internal metric names:

```python
METRIC_MAP = {
    "BUSINESS_IMPRESSIONS_DESKTOP_SEARCH": "search_impressions",
    "BUSINESS_IMPRESSIONS_MOBILE_SEARCH": "search_impressions",  # ADD to same key
    "BUSINESS_IMPRESSIONS_DESKTOP_MAPS": "map_views",
    "BUSINESS_IMPRESSIONS_MOBILE_MAPS": "map_views",            # ADD to same key
    "WEBSITE_CLICKS": "website_clicks",
    "CALL_CLICKS": "calls_made",
    "BUSINESS_DIRECTION_REQUESTS": "direction_clicks"
}
# total_views = search_impressions + map_views (derived)
```

- [ ] **6B.2** Implement `create_local_post(location_id, content)`:

```python
# API endpoint
POST https://mybusiness.googleapis.com/v4/accounts/{account_id}/locations/{location_id}/localPosts

# Request body
{
    "languageCode": "en",
    "summary": content,
    "callToAction": {"actionType": "LEARN_MORE", "url": "..."},
    "topicType": "STANDARD"
}
```

- [ ] **6B.3** Implement `get_location_reviews(location_id)`:

```python
# Fetch reviews and rating
GET https://mybusiness.googleapis.com/v4/accounts/{account_id}/locations/{location_id}/reviews
```

- [ ] **6B.4** Authentication for all API calls — use Service Account:

```python
from google.oauth2 import service_account
import googleapiclient.discovery

SCOPES = [
    'https://www.googleapis.com/auth/business.manage',
    'https://www.googleapis.com/auth/businessprofileperformance.readonly'
]

credentials = service_account.Credentials.from_service_account_file(
    'path/to/service-account.json',
    scopes=SCOPES
)
```

---

### 6C: Metric Sync Engine

**File**: `backend/app/google/sync.py` (currently a stub — implement it)

- [ ] **6C.1** Implement `sync_location_metrics(location_id)`:
  - If `USE_MOCK_DATA=true`: call `get_mock_google_metrics(location_id)` (existing mock)
  - If `USE_MOCK_DATA=false`: call `gbp_api.get_performance_metrics(location_id, ...)`
  - Write results to `metrics_cache` using `INSERT ... ON CONFLICT DO UPDATE` (upsert)

- [ ] **6C.2** Implement `sync_all_active_dealers()`:
  - Query all dealers where `is_active=TRUE`
  - Call `sync_location_metrics()` for each
  - Log success/failure per location
  - Target: runs daily via scheduler

- [ ] **6C.3** Add `POST /api/internal/sync-metrics` endpoint (agency only):
  - Manual trigger for sync
  - Returns sync status per dealer

- [ ] **6C.4** Add APScheduler for daily automatic sync:

```python
# In main.py startup event
from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()
scheduler.add_job(sync_all_active_dealers, 'cron', hour=2)  # 2 AM daily
scheduler.start()
```

---

### 6D: Switch Dashboard to Real Data

Once sync is working, update all dashboard endpoints to read from `metrics_cache` (they already do, since sync writes there). The toggle `USE_MOCK_DATA` controls which data source populates the cache.

- [ ] **6D.1** Update `dealer/routes.py`: fetch latest metrics from `metrics_cache` with a date filter
- [ ] **6D.2** Update `client/routes.py`: cumulative endpoint reads real metrics from cache
- [ ] **6D.3** Update trend chart: fetch 6 months of real data from `metrics_cache` grouped by month
- [ ] **6D.4** Wire up real `POST /api/dealer/posts/create` to call `gbp_api.create_local_post()`
- [ ] **6D.5** Set `USE_MOCK_DATA=false` in `.env` after verifying real data flows correctly

**Validation**: Login as `dealer.gurugram@fenesta.com` → See real GBP data → Trend chart shows actual 6-month history → Create a test post that appears on Google.

---

## PHASE 7 — Polish & Production ⬜

> To be started after Phase 6 is verified stable with real data.

### 7A: Missing API Endpoints

- [ ] **7A.1** `PUT /api/agency/clients/{id}` — update client name, GBP account ID
- [ ] **7A.2** `PUT /api/client/dealers/{id}` — update dealer name, location ID, city, state
- [ ] **7A.3** `DELETE /api/client/dealers/{id}` — soft-delete dealer (set `is_active=FALSE`)
- [ ] **7A.4** `GET /api/agency/clients/{id}/dealers` — agency oversight view of client's dealers
- [ ] **7A.5** `POST /api/auth/logout` — server-side token invalidation (add token blocklist)

### 7B: Email System

- [ ] **7B.1** Integrate SendGrid (or SMTP) for transactional email
- [ ] **7B.2** Send onboarding email with temp password when client/dealer is created
- [ ] **7B.3** Send weekly performance summary email to client (metrics highlights)

### 7C: Frontend Polish

- [ ] **7C.1** Replace `alert()` drill-down popup with a proper slide-over panel or modal
- [ ] **7C.2** Add loading skeleton screens while API calls are in progress
- [ ] **7C.3** Add proper 403/404/500 error pages
- [ ] **7C.4** Add date range picker to dashboards (last 7/30/90 days)
- [ ] **7C.5** Add data export (CSV download) for client cumulative dashboard
- [ ] **7C.6** Add search/filter to dealer list table
- [ ] **7C.7** Agency: add oversight view — click a client to see their dealers

### 7D: Security & Performance

- [ ] **7D.1** Restrict CORS from `*` to specific production domain
- [ ] **7D.2** Add rate limiting on auth endpoints (e.g., max 5 login attempts per minute)
- [ ] **7D.3** Add input validation and sanitization on all POST endpoints
- [ ] **7D.4** HTTPS enforcement (SSL certificate)
- [ ] **7D.5** Rotate `JWT_SECRET` with a proper 48-char generated value
- [ ] **7D.6** Security audit: SQL injection, XSS, CSRF checks
- [ ] **7D.7** Add request logging (`structlog` or similar)

### 7E: Deployment

- [ ] **7E.1** Create `Dockerfile` for backend
- [ ] **7E.2** Create `docker-compose.yml` (backend + postgres)
- [ ] **7E.3** Write VPS deployment guide (Nginx + Gunicorn)
- [ ] **7E.4** Set up CI/CD pipeline (GitHub Actions)
- [ ] **7E.5** Configure PostgreSQL for production (connection pooling, backups)
- [ ] **7E.6** Set up monitoring (Sentry for errors, Uptime Robot for availability)

---

## API Reference for Phase 6

### Google Business Profile Performance API

```text
Base URL: https://businessprofileperformance.googleapis.com/v1/

Key Endpoint:
  GET /locations/{location_id}:fetchMultiDailyMetricsTimeSeries

  Required metrics for our dashboard:
  - BUSINESS_IMPRESSIONS_DESKTOP_SEARCH  → search_impressions (desktop)
  - BUSINESS_IMPRESSIONS_MOBILE_SEARCH   → search_impressions (mobile)
  - BUSINESS_IMPRESSIONS_DESKTOP_MAPS    → map_views (desktop)
  - BUSINESS_IMPRESSIONS_MOBILE_MAPS     → map_views (mobile)
  - WEBSITE_CLICKS                       → website_clicks
  - CALL_CLICKS                          → calls_made
  - BUSINESS_DIRECTION_REQUESTS          → direction_clicks ← critical for us

Date range format:
  "dailyRange": {
    "startDate": {"year": 2025, "month": 10, "day": 1},
    "endDate": {"year": 2026, "month": 4, "day": 27}
  }
```

### Google My Business API v4 (Posts)

```text
Base URL: https://mybusiness.googleapis.com/v4/

Create Post:
  POST /accounts/{account_id}/locations/{location_id}/localPosts
  Body: {
    "languageCode": "en",
    "summary": "<post content>",
    "topicType": "STANDARD"
  }
```

### Reviews API

```text
GET /accounts/{account_id}/locations/{location_id}/reviews
Response includes: averageRating, totalReviewCount, reviews[]
```

---

## Files To Create in Phase 6

| File | What Goes In It |
| --- | --- |
| `backend/app/google/gbp_api.py` | Real GBP API calls (performance, posts, reviews) |
| `backend/app/google/sync.py` | Sync engine: fetch metrics → write to `metrics_cache` |
| `backend/app/auth/google_oauth.py` | Complete the OAuth callback (already stubbed) |
| `backend/service_account.json` | Google service account key (DO NOT commit to git) |
| `backend/.gitignore` | Must include `service_account.json`, `.env` |

---

## Files To Modify in Phase 6

| File | What Changes |
| --- | --- |
| `backend/app/main.py` | Add APScheduler startup event for daily sync |
| `backend/app/dealer/routes.py` | Trend chart endpoint with real date-range data |
| `backend/app/client/routes.py` | Trend data for cumulative chart |
| `backend/.env` | Set `USE_MOCK_DATA=false`, fill Google credentials |
| `frontend/app.js` | Handle Google OAuth callback in URL, wire up real trend chart |

---

> **When you are ready**: Fill in the Human Checklist at the top of this file, then hand it to Gemini with the instruction:
> *"Read `claude.md` first, then this file. Implement Phase 6 starting with 6A (Google OAuth callback). Do not modify Phase 0–5 code unless fixing a bug."*
