# Damaris Admin · Project Tracker Setup

The admin page at `/admin/projects.html` is a project editor that reads + writes the master Google Sheet (`Damaris · Client Project Tracker`). Every client dashboard pulls from the same sheet on every page load — so any change you save here propagates to every client.

## One-time setup · ~5 minutes

### 1. Share the sheet for reads

1. Open the sheet: <https://docs.google.com/spreadsheets/d/10TmTZ5AwiorQxdltY3lId8lO8AZlL_YRJAp4nleoxFE/edit>
2. Click **Share** (top-right) → "General access" → **Anyone with the link · Viewer** → Done.

That covers the **read** path used by every client dashboard.

### 2. Add the Apps Script for writes

1. Same sheet → menu: **Extensions → Apps Script**
2. Delete the boilerplate `function myFunction()` placeholder.
3. Open `admin/projects-apps-script.gs` from this repo, copy ALL of it, paste into the Apps Script editor.
4. **Change `SECRET`** on line 23 to a long random string (e.g. 32+ chars). Anyone who knows this token can write — keep it private.
5. Click the floppy-disk **Save** icon (or `Cmd+S`).
6. Click **Deploy → New deployment** (top-right).
7. Cog icon → **Web app**.
8. Description: anything ("v1"). **Execute as: Me**. **Who has access: Anyone**.
9. Click **Deploy**. Authorize when prompted (Google will warn it's an unverified script — that's expected for personal Apps Scripts; click Advanced → Go to script (unsafe) → Allow).
10. Copy the **Web app URL** (looks like `https://script.google.com/macros/s/AKfy.../exec`).

### 3. Connect the admin page

1. Open <https://damaris-dashboards.pages.dev/admin/projects.html>
2. In the **Setup · Apps Script connection** card:
   - Paste the Web app URL.
   - Paste the SECRET you set in step 2.
3. Click **Save & test**. You should see "✓ Apps Script reachable · config saved".
4. The URL and token are stored in your browser's localStorage only — they never get committed to the repo.

You're done. Add/edit/delete projects on the admin page → they appear on every client dashboard on next page refresh.

## What the API supports

The Apps Script exposes a single endpoint with four actions:

| Action | Body | Purpose |
|---|---|---|
| `upsert` | `{ token, action:"upsert", row:{...} }` | Update by `(Client, Project)`, insert if not found |
| `delete` | `{ token, action:"delete", client, project }` | Remove the matching row |
| `bulk_replace` | `{ token, action:"bulk_replace", rows:[...] }` | Wipe data rows and write all again |
| `list` | `{ token, action:"list" }` | Return every row (authenticated read; the dashboards use the public gviz reader instead) |

Plus task actions (`task_upsert` / `task_delete` / `task_list` / `task_list_for`) on a `Tasks` tab, and CRM lead actions:

| Action | Body | Purpose |
|---|---|---|
| `lead_upsert` | `{ token, action:"lead_upsert", row:{...} }` | Update by `ID`, insert if not found |
| `lead_delete` | `{ token, action:"lead_delete", id }` | Remove the matching lead |
| `lead_list` | `{ token, action:"lead_list" }` | Return every lead (CRM page uses the public gviz reader instead) |

The `Tasks` and `Leads` tabs auto-create themselves with the right headers on first write — no manual setup.

