225 lines
7.7 KiB
Markdown
225 lines
7.7 KiB
Markdown
# Implementation Plan: Product Selection Landing Page
|
|||
|
|
|
||
|
|
**Branch**: `002-product-list` | **Date**: 2025-10-17 | **Spec**: [spec.md](./spec.md)
|
||
|
|
|
||
|
|
## Summary
|
||
|
|
|
||
|
|
Add a landing page at root URL (`/`) that lists all active products, enabling visitors to discover and select products for feedback submission. This removes the barrier of requiring users to know direct product URLs.
|
||
|
|
|
||
|
|
**Technical approach**: Add new Flask route, reuse existing Product model, create simple HTML template with server-side rendering. No new dependencies needed.
|
||
|
|
|
||
|
|
## Technical Context
|
||
|
|
|
||
|
|
**Language/Version**: Python 3.11+
|
||
|
|
**Primary Dependencies**: Flask 3.0+, Jinja2 (built-in)
|
||
|
|
**Storage**: File-based (data/products/*/config.yaml - existing)
|
||
|
|
**Testing**: pytest + pytest-flask (existing)
|
||
|
|
**Target Platform**: Linux server (existing deployment)
|
||
|
|
**Project Type**: Web application (Flask backend with server-side rendering)
|
||
|
|
**Performance Goals**: <1 second page load for up to 100 products
|
||
|
|
**Constraints**: Server-side rendering only (no JavaScript), minimal CSS (no frameworks)
|
||
|
|
**Scale/Scope**: Simple single-page addition to existing Flask app
|
||
|
|
|
||
|
|
## Constitution Check
|
||
|
|
|
||
|
|
*GATE: Must pass before Phase 0 research.*
|
||
|
|
|
||
|
|
### Principle I: Specification-First Development
|
||
|
|
✅ **PASS** - Complete specification exists at spec.md with prioritized user stories, functional requirements, and success criteria.
|
||
|
|
|
||
|
|
### Principle II: Test-First Discipline
|
||
|
|
✅ **PASS** - Implementation will follow TDD: contract tests → integration tests → implementation.
|
||
|
|
|
||
|
|
### Principle III: Independent User Stories
|
||
|
|
✅ **PASS** - All 3 user stories (P1: Browse/Select, P2: Status visibility, P3: Direct nav) are independently testable and deliverable.
|
||
|
|
|
||
|
|
### Principle IV: Simplicity & Justification
|
||
|
|
✅ **PASS** - Feature reuses existing architecture (Flask routes, Product model, Jinja2 templates). No new abstractions, dependencies, or complexity added.
|
||
|
|
|
||
|
|
### Principle V: Documentation as Code
|
||
|
|
✅ **PASS** - Specification, plan, and implementation artifacts maintained in specs/002-product-list/ with version control.
|
||
|
|
|
||
|
|
**Constitution Status**: ✅ All principles satisfied. No violations to justify.
|
||
|
|
|
||
|
|
## Project Structure
|
||
|
|
|
||
|
|
### Documentation (this feature)
|
||
|
|
|
||
|
|
```
|
||
|
|
specs/002-product-list/
|
||
|
|
├── spec.md # Feature specification (complete)
|
||
|
|
├── plan.md # This file
|
||
|
|
├── research.md # Phase 0 - Technical research (minimal - reuses 001)
|
||
|
|
├── data-model.md # Phase 1 - Data model (reference to existing Product)
|
||
|
|
├── contracts/ # Phase 1 - API contract (GET / route)
|
||
|
|
│ └── landing-page.yaml
|
||
|
|
├── quickstart.md # Phase 1 - Developer quickstart
|
||
|
|
└── tasks.md # Phase 2 - Task breakdown (/speckit.tasks)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Source Code (repository root)
|
||
|
|
|
||
|
|
```
|
||
|
|
app/
|
||
|
|
├── models/
|
||
|
|
│ └── product.py # Existing - no changes needed
|
||
|
|
├── routes/
|
||
|
|
│ └── landing.py # NEW - landing page route
|
||
|
|
└── templates/
|
||
|
|
└── landing/
|
||
|
|
└── index.html # NEW - product list template
|
||
|
|
|
||
|
|
tests/
|
||
|
|
├── contract/
|
||
|
|
│ └── test_landing_routes.py # NEW - contract tests for GET /
|
||
|
|
└── integration/
|
||
|
|
└── test_landing_flow.py # NEW - end-to-end user journey tests
|
||
|
|
```
|
||
|
|
|
||
|
|
**Structure Decision**: Reuse existing Flask application structure. Landing page is a simple addition: one new route file, one new template, and corresponding tests. Follows established patterns from feature 001.
|
||
|
|
|
||
|
|
## Complexity Tracking
|
||
|
|
|
||
|
|
*No violations - table not needed.*
|
||
|
|
|
||
|
|
All complexity requirements from Constitution Principle IV are satisfied:
|
||
|
|
- No additional abstraction layers
|
||
|
|
- No new dependencies
|
||
|
|
- No new design patterns
|
||
|
|
- Reuses existing Flask/Jinja2/Product architecture
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 0: Research
|
||
|
|
|
||
|
|
### Research Scope
|
||
|
|
|
||
|
|
Since this feature builds on existing infrastructure from 001-build-an-application, minimal research is needed. Key questions already answered:
|
||
|
|
|
||
|
|
1. **Product data access**: Resolved in 001 - Product.load_all() method exists
|
||
|
|
2. **Template rendering**: Resolved in 001 - Jinja2 with server-side rendering
|
||
|
|
3. **Route patterns**: Resolved in 001 - Flask blueprints for organization
|
||
|
|
4. **Sorting implementation**: Python built-in sorted() with key function
|
||
|
|
|
||
|
|
### New Technical Decisions
|
||
|
|
|
||
|
|
Only one new decision needed for this feature:
|
||
|
|
|
||
|
|
**Product List Retrieval & Sorting**
|
||
|
|
- Decision: Extend existing Product model with `load_active()` class method
|
||
|
|
- Rationale: Centralizes "active products only" logic, enables reuse
|
||
|
|
- Sorting: Python's `sorted()` with `key=lambda p: (p.name.lower(), p.product_id)`
|
||
|
|
- Performance: File I/O for 100 products ~10-50ms (acceptable for <1s target)
|
||
|
|
|
||
|
|
**Output**: research.md (minimal - references 001, documents sorting decision)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 1: Design & Contracts
|
||
|
|
|
||
|
|
### Data Model
|
||
|
|
|
||
|
|
**Entities**: Reuse existing Product model from 001-build-an-application
|
||
|
|
|
||
|
|
**Extension needed**:
|
||
|
|
```python
|
||
|
|
# app/models/product.py - add class method
|
||
|
|
@classmethod
|
||
|
|
def load_active(cls):
|
||
|
|
"""Load all active products, sorted alphabetically by name then product_id"""
|
||
|
|
all_products = cls.load_all()
|
||
|
|
active = [p for p in all_products if p.status == 'active' and p.submission_url_slug]
|
||
|
|
return sorted(active, key=lambda p: (p.name.lower(), p.product_id))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Output**: data-model.md (references existing Product entity, documents extension)
|
||
|
|
|
||
|
|
### API Contracts
|
||
|
|
|
||
|
|
**New Route**: `GET /`
|
||
|
|
|
||
|
|
**Contract**:
|
||
|
|
```yaml
|
||
|
|
# contracts/landing-page.yaml
|
||
|
|
paths:
|
||
|
|
/:
|
||
|
|
get:
|
||
|
|
summary: Landing page - list active products
|
||
|
|
operationId: getLandingPage
|
||
|
|
responses:
|
||
|
|
'200':
|
||
|
|
description: HTML page with product list
|
||
|
|
content:
|
||
|
|
text/html:
|
||
|
|
schema:
|
||
|
|
type: string
|
||
|
|
examples:
|
||
|
|
with_products:
|
||
|
|
summary: Multiple active products
|
||
|
|
value: |
|
||
|
|
<html>
|
||
|
|
<h1>Select a Product</h1>
|
||
|
|
<ul>
|
||
|
|
<li><a href="/submit/product-a">Product A</a> - Description</li>
|
||
|
|
<li><a href="/submit/product-b">Product B</a></li>
|
||
|
|
</ul>
|
||
|
|
</html>
|
||
|
|
no_products:
|
||
|
|
summary: No active products
|
||
|
|
value: |
|
||
|
|
<html>
|
||
|
|
<p>No products are currently accepting feedback.</p>
|
||
|
|
</html>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Output**: contracts/landing-page.yaml
|
||
|
|
|
||
|
|
### Developer Quickstart
|
||
|
|
|
||
|
|
Key implementation points for developers:
|
||
|
|
|
||
|
|
1. **Route**: app/routes/landing.py with `@app.route('/')`
|
||
|
|
2. **Template**: app/templates/landing/index.html - loop over products
|
||
|
|
3. **XSS Prevention**: Use Jinja2 auto-escaping for product names/descriptions
|
||
|
|
4. **Empty State**: Check `if products` to show appropriate message
|
||
|
|
5. **Logging**: Log landing page access with product count
|
||
|
|
|
||
|
|
**Output**: quickstart.md
|
||
|
|
|
||
|
|
### Agent Context Update
|
||
|
|
|
||
|
|
Run: `.specify/scripts/bash/update-agent-context.sh claude`
|
||
|
|
|
||
|
|
**Expected update**: No new technologies added (reuses Flask, Jinja2, Python 3.11+)
|
||
|
|
|
||
|
|
**Output**: Updated .claude.md or equivalent agent context file
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 2: Task Generation
|
||
|
|
|
||
|
|
**Not executed by /speckit.plan** - run `/speckit.tasks` next.
|
||
|
|
|
||
|
|
Expected task structure:
|
||
|
|
1. Contract tests for GET / (various scenarios)
|
||
|
|
2. Integration test for user journey
|
||
|
|
3. Implement Product.load_active() method
|
||
|
|
4. Implement landing route
|
||
|
|
5. Create landing template
|
||
|
|
6. Add logging
|
||
|
|
7. Manual verification
|
||
|
|
|
||
|
|
**Output**: tasks.md (generated by /speckit.tasks command)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Next Steps
|
||
|
|
|
||
|
|
1. ✅ Phase 0 complete: Generate research.md
|
||
|
|
2. ✅ Phase 1 complete: Generate data-model.md, contracts/, quickstart.md
|
||
|
|
3. ⏭️ Run `/speckit.tasks` to generate tasks.md
|
||
|
|
4. ⏭️ Run `/speckit.implement` to execute tasks
|
||
|
|
|
||
|
|
**Branch**: 002-product-list
|
||
|
|
**Plan**: /home/markus/workspace/reklamator/specs/002-product-list/plan.md
|