Files
Reklamator/specs/002-product-list/data-model.md
T
gurixandClaude 0f71ba969f Add implementation plan for product selection landing page (Feature 002)
Completed planning phases 0 and 1 for simple landing page feature.

## Plan Overview:

**Approach**: Minimal addition to existing Flask app - reuse Product model,
add one route, one template. No new dependencies or complexity.

**Constitution Check**:  All 5 principles satisfied
- Specification-first development (spec.md complete)
- Test-first discipline (TDD workflow defined)
- Independent user stories (3 stories, all independently testable)
- Simplicity (reuses existing Flask/Jinja2/Product architecture)
- Documentation as code (all artifacts in specs/002-product-list/)

## Artifacts Created:

### Phase 0: Research (research.md)
- Reuses infrastructure from feature 001 (Flask, Jinja2, file storage)
- Single new decision: Product.load_active() method for filtering/sorting
- Performance analysis: <100ms for 100 products (well under 1s target)

### Phase 1: Design & Contracts
- **data-model.md**: Documents Product model extension (load_active method)
- **contracts/landing-page.yaml**: OpenAPI contract for GET / route
- **quickstart.md**: Developer implementation guide with:
  - Step-by-step implementation checklist
  - Code snippets for route, template, tests
  - TDD workflow (write tests → verify fail → implement → pass)
  - Manual verification checklist

### Agent Context
- Updated CLAUDE.md with feature technologies (no new tech added)

## Implementation Summary:

**New Files** (to be created):
- app/routes/landing.py - Landing page route handler
- app/templates/landing/index.html - Product list template
- tests/contract/test_landing_routes.py - Contract tests (6 scenarios)
- tests/integration/test_landing_flow.py - User journey test

**Modified Files**:
- app/models/product.py - Add load_active() class method
- app/__init__.py - Register landing blueprint

## Key Technical Decisions:

1. **Filtering**: status=='active' AND submission_url_slug exists
2. **Sorting**: Alphabetical by name (case-insensitive), then product_id
3. **Empty State**: "No products are currently accepting feedback" message
4. **XSS Prevention**: Jinja2 auto-escaping (no manual escaping needed)
5. **Performance**: File I/O sufficient (<1s for 100 products, no caching)

## Next Steps:

1. Run /speckit.tasks to generate tasks.md
2. Run /speckit.implement to execute TDD workflow
3. Verify all tests pass
4. Manual verification checklist
5. Create pull request

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 14:26:47 +02:00

4.3 KiB

Data Model: Product Selection Landing Page

Feature: 002-product-list | Date: 2025-10-17

Overview

This feature reuses the existing Product entity from feature 001-build-an-application with a minor extension. No new entities are created.

Existing Entity: Product

Source: app/models/product.py (from feature 001)

Attributes (relevant to this feature):

Attribute Type Required Description
product_id str Yes Unique identifier (e.g., "001-acme-app")
name str Yes Display name for the product
submission_url_slug str Yes URL segment for submission form
status str Yes "active" or "archived"
description str No Brief product description (optional)

Storage: File-based YAML at data/products/{product-id}/config.yaml

Example:

product_id: "001-acme-app"
name: "Acme Application"
submission_url_slug: "acme-app"
status: "active"
description: "Enterprise resource planning system"

Model Extension

New Class Method: load_active()

Purpose: Load and return only active products with valid submission URLs, sorted for display.

Signature:

@classmethod
def load_active(cls) -> list[Product]:
    """Load all active products, sorted alphabetically by name then product_id."""

Returns: List of Product instances where:

  • status == 'active'
  • submission_url_slug is present and valid
  • Sorted by: (name.lower(), product_id)

Implementation Location: app/models/product.py

Usage Example:

from app.models.product import Product

# In route handler
products = Product.load_active()
# Returns sorted list of active products ready for display

Data Flow

Landing Page Load Sequence

  1. Request: User navigates to /
  2. Route Handler: app/routes/landing.py
    • Calls Product.load_active()
  3. Data Loading: Product model
    • Reads all data/products/*/config.yaml files
    • Filters: status == 'active' AND submission_url_slug exists
    • Sorts: By (name.lower(), product_id)
  4. Response: Render template with product list
    • Pass products to templates/landing/index.html
    • Template loops over products, displays name/description/link

Diagram

User → GET / → landing.py → Product.load_active() → [Product, Product, ...]
                                  ↓
                            Templates (Jinja2) → HTML Response → User

Validation Rules

Product Visibility (for landing page)

A product is visible on the landing page if and only if:

  1. status == 'active' (FR-003)
  2. submission_url_slug is not None/empty (FR-015)

Products failing either condition are excluded from the list.

Sorting Rules (FR-009)

Products are sorted by:

  1. Primary: name (case-insensitive alphabetical)
  2. Secondary: product_id (alphabetical, for ties)

Examples:

  • Input: [{"name": "Zebra", "product_id": "001"}, {"name": "Apple", "product_id": "002"}]

  • Output: [{"name": "Apple", ...}, {"name": "Zebra", ...}]

  • Input: [{"name": "App", "product_id": "002"}, {"name": "App", "product_id": "001"}]

  • Output: [{"name": "App", "product_id": "001"}, {"name": "App", "product_id": "002"}]


No New Entities

This feature introduces no new entities. All data structures are reused from feature 001:

  • Product entity (extended with load_active() method only)
  • File-based YAML storage (unchanged)
  • No database tables, no new data files

Testing Considerations

Test Data Requirements

For comprehensive testing, create products with:

  • Various statuses: "active", "archived"
  • With/without descriptions
  • With/without valid submission_url_slug
  • Duplicate names (to test secondary sort)
  • Various name cases ("Apple", "apple", "APPLE")

Expected Behaviors

Scenario Expected Result
Product with status: active Included in list
Product with status: archived Excluded from list
Product with missing submission_url_slug Excluded from list
Products with same name Sorted by product_id
Empty product directory Returns empty list

Reference Tests: See tests/contract/test_landing_routes.py for data model validation tests.