From 0f71ba969ff23cd6c172ce75a6a08f37be9f00e3 Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Fri, 17 Oct 2025 14:26:47 +0200 Subject: [PATCH] Add implementation plan for product selection landing page (Feature 002) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CLAUDE.md | 5 +- .../contracts/landing-page.yaml | 101 +++++++ specs/002-product-list/data-model.md | 147 ++++++++++ specs/002-product-list/plan.md | 224 ++++++++++++++ specs/002-product-list/quickstart.md | 276 ++++++++++++++++++ specs/002-product-list/research.md | 93 ++++++ specs/002-product-list/spec.md | 35 ++- 7 files changed, 868 insertions(+), 13 deletions(-) create mode 100644 specs/002-product-list/contracts/landing-page.yaml create mode 100644 specs/002-product-list/data-model.md create mode 100644 specs/002-product-list/plan.md create mode 100644 specs/002-product-list/quickstart.md create mode 100644 specs/002-product-list/research.md diff --git a/CLAUDE.md b/CLAUDE.md index 60829bc..99c5cbb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,6 +4,8 @@ Auto-generated from all feature plans. Last updated: 2025-10-15 ## Active Technologies - Python 3.11+ + Flask (web framework), no CSS frameworks, no JavaScript libraries (001-build-an-application) +- Python 3.11+ + Flask 3.0+, Jinja2 (built-in) (002-product-list) +- File-based (data/products/*/config.yaml - existing) (002-product-list) ## Project Structure ``` @@ -19,7 +21,8 @@ cd src [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLO Python 3.11+: Follow standard conventions ## Recent Changes +- 002-product-list: Added Python 3.11+ + Flask 3.0+, Jinja2 (built-in) - 001-build-an-application: Added Python 3.11+ + Flask (web framework), no CSS frameworks, no JavaScript libraries - \ No newline at end of file + diff --git a/specs/002-product-list/contracts/landing-page.yaml b/specs/002-product-list/contracts/landing-page.yaml new file mode 100644 index 0000000..b92e40d --- /dev/null +++ b/specs/002-product-list/contracts/landing-page.yaml @@ -0,0 +1,101 @@ +openapi: 3.0.3 +info: + title: Reklamator Landing Page API + version: 1.0.0 + description: Product selection landing page contract for feature 002-product-list + +paths: + /: + get: + summary: Landing page - list active products + description: | + Display a landing page showing all active products available for feedback submission. + Products are sorted alphabetically by name (case-insensitive), with product_id as tiebreaker. + operationId: getLandingPage + tags: + - Landing Page + responses: + '200': + description: HTML page with product list or empty state message + content: + text/html: + schema: + type: string + description: Server-rendered HTML page + examples: + with_products: + summary: Multiple active products displayed + value: | + + + Select a Product + +

Select a Product for Feedback

+ + + + + no_products: + summary: No active products (empty state) + value: | + + + Select a Product + +

Select a Product for Feedback

+

No products are currently accepting feedback. Please check back later.

+ + + + product_without_description: + summary: Product without description (no placeholder text) + value: | + + + Select a Product + +

Select a Product for Feedback

+ + + + +components: + schemas: + # No request/response schemas needed (HTML rendering) + # Product data comes from file-based storage, not API request body + +# Contract Test Scenarios +# These scenarios should be covered in tests/contract/test_landing_routes.py: +# +# 1. GET / with active products → 200 OK with product list HTML +# 2. GET / with no active products → 200 OK with empty state message +# 3. GET / with mixed active/archived → 200 OK showing only active +# 4. GET / verifies alphabetical sorting (name, then product_id) +# 5. GET / excludes products with missing submission_url_slug +# 6. GET / properly escapes product names (XSS prevention) +# 7. GET / displays descriptions when present +# 8. GET / omits description placeholder when missing +# 9. GET / accessible to anonymous users +# 10. GET / accessible to authenticated users (same behavior) + +# Success Criteria Validation: +# - SC-001: Page contains clickable links to /submit/{slug} +# - SC-002: Response time <1s for up to 100 products +# - SC-004: Existing /submit/{slug} routes still functional (backwards compatibility) +# - SC-005: Empty state message displayed when no active products +# - SC-006: HTML escaping prevents XSS (test with " + # Assert: HTML is escaped, script not executed + pass + +@pytest.mark.contract +def test_get_landing_page_missing_slug(client, temp_data_dir): + """T306: GET / excludes products with missing submission_url_slug""" + # Setup: Product with submission_url_slug = None + # Assert: Product not shown in list + pass +``` + +--- + +### 5. Write Integration Tests + +**File**: `tests/integration/test_landing_flow.py` (new file) + +**User journey test**: + +```python +@pytest.mark.integration +def test_landing_to_submission_flow(client, temp_data_dir): + """T307: Complete flow - landing page → product selection → submission form""" + # Step 1: Visit landing page, see products + # Step 2: Click product link + # Step 3: Verify redirected to /submit/{slug} + pass +``` + +--- + +## Key Implementation Notes + +### XSS Prevention +- ✅ Jinja2 auto-escaping handles product names and descriptions +- ✅ No manual HTML escaping needed +- ✅ Test with `