AstroBaaS

Catalogue & product data

Product Status (Draft/Published)

Free — GPL coresize Splanned, not built

Generated from docs/plan/core/product-status/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Core feature for controlling product visibility through a status field (draft, published, archived). Merchants can prepare products before launch without publishing them to the storefront, schedule publication, and retire old products. This is foundational lifecycle management for every product.

The problem

Merchants can’t prep products before launch; there’s no draft state, so they must publish incomplete products.

What it does

  • Status enum: ‘draft’ (not visible to customers), ‘published’ (visible), ‘archived’ (hidden, but data preserved)
  • Draft products: only visible in admin; not returned by storefront product API unless explicitly queried (admin mode)
  • Published products: visible on storefront (default GET /products returns only published unless filter says otherwise)
  • Archived products: hidden from storefront like draft; merchants use to retire old products without deleting
  • Status transition: admin can change status from draft → published → archived (any order allowed)
  • Scheduled publication: optional publish_scheduled_at field; product auto-publishes at that time (not enforced by core; merchant manually checks or uses scheduler plugin)
  • Filter by status: GET /products?status=draft (admin only), GET /products?status=published (public, default)
  • Status indicator: admin list view shows status badge per product (green=published, yellow=draft, gray=archived)
  • Bulk status change: bulk edit to change status across many products
  • SEO consideration: drafted products should not be indexed by search engines (X-Robots-Tag: noindex header or noindex in meta)

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Automatic scheduling (product goes live at midnight) — merchant must check or use scheduler plugin
  • Status approval workflows (draft → pending review → approved → published) — that’s workflow/multi-user collaboration tier
  • Region-specific visibility (published in US, draft in EU) — single global status only
  • Time-based expiration (product auto-archives after 30 days) — manual or plugin-driven only

Data model

Product.status field (enum: ‘draft’ | ‘published’ | ‘archived’, default ‘draft’). Product.publish_scheduled_at optional field (timestamp, nullable). No new entities.

API

  • PUT /products/:id — update status {status: ‘published’} or {publish_scheduled_at: ‘2026-10-01T00:00:00Z’}
  • GET /products — list products, default returns only status=‘published’; ?status=draft returns draft (admin only); ?status=archived returns archived (admin only)
  • GET /products/:id — retrieve any product by ID (status is not a gate; storefront enforces via API query params)
  • PATCH /products/bulk — bulk update status {product_ids: […], updates: {status: ‘published’}}

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Admin list view shows status column with badges (green=published, yellow=draft, gray=archived). Admin product edit form has status selector (radio or dropdown: draft/published/archived) and optional scheduled publication date picker. Filters at top of product list (Status: All / Draft / Published / Archived).

The seam — why this is core

Core owns the status field and API filtering. Storefront (Next.js) enforces by querying only status=‘published’ products. Core does not gate; storefront decides what to show.

Core owns the interface + honest status controller; product lifecycle is infrastructure, not a support commitment or credential.

Dependencies

  • product-management (core; products have status)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • A product created with status=‘draft’ does not appear in GET /products (storefront default query).
  • GET /products?status=draft (admin auth) returns only draft products.
  • PUT /products/:id with {status: ‘published’} makes product visible to storefront (appears in GET /products).
  • An archived product (status=‘archived’) does not appear in default GET /products or GET /products?status=published.
  • Filtering by status=‘draft’ in admin list shows only draft products; all other products are filtered out.
  • Bulk edit with {status: ‘published’} changes status for 50 selected products; they appear on storefront immediately.
  • Product with publish_scheduled_at=‘2026-10-01’ remains draft until merchant manually publishes (scheduled publication requires scheduler plugin).
  • Storefront sets X-Robots-Tag: noindex on drafted product pages (if accessed directly via ID link).

Risks

If storefront queries do not filter by status, draft products appear on the shop. If a merchant changes status to ‘published’ without testing, incomplete products appear to customers. If scheduled_at is not honored, merchants are surprised when products don’t auto-publish.

Commercial context

Suggested pricefree (core)
Rival anchorShopify: included; Magento: included

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.