Files
descrybe/docs/plan-permissions/09-dashboard-gating.md
T

98 lines
4.2 KiB
Markdown
Raw Normal View History

# 09 — Dashboard gating (agent 9/10)
User-facing dashboard UI respects resolved plan capabilities (`plan ∩ global`).
**Coordinates with:** `01-feature-keys.json`, `03-permission-contract.md`, `05-api.md`.
---
## ASSUMPTIONS
1. **Primary source:** `credits.features` on `GET /api/auth/me` (and `/api/billing/credits`), per contract §8.
2. **Secondary fetch once:** `GET /api/billing/capabilities` from layout after `/me` (full registry + `sections` + etag). Safe if 404/unavailable.
3. **Fail behavior when features map is absent** (pre-cutover or capabilities error without a map):
- **Fail-open** for core Free-loop + account/billing/support keys (`CORE_ALWAYS_ON`).
- **Fail-closed** for paid keys in `PAID_FAIL_CLOSED` (AI process, campaign AI/send, brand AI apply, SEO AI, BYOK, API keys, …) — matches agent-9 brief while keeping billing/settings reachable.
4. **When features map is present:** enforce map + parent-prefix deny (disabled `catalog.products` hides children). Missing key → false.
5. **Admin shell** (`/admin/*`) is out of scope.
6. **Meters unchanged:** `can_use_ai` / wallet still compose with feature keys for AI actions.
---
## Implementation
| Piece | Path |
|-------|------|
| Helpers + route map | `apps/web/src/lib/plan-capabilities.ts` |
| Session store (fetch once) | `apps/web/src/lib/plan-capabilities.svelte.ts` |
| `canFeature` re-export | `apps/web/src/lib/plan-gates.ts` |
| Types | `CreditBalance.features` / `disabled_features` / `feature_etag` |
| Route guard | `PlanRouteGuard.svelte` in dashboard layout |
| Upgrade CTA | `PlanUpgradePanel.svelte` / `FeatureGate.svelte` |
| Nav filter | `Nav.svelte` primary + More |
| Command palette | `CommandPalette.svelte` |
| Settings tabs | `settings/+page.svelte` |
| Section actions | Products AI, Campaign AI, SEO AI, Brand AI apply, AI BYOK |
Hydration: layout `/api/auth/me``planCapabilities.hydrateFromCredits``planCapabilities.refresh()`.
---
## Gated routes
| Path | Feature key |
|------|-------------|
| `/dashboard` | `dashboard.overview` |
| `/products` | `catalog.products` |
| `/feeds` | `feeds.list` |
| `/feeds/[feedId]/mapping` | `feeds.mapping` |
| `/export-feeds` | `feeds.export_feeds` |
| `/files` | `feeds.uploads` |
| `/stores` | `stores.hub` |
| `/woocommerce` | `stores.woocommerce` |
| `/woocommerce?tab=reviews` | `marketing.reviews` |
| `/stores/shopify`, `/shopify` | `stores.shopify` |
| `/processing`, `/tasks` | `processing.monitor` |
| `/categories` | `catalog.categories` |
| `/categories/[id]/title-formula` | `catalog.categories.title_formula` |
| `/categories/[id]/description-formula` | `catalog.categories.description_formula` |
| `/attributes` | `catalog.attributes` |
| `/standard-fields` | `catalog.standard_fields` |
| `/structured-descriptions` | `catalog.structured_descriptions` |
| `/vector-categories` | `catalog.vector_categories` |
| `/campaigns`, `/campaigns/new`, `/campaigns/[id]` | `marketing.campaigns` |
| `/marketing/calendar` | `marketing.content_calendar` |
| `/brand` | `marketing.brand_kit` |
| `/seo` | `marketing.seo` |
| `/integrations/ai` | `integrations.ai` |
| `/integrations/email` | `integrations.email` |
| `/billing` | `billing.overview` |
| `/plans` | `billing.plans_compare` |
| `/settings` | `settings.profile` (+ tab keys) |
| `/settings?tab=api-keys` | `settings.api_keys` |
| `/support` | `support.center` |
| `/support/new` | `support.ticket_create` |
| `/support/[ticketId]` | `support.ticket_thread` |
Deep-link to a disabled route → in-place upgrade panel (not hard redirect).
---
## Section gates (shared pages)
| Surface | Keys |
|---------|------|
| Settings tabs | `settings.profile` / `company` / `alerts` / `api_keys` / `team` |
| Products AI actions | `catalog.products.process_ai_*` + `capability.ai_processing` |
| Campaign AI / send | `marketing.campaigns.generate_ai` + `capability.campaign_ai` |
| SEO AI rewrite | `marketing.seo.ai_rewrite` + `capability.seo_ai_rewrite` |
| Brand AI apply banner | `marketing.brand_ai_apply` + `capability.brand_ai_apply` |
| AI BYOK modes | `integrations.ai.byok` + `capability.byok` |
---
## Verification
- `npm run check` in `apps/web` (svelte-check)
- Manual: Free user — API keys tab hidden/upgraded; AI process locked; billing/settings reachable when capabilities 404