Summary
The dashboard's Local Presence (Google Business Profile) section only exposes the tail of GBP setup: track/untrack an already-discovered location and trigger a sync. Every onboarding step before that is CLI-only, and the section's empty states hard-code cnry commands as the instruction. GBP is the only Google integration without a UI onboarding flow. GSC already drives the full OAuth connect + property selection from the dashboard (apps/web/src/components/project/GscSection.tsx), and GA/Bing have UI connect + disconnect.
Current state (web)
The web app wires only these GBP operations:
- reads:
GET /gbp/locations, /gbp/summary, /gbp/keywords, /gbp/places
POST /gbp/sync
PUT /gbp/locations/:locationName/selection (track / untrack)
Missing from the UI, CLI-only today:
| Step |
Endpoint (already exists) |
CLI |
| Connect (OAuth grant) |
shared Google OAuth client, gbp connectionType |
cnry gbp connect |
| Pick account |
GET /gbp/accounts |
cnry gbp accounts |
| Discover locations |
POST /gbp/locations/discover |
cnry gbp locations discover |
| Disconnect |
DELETE /gbp/connection |
cnry gbp disconnect |
The not-connected empty state currently reads "Connect one with cnry gbp connect <project>" and the manage-locations panel reads "Run cnry gbp locations discover". A dashboard user has to drop to a terminal to onboard.
Why
- This is a web-only gap, not a capability gap. The API and CLI already support the entire flow, so no backend work is required. It is purely wiring web components to existing endpoints.
- Onboarding a dashboard user through a terminal command is a rough first-run experience for the one integration that is most visual (a map listing).
- Parity is already satisfied on the API/CLI side; this brings the UI up to the same surface that GSC, GA, and Bing already have.
Proposed
Mirror the existing GscSection.tsx OAuth pattern (GBP shares the same Google OAuth app credentials, distinguished by the gbp connectionType, so connect can reuse the GSC redirect handling, not a second credential set):
- Not-connected empty state gets a "Connect Google Business Profile" button that starts the shared Google OAuth flow.
- After connect, if no account is selected, show an account picker fed by
GET /gbp/accounts.
- A discover control calls
POST /gbp/locations/discover for the chosen account, then the existing location list / track UI takes over.
- A disconnect control (
DELETE /gbp/connection) with a confirm step, matching the GA / Bing disconnect affordance. Note this clears the project's whole GBP footprint, so the confirm copy should say so.
Acceptance criteria
- A user can go from zero to synced GBP data without leaving the dashboard.
- Connect reuses the shared Google OAuth client (no second app credential set, no new OAuth route).
- Account selection and location discovery are both reachable from the UI.
- Disconnect is available with a confirmation and clears the project's GBP footprint.
- UI/CLI parity holds: no UI-only logic, all reads/writes go through the existing endpoints.
- Agent-first posture preserved: the CLI stays primary; this is supplementary UI, and the existing empty-state CLI hints can remain as a secondary path.
Out of scope
- Writing to the public listing (CTAs, lodging attributes, hours). That needs new backend work against Google's write APIs and is a separate, permission-sensitive decision.
- Any change to the GBP read/summary surfaces.
Summary
The dashboard's Local Presence (Google Business Profile) section only exposes the tail of GBP setup: track/untrack an already-discovered location and trigger a sync. Every onboarding step before that is CLI-only, and the section's empty states hard-code
cnrycommands as the instruction. GBP is the only Google integration without a UI onboarding flow. GSC already drives the full OAuth connect + property selection from the dashboard (apps/web/src/components/project/GscSection.tsx), and GA/Bing have UI connect + disconnect.Current state (web)
The web app wires only these GBP operations:
GET /gbp/locations,/gbp/summary,/gbp/keywords,/gbp/placesPOST /gbp/syncPUT /gbp/locations/:locationName/selection(track / untrack)Missing from the UI, CLI-only today:
gbpconnectionTypecnry gbp connectGET /gbp/accountscnry gbp accountsPOST /gbp/locations/discovercnry gbp locations discoverDELETE /gbp/connectioncnry gbp disconnectThe not-connected empty state currently reads "Connect one with
cnry gbp connect <project>" and the manage-locations panel reads "Runcnry gbp locations discover". A dashboard user has to drop to a terminal to onboard.Why
Proposed
Mirror the existing
GscSection.tsxOAuth pattern (GBP shares the same Google OAuth app credentials, distinguished by thegbpconnectionType, so connect can reuse the GSC redirect handling, not a second credential set):GET /gbp/accounts.POST /gbp/locations/discoverfor the chosen account, then the existing location list / track UI takes over.DELETE /gbp/connection) with a confirm step, matching the GA / Bing disconnect affordance. Note this clears the project's whole GBP footprint, so the confirm copy should say so.Acceptance criteria
Out of scope