All requests are HTTP POST to the Web App URL with `Content-Type: text/plain;charset=utf-8` (this avoids the CORS preflight that would otherwise fail against Google's redirect).

## CRM · Lead Tracker (`admin/crm.html`)

A lead / prospect / client pipeline linked from the **main dashboards page only** (the `🎯 Lead CRM` button in the header — not on any client landing page). Table view + draggable Kanban pipeline: **New → Contacted → Proposal Sent → Negotiating → Won → Active Client → Lost**. Current (active) clients live in the `Active Client` column, so the CRM tracks both prospects and ongoing clients in one board. It reuses the **same** Apps Script URL + token as the Project Tracker (shared `localStorage` keys), so if the Project Tracker is already connected, the CRM works with zero extra setup. Records live on the auto-created `Leads` tab.

The stat strip shows per-stage counts plus **Active clients $** (recurring revenue summed from `Active Client` rows) and **Open pipeline** (summed from New/Contacted/Proposal Sent/Negotiating, with won total in the subline). A `↓ Import clients` button one-time-seeds your known clients + sample-dashboard prospects (idempotent — stable `seed-*` IDs, so re-running updates rather than duplicates); active clients link to their live dashboard, which renders as a `📊 Dashboard` chip.

**Persistence is local-first.** Every add/edit/delete/drag auto-saves to the browser (`localStorage` key `damaris.crm.leads`) — no Google setup required, so the CRM works immediately on first visit. Load priority on open: (1) Google Sheet `Leads` tab if connected + populated, (2) browser auto-save, (3) the built-in starter list (`SEED_CLIENTS`). When not synced to the sheet, a banner says "Saved in this browser" and offers Import (push to sheet) + `⬇ Backup` (download a JSON file). The Google Sheet is **optional cross-device sync** layered on top: connect the Apps Script (URL+token) and click Import, after which the sheet becomes the source of truth and writes mirror to it best-effort (a failed sheet write never loses the local copy). This means a sheet/Apps-Script outage degrades to local-only rather than breaking the page.

Lead row schema: `ID` (auto-generated upsert key), `Business` (required), `Contact`, `Email`, `Phone`, `Status`, `Source`, `Industry`, `Value`, `Website`, `Socials` (one URL per line), `Proposal`, `Notes`, `Next Action`, `Next Date`, `Created`, `Last Update` (auto-set on every upsert).

## Row schema

| Column | Required | Notes |
|---|---|---|
| `Client` | ✓ | Matches the client key the dashboard filters on (e.g. `JMB`) |
| `Project` | ✓ | The display name. `(Client, Project)` is the upsert key — change it carefully |
| `Category` | | Free text (`Advertising`, `SEO`, `Strategy`, …) |
| `Phase` | | `Planning · In Progress · Review · Live · Complete · On Hold` |
| `Status` | | `On Track · At Risk · Blocked · Complete` |
| `Start Date` | | `YYYY-MM-DD` |
| `Due Date` | | `YYYY-MM-DD` or literal `Ongoing` |
| `Percent Complete` | | `0–100` |
| `Owner` | | Free text (`Damaris`, `Client`, `Both`) |
| `Description` | | Shown on the client card |
| `Last Update` | | Auto-set to "now" on every upsert |

## Wiring up your own task dashboard / Claude artifact

To have a separate task dashboard write to the same sheet, copy this fetch snippet:

```js
const PROJECT_API = 'https://script.google.com/macros/s/AKfy.../exec';
const PROJECT_TOKEN = 'your-shared-secret';

async function saveProject(row) {
  const res = await fetch(PROJECT_API, {
    method: 'POST',
    headers: { 'Content-Type': 'text/plain;charset=utf-8' },
    body: JSON.stringify({ token: PROJECT_TOKEN, action: 'upsert', row })
  });
  return res.json();
}

// Example:
await saveProject({
  Client: 'JMB',
  Project: 'Q3 strategy review',
  Phase: 'Planning',
  Status: 'On Track',
  'Start Date': '2026-07-01',
  'Due Date': '2026-07-15',
  'Percent Complete': 0,
  Owner: 'Both',
  Description: 'Quarterly check-in on campaign performance and next 90-day priorities.'
});
```

Same pattern for delete and bulk_replace — just swap the `action` field.

## Adding a new client to the live tracker

1. Add their `Client` key on each new project row in the sheet (e.g. `JMB`, `Prometheus`, `Brightwood`).
2. Open that client's dashboard HTML and change `const PROJ_CLIENT_KEY = '...';` to match.
3. Make sure the dashboard has the Projects tab + Summary card (currently only JMB). Ask Claude to mirror the JMB pattern.

## Security model

- **Reads** are open — the sheet is shared as "Anyone with the link · Viewer". Project descriptions are visible to anyone with the URL. Don't put secrets in the sheet.
- **Writes** require the shared SECRET in the request body. Anyone who knows the URL + token can write. Treat the token like a password.
- If the token ever leaks, change `SECRET` in the Apps Script and re-deploy. All current connections (admin page, artifact, etc.) need the new token.
