Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
4.2 KiB
4.2 KiB
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
- Primary source:
credits.featuresonGET /api/auth/me(and/api/billing/credits), per contract §8. - Secondary fetch once:
GET /api/billing/capabilitiesfrom layout after/me(full registry +sections+ etag). Safe if 404/unavailable. - 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.
- Fail-open for core Free-loop + account/billing/support keys (
- When features map is present: enforce map + parent-prefix deny (disabled
catalog.productshides children). Missing key → false. - Admin shell (
/admin/*) is out of scope. - 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 checkinapps/web(svelte-check)- Manual: Free user — API keys tab hidden/upgraded; AI process locked; billing/settings reachable when capabilities 404