Files
marketplaces/docs/superpowers/specs/2026-08-22-frontend-default-bootstrap-design.md
sdarbinyan c2a56571af
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Deploy Frontend / deploy (push) Has been cancelled
feat(bootstrap): fall back to built-in placeholder when marketplace unpublished
Adds published: boolean to the bootstrap wire contract. ConfigService
swaps to a new DEFAULT_BOOTSTRAP constant (all feature flags on, generic
branding/theme/pages) whenever the backend reports published: false, so
an unpublished marketplace renders a working demo instead of a blank or
broken page. Missing published field stays backward compatible (treated
as true). Documents the brand bootstrap wire shape for backend/ops use.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 22:02:42 +04:00

4.5 KiB

Frontend default bootstrap (unpublished-marketplace placeholder)

Date: 2026-08-22 Status: approved (decided by project owner in-session, no further review requested)

Problem

Production /bootstrap has no fallback today. A marketplace with no published revision either 404s or returns whatever partial row the backend has — frontend has nothing sane to render. Need a placeholder that shows immediately for any brand before its first publish, with every feature switched on so it doubles as a full product demo.

Decision

Whole-object fallback, decided client-side from one explicit backend signal.

1. Backend contract change

Add one required top-level field to the /bootstrap response:

interface BootstrapConfig {
  schemaVersion: string;
  generatedAt: string;
  published: boolean;   // NEW — false until MarketplaceRevision.status = 'published'
  tenant: TenantConfig;
  ...
}

published mirrors whether the marketplace has a publishedRevision (see MarketplaceRevision.status in BACKEND-INTEGRATION.md §11) — not lifecycleState directly, since a marketplace can be live while a new draft revision sits unpublished. Backend still returns full real tenant/branding/etc when published: true; when false it may return anything or the last-known real data — frontend ignores every other field in that case (see §2).

2. Frontend: whole-object swap

New constant, colocated with the other DEFAULT_* config constants:

// src/app/shared/models/config/default-bootstrap.const.ts
export const DEFAULT_BOOTSTRAP: BootstrapConfig = {
  schemaVersion: '1.0.0',
  generatedAt: new Date(0).toISOString(),
  published: false,
  tenant: { /* generic placeholder — brandName 'Marketplace', no real domain */ },
  branding: { brandName: 'Marketplace', ... },
  theme: { /* the existing default-light palette from bootstrap.json */ },
  featureFlags: { wishlist: true, compare: true, reviews: true, blog: true, chat: true,
                   analytics: true, notifications: true, coupons: true, loyalty: true,
                   giftCards: true, invoices: true },          // everything ON
  features: DEFAULT_MARKETPLACE_FEATURES_CONFIG,                // reused, already all-true
  header: DEFAULT_HEADER_CONFIG,                                 // reused
  modules: DEFAULT_PLATFORM_MODULES_CONFIG,                      // reused (sellerManagement off — real module gate, not a feature flag)
  navigation: { /* hardcoded generic nav */ },
  pages: [ /* hardcoded generic home page, hero+categories+featured, same shape as bootstrap.json */ ],
  staticPages: { /* generic about/privacy/terms/contacts */ },
  ...
};

ConfigService.loadBootstrap() gains one check after the provider emits:

tap(config => {
  const resolved = config.published ? config : DEFAULT_BOOTSTRAP;
  this.bootstrapSnapshot = resolved;
  this.revisionState.update(v => v + 1);
}),

No change to ApiBootstrapProvider, MockBootstrapProvider, or the ConfigProvider interface — the swap is a ConfigService-only concern, so it applies uniformly regardless of provider mode.

3. Error handling

  • published missing/undefined from an old backend response → treat as true (backwards compatible: existing marketplaces that never send the field keep behaving exactly as today, same pattern already used for modules/ADR-011).
  • Actual HTTP failure (network error, 5xx) stays a hard error — catchError behavior unchanged, no fallback. Fallback is only for the known "not published yet" case, not for "backend unreachable." (Matches your earlier answer: explicit signal, not HTTP-status-driven.)

4. Testing

  • ConfigService unit test: published: false response → snapshot equals DEFAULT_BOOTSTRAP.
  • ConfigService unit test: published: true → snapshot equals the real response, untouched.
  • ConfigService unit test: published absent → snapshot equals the real response (back-compat).
  • DEFAULT_BOOTSTRAP itself: a schema-shape test (it must satisfy BootstrapConfig — TypeScript already enforces this at compile time, so this is really just "does it compile").
  • One E2E smoke: a marketplace with no revision renders the placeholder home page without erroring.

Out of scope (explicitly deferred)

  • Field-level merge (real brand name + placeholder theme) — rejected in favor of simpler whole-object swap.
  • Any admin-panel UI for previewing/editing the default — not asked for.
  • Backend implementation of published resolution logic — backend team's own call once they build the service; this spec only fixes the wire contract.