Operations & platform
Dynamic Product Badges
Indicative price, not an offer: EUR 100-200/year or EUR 250 one-off
Generated from docs/plan/paid/dynamic-product-badges/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Static product images don’t convey promotions or inventory status. This feature adds dynamic badges (New, Sale, Low Stock, Bestseller) with rule-based activation, displayed as image overlays or in product cards.
The problem
My product cards are static. I want to show ‘New’, ‘On Sale’, ‘Last 3 left’ badges automatically based on product data, without editing images manually.
What it does
- Badge types: predefined (New, Sale, Low Stock, Bestseller, Popular) and custom text badges
- Badge rules: trigger badge based on product state (sale_price < regular_price, stock < 10, created_at < 30 days ago, sold > 100 in last 30 days)
- Visual config: badge color, position (top-left, top-right, center), text/label, optional icon
- Admin UI: toggle each badge type on/off, configure rules and visuals
- Storefront: badges render as image overlays on product cards and detail pages
- API: badge list for product (returns active badges for that product)
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.
- Animated badges (GIF/video overlays) — static images only
- Customer-visible badge customization — admin controls visuals; customer sees result
- Badge analytics (which badges drive clicks) — display only, no interaction tracking
Data model
New entity: Badge {id, name, type, enabled, rules (JSON: field + operator + value), visual_config (color, position, label), created_at}. Example: {type: ‘sale’, rules: {sale_price: ’<’, regular_price}, label: ‘Sale’, color: ‘red’, position: ‘top-right’}.
API
- GET /api/products/:id/badges — return active badges for product (public)
- POST /api/badges — create badge (admin only)
- PATCH /api/badges/:id — update badge config (admin only)
- DELETE /api/badges/:id — delete badge (admin only)
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Badge manager: list of badge types (New, Sale, Low Stock, Bestseller). Toggle on/off. Edit: configure rules (if sale_price < regular_price, show Sale badge), visual (color: red, position: top-left, label: ‘Sale’), preview badge on sample product card.
The seam — why this is paid
Core owns the badge model and storefront rendering logic. Paid layer owns advanced analytics and targeting (show badge only for specific customer segments, A/B test badge text).
Paid marketing feature. Core provides basic product info; dynamic badges with rule-based automation is a paid marketing tool.
Dependencies
- Product entity with sale_price, created_at, stock fields (already shipped)
- Sales/order history for bestseller calculation
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.
- Badge ‘Sale’ is created with rule: sale_price < regular_price
- Product with regular_price €50, sale_price €40 gets the Sale badge automatically
- Badge is rendered on product card as image overlay (position: top-right, color: red, text: ‘Sale’)
- Badge ‘Low Stock’ is created with rule: stock < 10
- Product with stock=5 gets Low Stock badge; stock=10 does not
- Badge is disabled; all products stop showing that badge (no cache invalidation needed; check on render)
Risks
Badge rules must be evaluated efficiently; querying ‘sold > 100 in last 30 days’ on every product list query could be slow (index on sale timestamps or pre-compute bestseller flag). Rule evaluation can be wrong if product data is stale. Badge clutter (too many badges) reduces impact; restrict to 2–3 simultaneous badges per product.
Commercial context
| Suggested price | EUR 100-200/year or EUR 250 one-off |
| Rival anchor | Mirasvit Advanced Product Labels |
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.