fix(task_graph): fix JSON escaping + add T01 acceptance criteria (phone link, social icons) - quality gate MINOR 5
This commit is contained in:
+169
-51
@@ -9,7 +9,13 @@
|
|||||||
"title": "Frontend Foundation: Nuxt 3 + Design System + Layout + Meta",
|
"title": "Frontend Foundation: Nuxt 3 + Design System + Layout + Meta",
|
||||||
"description": "Nuxt 3 project setup with Tailwind CSS, design tokens (CSS custom properties for A0 Dark Theme + Orange accent), base layout (AppHeader sticky with mobile burger, AppFooter 4-column), HmsLogo SVG component, robots.txt (Disallow all), error page (404/500), and global meta tags (noindex/nofollow/noarchive/nosnippet, OpenGraph, JSON-LD LocalBusiness on home). Includes nuxt.config.ts with routeRules (SSG/SSR strategy), tailwind.config.ts with custom theme, app.vue, layouts/default.vue, server middleware for noindex headers, and all shared components: LoadingSkeleton, EmptyState, ErrorState, LegalContentPage, SpeakerIcon.",
|
"description": "Nuxt 3 project setup with Tailwind CSS, design tokens (CSS custom properties for A0 Dark Theme + Orange accent), base layout (AppHeader sticky with mobile burger, AppFooter 4-column), HmsLogo SVG component, robots.txt (Disallow all), error page (404/500), and global meta tags (noindex/nofollow/noarchive/nosnippet, OpenGraph, JSON-LD LocalBusiness on home). Includes nuxt.config.ts with routeRules (SSG/SSR strategy), tailwind.config.ts with custom theme, app.vue, layouts/default.vue, server middleware for noindex headers, and all shared components: LoadingSkeleton, EmptyState, ErrorState, LegalContentPage, SpeakerIcon.",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"requirement_ids": ["F-HMS-10", "F-HMS-11", "F-HMS-15", "F-HMS-18", "F-HMS-19"],
|
"requirement_ids": [
|
||||||
|
"F-HMS-10",
|
||||||
|
"F-HMS-11",
|
||||||
|
"F-HMS-15",
|
||||||
|
"F-HMS-18",
|
||||||
|
"F-HMS-19"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
@@ -44,15 +50,26 @@
|
|||||||
"Home page HTML contains JSON-LD script with '@type': 'LocalBusiness'",
|
"Home page HTML contains JSON-LD script with '@type': 'LocalBusiness'",
|
||||||
"404 page shows 'Seite nicht gefunden' with link to /",
|
"404 page shows 'Seite nicht gefunden' with link to /",
|
||||||
"CSS variables --bg, --panel, --color-accent defined in :root",
|
"CSS variables --bg, --panel, --color-accent defined in :root",
|
||||||
"HmsLogo renders SVG with orange (#EC6925) border"
|
"HmsLogo renders SVG with orange (#EC6925) border",
|
||||||
|
"AppHeader contains clickable phone link (tel:+491726264796)",
|
||||||
|
"AppHeader contains social icons (Facebook, Instagram) with correct links to social profiles"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "T02",
|
"id": "T02",
|
||||||
"title": "Static Pages: Home, Referenzen, Kontakt, Legal (Impressum/DSGVO/AGB)",
|
"title": "Static Pages: Home, Referenzen, Kontakt, Legal (Impressum/DSGVO/AGB)",
|
||||||
"description": "All static content pages: Home (hero with bg image + overlay, speaker grid 6 types, über uns section with stats, 8 service cards, mietkatalog CTA), Referenzen (filterable gallery with 9 images, 4 category chips, lightbox with prev/next/close), Kontakt (office/warehouse address cards, opening hours, 2 contact persons, contact form with Name/Email/Phone/Message/Privacy consent, client-side + submit to /api/contact), Impressum (§5 TMG legal info), Datenschutz (DSGVO privacy policy), AGB Vermietung (rental terms), NotFound catch-all page. All pages use LegalContentPage component for legal pages, ServiceCard for services, Lightbox component for gallery.",
|
"description": "All static content pages: Home (hero with bg image + overlay, speaker grid 6 types, über uns section with stats, 8 service cards, mietkatalog CTA), Referenzen (filterable gallery with 9 images, 4 category chips, lightbox with prev/next/close), Kontakt (office/warehouse address cards, opening hours, 2 contact persons, contact form with Name/Email/Phone/Message/Privacy consent, client-side + submit to /api/contact), Impressum (§5 TMG legal info), Datenschutz (DSGVO privacy policy), AGB Vermietung (rental terms), NotFound catch-all page. All pages use LegalContentPage component for legal pages, ServiceCard for services, Lightbox component for gallery.",
|
||||||
"dependencies": ["T01"],
|
"dependencies": [
|
||||||
"requirement_ids": ["F-HMS-01", "F-HMS-02", "F-HMS-03", "F-HMS-12", "F-HMS-13", "F-HMS-14"],
|
"T01"
|
||||||
|
],
|
||||||
|
"requirement_ids": [
|
||||||
|
"F-HMS-01",
|
||||||
|
"F-HMS-02",
|
||||||
|
"F-HMS-03",
|
||||||
|
"F-HMS-12",
|
||||||
|
"F-HMS-13",
|
||||||
|
"F-HMS-14"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
@@ -100,7 +117,10 @@
|
|||||||
"title": "Backend Core: FastAPI + DB Models + Equipment API + Redis Cache + Health",
|
"title": "Backend Core: FastAPI + DB Models + Equipment API + Redis Cache + Health",
|
||||||
"description": "FastAPI application setup with config (Pydantic Settings for env vars), SQLAlchemy async engine + session, all 5 DB models (equipment_cache, rental_requests, rental_request_items, contacts, admin_users, sync_log), Pydantic schemas, equipment router (GET /api/equipment with search/filter/sort/pagination, GET /api/equipment/{id}, GET /api/equipment/categories), health router (GET /api/health), Redis cache client (equipment list/detail/categories with 1h TTL), CORS middleware, main.py app assembly, Alembic migration setup, requirements.txt, and Dockerfile.",
|
"description": "FastAPI application setup with config (Pydantic Settings for env vars), SQLAlchemy async engine + session, all 5 DB models (equipment_cache, rental_requests, rental_request_items, contacts, admin_users, sync_log), Pydantic schemas, equipment router (GET /api/equipment with search/filter/sort/pagination, GET /api/equipment/{id}, GET /api/equipment/categories), health router (GET /api/health), Redis cache client (equipment list/detail/categories with 1h TTL), CORS middleware, main.py app assembly, Alembic migration setup, requirements.txt, and Dockerfile.",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"requirement_ids": ["F-HMS-04", "F-HMS-06"],
|
"requirement_ids": [
|
||||||
|
"F-HMS-04",
|
||||||
|
"F-HMS-06"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
@@ -113,7 +133,7 @@
|
|||||||
"curl -s 'http://localhost:8000/api/equipment?search=Lautsprecher' | python -m json.tool",
|
"curl -s 'http://localhost:8000/api/equipment?search=Lautsprecher' | python -m json.tool",
|
||||||
"curl -s 'http://localhost:8000/api/equipment/categories' | python -m json.tool",
|
"curl -s 'http://localhost:8000/api/equipment/categories' | python -m json.tool",
|
||||||
"curl -s http://localhost:8000/api/equipment/999999 | grep '404'"
|
"curl -s http://localhost:8000/api/equipment/999999 | grep '404'"
|
||||||
],
|
],
|
||||||
"expected_results": "All dependencies install. Pytest passes with >=80% coverage. FastAPI app starts. Health endpoint returns {status: ok, db: connected, redis: connected}. Equipment list returns paginated response with items, total, page, page_size, total_pages. Search filters by name. Categories endpoint returns string array. Non-existent equipment ID returns 404.",
|
"expected_results": "All dependencies install. Pytest passes with >=80% coverage. FastAPI app starts. Health endpoint returns {status: ok, db: connected, redis: connected}. Equipment list returns paginated response with items, total, page, page_size, total_pages. Search filters by name. Categories endpoint returns string array. Non-existent equipment ID returns 404.",
|
||||||
"test_files": [
|
"test_files": [
|
||||||
"backend/tests/test_equipment_router.py",
|
"backend/tests/test_equipment_router.py",
|
||||||
@@ -142,15 +162,21 @@
|
|||||||
"id": "T04",
|
"id": "T04",
|
||||||
"title": "Rentman Integration: Import Service + Request Service + Admin Auth & Sync",
|
"title": "Rentman Integration: Import Service + Request Service + Admin Auth & Sync",
|
||||||
"description": "Complete Rentman integration: (1) Import service (rentman_import.py) using RentmanAPI Python client, paginated GET /equipment (limit=100, iterate offset until data empty), upsert into equipment_cache, invalidate Redis cache, sync_log entries. (2) Request service (rentman_request.py) with POST /projectrequests + POST /projectrequests/{id}/projectrequestequipment, retry queue with exponential backoff (max 3 retries), admin notification on failure. (3) Admin auth: JWT login (auth_service.py, python-jose, HS256, HttpOnly cookie), admin router (POST /api/admin/login, GET /api/admin/me, POST /api/admin/sync, GET /api/admin/sync-status, GET /api/admin/sync-log). (4) Auth schemas, admin_user model seeding script, rate limiting for login endpoint.",
|
"description": "Complete Rentman integration: (1) Import service (rentman_import.py) using RentmanAPI Python client, paginated GET /equipment (limit=100, iterate offset until data empty), upsert into equipment_cache, invalidate Redis cache, sync_log entries. (2) Request service (rentman_request.py) with POST /projectrequests + POST /projectrequests/{id}/projectrequestequipment, retry queue with exponential backoff (max 3 retries), admin notification on failure. (3) Admin auth: JWT login (auth_service.py, python-jose, HS256, HttpOnly cookie), admin router (POST /api/admin/login, GET /api/admin/me, POST /api/admin/sync, GET /api/admin/sync-status, GET /api/admin/sync-log). (4) Auth schemas, admin_user model seeding script, rate limiting for login endpoint.",
|
||||||
"dependencies": ["T03"],
|
"dependencies": [
|
||||||
"requirement_ids": ["F-HMS-08", "F-HMS-09", "F-HMS-17"],
|
"T03"
|
||||||
|
],
|
||||||
|
"requirement_ids": [
|
||||||
|
"F-HMS-08",
|
||||||
|
"F-HMS-09",
|
||||||
|
"F-HMS-17"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
"commands": [
|
"commands": [
|
||||||
"cd backend && python -m pytest tests/test_rentman_import.py tests/test_rentman_request.py tests/test_admin_auth.py -v --cov=app.services --cov-report=term-missing",
|
"cd backend && python -m pytest tests/test_rentman_import.py tests/test_rentman_request.py tests/test_admin_auth.py -v --cov=app.services --cov-report=term-missing",
|
||||||
"curl -s -X POST http://localhost:8000/api/admin/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"wrong"}' | grep '401'",
|
"curl -s -X POST http://localhost:8000/api/admin/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"wrong\"}' | grep '401'",
|
||||||
"curl -s -X POST http://localhost:8000/api/admin/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"correct"}' | grep 'token'",
|
"curl -s -X POST http://localhost:8000/api/admin/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"correct\"}' | grep 'token'",
|
||||||
"curl -s http://localhost:8000/api/admin/sync-status | grep '401'",
|
"curl -s http://localhost:8000/api/admin/sync-status | grep '401'",
|
||||||
"curl -s -X POST http://localhost:8000/api/admin/sync -H 'Cookie: hms_admin_token=invalid' | grep '401'",
|
"curl -s -X POST http://localhost:8000/api/admin/sync -H 'Cookie: hms_admin_token=invalid' | grep '401'",
|
||||||
"cd backend && python -m pytest tests/test_rentman_import.py::test_paginated_import -v",
|
"cd backend && python -m pytest tests/test_rentman_import.py::test_paginated_import -v",
|
||||||
@@ -187,18 +213,24 @@
|
|||||||
"id": "T05",
|
"id": "T05",
|
||||||
"title": "Email Service + Contact & Rental Request Backend Endpoints",
|
"title": "Email Service + Contact & Rental Request Backend Endpoints",
|
||||||
"description": "Complete backend for user-facing forms: (1) Email service (email_service.py) with aiosmtplib, HTML+text templates for contact form email (to info@hms-licht-ton.de, Reply-To sender) and rental confirmation email (to customer, with reference_number + item list). SMTP via env vars. Retry queue on failure. (2) Contact router (POST /api/contact) with Pydantic validation (name*, email*, phone, message*, privacy_consent* must be True), rate limiting (5/min per IP), saves to contacts table, sends email. (3) Rental request router (POST /api/rental-requests) with full validation (event data, contact data, items array), generates reference_number (HMS-YYYY-NNNNN), saves to rental_requests + rental_request_items, calls rentman_request service, sends confirmation email, returns 201 with reference_number. (4) Rate limiting for both endpoints.",
|
"description": "Complete backend for user-facing forms: (1) Email service (email_service.py) with aiosmtplib, HTML+text templates for contact form email (to info@hms-licht-ton.de, Reply-To sender) and rental confirmation email (to customer, with reference_number + item list). SMTP via env vars. Retry queue on failure. (2) Contact router (POST /api/contact) with Pydantic validation (name*, email*, phone, message*, privacy_consent* must be True), rate limiting (5/min per IP), saves to contacts table, sends email. (3) Rental request router (POST /api/rental-requests) with full validation (event data, contact data, items array), generates reference_number (HMS-YYYY-NNNNN), saves to rental_requests + rental_request_items, calls rentman_request service, sends confirmation email, returns 201 with reference_number. (4) Rate limiting for both endpoints.",
|
||||||
"dependencies": ["T03", "T04"],
|
"dependencies": [
|
||||||
"requirement_ids": ["F-HMS-07", "F-HMS-20"],
|
"T03",
|
||||||
|
"T04"
|
||||||
|
],
|
||||||
|
"requirement_ids": [
|
||||||
|
"F-HMS-07",
|
||||||
|
"F-HMS-20"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "M",
|
"estimated_effort": "M",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
"commands": [
|
"commands": [
|
||||||
"cd backend && python -m pytest tests/test_contact_router.py tests/test_rental_router.py tests/test_email_service.py -v --cov=app.routers --cov=app.services.email --cov-report=term-missing",
|
"cd backend && python -m pytest tests/test_contact_router.py tests/test_rental_router.py tests/test_email_service.py -v --cov=app.routers --cov=app.services.email --cov-report=term-missing",
|
||||||
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{"name":"Test","email":"test@example.com","message":"Hallo","privacy_consent":true}' | grep '200'",
|
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{\"name\":\"Test\",\"email\":\"test@example.com\",\"message\":\"Hallo\",\"privacy_consent\":true}' | grep '200'",
|
||||||
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{"name":"Test","email":"invalid","message":"Hallo","privacy_consent":true}' | grep '422'",
|
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{\"name\":\"Test\",\"email\":\"invalid\",\"message\":\"Hallo\",\"privacy_consent\":true}' | grep '422'",
|
||||||
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{"name":"Test","email":"test@example.com","message":"Hallo","privacy_consent":false}' | grep '422'",
|
"curl -s -X POST http://localhost:8000/api/contact -H 'Content-Type: application/json' -d '{\"name\":\"Test\",\"email\":\"test@example.com\",\"message\":\"Hallo\",\"privacy_consent\":false}' | grep '422'",
|
||||||
"curl -s -X POST http://localhost:8000/api/rental-requests -H 'Content-Type: application/json' -d '{"event_name":"Test Event","date_start":"2026-08-15","date_end":"2026-08-16","location":"München","contact_name":"Max Mustermann","contact_email":"max@example.com","items":[{"equipment_id":1,"quantity":2}]}' | grep '201'",
|
"curl -s -X POST http://localhost:8000/api/rental-requests -H 'Content-Type: application/json' -d '{\"event_name\":\"Test Event\",\"date_start\":\"2026-08-15\",\"date_end\":\"2026-08-16\",\"location\":\"München\",\"contact_name\":\"Max Mustermann\",\"contact_email\":\"max@example.com\",\"items\":[{\"equipment_id\":1,\"quantity\":2}]}' | grep '201'",
|
||||||
"curl -s -X POST http://localhost:8000/api/rental-requests -H 'Content-Type: application/json' -d '{"event_name":"Test","date_start":"2026-08-16","date_end":"2026-08-15","location":"München","contact_name":"Max","contact_email":"max@example.com","items":[]}' | grep '422'"
|
"curl -s -X POST http://localhost:8000/api/rental-requests -H 'Content-Type: application/json' -d '{\"event_name\":\"Test\",\"date_start\":\"2026-08-16\",\"date_end\":\"2026-08-15\",\"location\":\"München\",\"contact_name\":\"Max\",\"contact_email\":\"max@example.com\",\"items\":[]}' | grep '422'"
|
||||||
],
|
],
|
||||||
"expected_results": "All tests pass with >=80% coverage. Contact POST with valid data returns 200. Contact POST with invalid email returns 422. Contact POST without privacy consent returns 422. Rental request POST with valid data returns 201 with reference_number. Rental request with date_end before date_start returns 422. Email service mock sends correct emails. Rate limiting blocks 6th request within 60s.",
|
"expected_results": "All tests pass with >=80% coverage. Contact POST with valid data returns 200. Contact POST with invalid email returns 422. Contact POST without privacy consent returns 422. Rental request POST with valid data returns 201 with reference_number. Rental request with date_end before date_start returns 422. Email service mock sends correct emails. Rate limiting blocks 6th request within 60s.",
|
||||||
"test_files": [
|
"test_files": [
|
||||||
@@ -229,8 +261,15 @@
|
|||||||
"id": "T06",
|
"id": "T06",
|
||||||
"title": "Mietkatalog Frontend: Equipment List, Detail, Search/Filter, SSR",
|
"title": "Mietkatalog Frontend: Equipment List, Detail, Search/Filter, SSR",
|
||||||
"description": "Complete Mietkatalog frontend: (1) Mietkatalog list page (/mietkatalog) with SSR (useAsyncData), equipment grid with EquipmentCard components, search input (freitext), category filter chips, sort dropdown (name_asc, name_desc), result count, pagination/infinite scroll, loading skeleton, empty state, error state with retry. (2) Equipment detail page (/mietkatalog/[id]) with SSR, full specs table, image/placeholder, related items, 'Mietanfrage' add-to-cart button. (3) Composables: useEquipment (API fetch wrapper), useApi (base API client). (4) EquipmentCard component with image, badge, name, description, add-to-cart emit. (5) Breadcrumb navigation on detail page.",
|
"description": "Complete Mietkatalog frontend: (1) Mietkatalog list page (/mietkatalog) with SSR (useAsyncData), equipment grid with EquipmentCard components, search input (freitext), category filter chips, sort dropdown (name_asc, name_desc), result count, pagination/infinite scroll, loading skeleton, empty state, error state with retry. (2) Equipment detail page (/mietkatalog/[id]) with SSR, full specs table, image/placeholder, related items, 'Mietanfrage' add-to-cart button. (3) Composables: useEquipment (API fetch wrapper), useApi (base API client). (4) EquipmentCard component with image, badge, name, description, add-to-cart emit. (5) Breadcrumb navigation on detail page.",
|
||||||
"dependencies": ["T01", "T03"],
|
"dependencies": [
|
||||||
"requirement_ids": ["F-HMS-04", "F-HMS-05", "F-HMS-06"],
|
"T01",
|
||||||
|
"T03"
|
||||||
|
],
|
||||||
|
"requirement_ids": [
|
||||||
|
"F-HMS-04",
|
||||||
|
"F-HMS-05",
|
||||||
|
"F-HMS-06"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
@@ -242,7 +281,7 @@
|
|||||||
"curl -s http://localhost:3000/mietkatalog | grep 'EquipmentCard'",
|
"curl -s http://localhost:3000/mietkatalog | grep 'EquipmentCard'",
|
||||||
"curl -s 'http://localhost:3000/mietkatalog?search=Lautsprecher' | grep 'Lautsprecher'",
|
"curl -s 'http://localhost:3000/mietkatalog?search=Lautsprecher' | grep 'Lautsprecher'",
|
||||||
"curl -s http://localhost:3000/mietkatalog/1 | grep 'Specs'",
|
"curl -s http://localhost:3000/mietkatalog/1 | grep 'Specs'",
|
||||||
"curl -s http://localhost:3000/mietkatalog/999999 | grep 'nicht gefunden'",
|
"curl -s http://localhost:3000/mietkatalog/999999 | grep 'nicht gefunden'"
|
||||||
],
|
],
|
||||||
"expected_results": "Build succeeds. All Vitest tests pass. Playwright E2E for mietkatalog pass. Mietkatalog page SSR-renders equipment cards in HTML (not just client JS). Search filters results in real-time. Category filter works. Sort changes order. Detail page shows specs and add-to-cart. Non-existent ID shows 404-style error with link back to catalog.",
|
"expected_results": "Build succeeds. All Vitest tests pass. Playwright E2E for mietkatalog pass. Mietkatalog page SSR-renders equipment cards in HTML (not just client JS). Search filters results in real-time. Category filter works. Sort changes order. Detail page shows specs and add-to-cart. Non-existent ID shows 404-style error with link back to catalog.",
|
||||||
"test_files": [
|
"test_files": [
|
||||||
@@ -274,8 +313,14 @@
|
|||||||
"id": "T07",
|
"id": "T07",
|
||||||
"title": "Warenkorb & Mietanfrage Frontend: Cart Store + Cart Drawer + Mietanfrage Form",
|
"title": "Warenkorb & Mietanfrage Frontend: Cart Store + Cart Drawer + Mietanfrage Form",
|
||||||
"description": "Complete cart and rental request frontend: (1) Pinia cart store (stores/cart.ts) with localStorage persistence (plugin pinia-persist.client.ts), add/remove items, adjust quantity, cart total count getter, clear cart. (2) CartDrawer component (mobile bottom-sheet, desktop side panel) accessible from header cart icon with counter badge. (3) Warenkorb page (/warenkorb) with item list, quantity steppers, remove buttons, rental dates display, CTA to /mietanfrage, empty state. (4) Mietanfrage page (/mietanfrage) with: cart overview (read-only), event data form (name*, date_start*, date_end*, location*, person_count), contact data form (name*, company, email*, phone, street, postalcode, city), message field, submit button, validation (client-side), states (form, validation-error, submitting, success, error), success page with reference_number, empty cart handling. (5) useCart composable. (6) Header cart counter integration.",
|
"description": "Complete cart and rental request frontend: (1) Pinia cart store (stores/cart.ts) with localStorage persistence (plugin pinia-persist.client.ts), add/remove items, adjust quantity, cart total count getter, clear cart. (2) CartDrawer component (mobile bottom-sheet, desktop side panel) accessible from header cart icon with counter badge. (3) Warenkorb page (/warenkorb) with item list, quantity steppers, remove buttons, rental dates display, CTA to /mietanfrage, empty state. (4) Mietanfrage page (/mietanfrage) with: cart overview (read-only), event data form (name*, date_start*, date_end*, location*, person_count), contact data form (name*, company, email*, phone, street, postalcode, city), message field, submit button, validation (client-side), states (form, validation-error, submitting, success, error), success page with reference_number, empty cart handling. (5) useCart composable. (6) Header cart counter integration.",
|
||||||
"dependencies": ["T01", "T06"],
|
"dependencies": [
|
||||||
"requirement_ids": ["F-HMS-16", "F-HMS-07"],
|
"T01",
|
||||||
|
"T06"
|
||||||
|
],
|
||||||
|
"requirement_ids": [
|
||||||
|
"F-HMS-16",
|
||||||
|
"F-HMS-07"
|
||||||
|
],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "L",
|
"estimated_effort": "L",
|
||||||
"test_spec": {
|
"test_spec": {
|
||||||
@@ -318,7 +363,15 @@
|
|||||||
"id": "T08",
|
"id": "T08",
|
||||||
"title": "Docker, Deployment & CI/CD Configuration",
|
"title": "Docker, Deployment & CI/CD Configuration",
|
||||||
"description": "Complete deployment infrastructure: (1) Frontend Dockerfile (Node 20, multi-stage build, Nuxt 3 production). (2) Backend Dockerfile (Python 3.12, pip install, uvicorn/gunicorn). (3) docker-compose.yml with 4 services (frontend, backend, postgres, redis), volumes, networks, health checks, restart policies. (4) Coolify deployment config (docker-compose for Coolify with env var placeholders). (5) Forgejo Actions CI/CD pipeline (.forgejo/workflows/ci.yml): lint, test (frontend + backend), build Docker images, deploy to Coolify via API. (6) .env.example with all required env vars. (7) README.md with setup, development, and deployment instructions.",
|
"description": "Complete deployment infrastructure: (1) Frontend Dockerfile (Node 20, multi-stage build, Nuxt 3 production). (2) Backend Dockerfile (Python 3.12, pip install, uvicorn/gunicorn). (3) docker-compose.yml with 4 services (frontend, backend, postgres, redis), volumes, networks, health checks, restart policies. (4) Coolify deployment config (docker-compose for Coolify with env var placeholders). (5) Forgejo Actions CI/CD pipeline (.forgejo/workflows/ci.yml): lint, test (frontend + backend), build Docker images, deploy to Coolify via API. (6) .env.example with all required env vars. (7) README.md with setup, development, and deployment instructions.",
|
||||||
"dependencies": ["T01", "T02", "T03", "T04", "T05", "T06", "T07"],
|
"dependencies": [
|
||||||
|
"T01",
|
||||||
|
"T02",
|
||||||
|
"T03",
|
||||||
|
"T04",
|
||||||
|
"T05",
|
||||||
|
"T06",
|
||||||
|
"T07"
|
||||||
|
],
|
||||||
"requirement_ids": [],
|
"requirement_ids": [],
|
||||||
"assigned_subagent": "implementation_engineer",
|
"assigned_subagent": "implementation_engineer",
|
||||||
"estimated_effort": "M",
|
"estimated_effort": "M",
|
||||||
@@ -327,13 +380,13 @@
|
|||||||
"docker build -t hms-frontend ./frontend --dry-run",
|
"docker build -t hms-frontend ./frontend --dry-run",
|
||||||
"docker build -t hms-backend ./backend --dry-run",
|
"docker build -t hms-backend ./backend --dry-run",
|
||||||
"docker compose config --quiet",
|
"docker compose config --quiet",
|
||||||
"python -c 'import yaml; yaml.safe_load(open("docker-compose.yml"))'",
|
"python -c 'import yaml; yaml.safe_load(open(\"docker-compose.yml\"))'",
|
||||||
"cat .env.example | grep 'RENTMAN_API_TOKEN'",
|
"cat .env.example | grep 'RENTMAN_API_TOKEN'",
|
||||||
"cat .env.example | grep 'JWT_SECRET'",
|
"cat .env.example | grep 'JWT_SECRET'",
|
||||||
"cat .env.example | grep 'DATABASE_URL'",
|
"cat .env.example | grep 'DATABASE_URL'",
|
||||||
"cat .env.example | grep 'SMTP_HOST'",
|
"cat .env.example | grep 'SMTP_HOST'",
|
||||||
"cat .forgejo/workflows/ci.yml | grep 'test'",
|
"cat .forgejo/workflows/ci.yml | grep 'test'",
|
||||||
"cat README.md | grep 'Setup'",
|
"cat README.md | grep 'Setup'"
|
||||||
],
|
],
|
||||||
"expected_results": "Dockerfiles build without errors. docker-compose.yml is valid YAML and passes docker compose config. .env.example contains all required env vars. CI/CD pipeline has test and build steps. README contains setup and deployment instructions.",
|
"expected_results": "Dockerfiles build without errors. docker-compose.yml is valid YAML and passes docker compose config. .env.example contains all required env vars. CI/CD pipeline has test and build steps. README contains setup and deployment instructions.",
|
||||||
"test_files": [
|
"test_files": [
|
||||||
@@ -359,13 +412,34 @@
|
|||||||
],
|
],
|
||||||
"dependency_graph": {
|
"dependency_graph": {
|
||||||
"T01": [],
|
"T01": [],
|
||||||
"T02": ["T01"],
|
"T02": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
"T03": [],
|
"T03": [],
|
||||||
"T04": ["T03"],
|
"T04": [
|
||||||
"T05": ["T03", "T04"],
|
"T03"
|
||||||
"T06": ["T01", "T03"],
|
],
|
||||||
"T07": ["T01", "T06"],
|
"T05": [
|
||||||
"T08": ["T01", "T02", "T03", "T04", "T05", "T06", "T07"]
|
"T03",
|
||||||
|
"T04"
|
||||||
|
],
|
||||||
|
"T06": [
|
||||||
|
"T01",
|
||||||
|
"T03"
|
||||||
|
],
|
||||||
|
"T07": [
|
||||||
|
"T01",
|
||||||
|
"T06"
|
||||||
|
],
|
||||||
|
"T08": [
|
||||||
|
"T01",
|
||||||
|
"T02",
|
||||||
|
"T03",
|
||||||
|
"T04",
|
||||||
|
"T05",
|
||||||
|
"T06",
|
||||||
|
"T07"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"parallel_tracks": [
|
"parallel_tracks": [
|
||||||
"Track A (Frontend): T01 → T02, T06, T07",
|
"Track A (Frontend): T01 → T02, T06, T07",
|
||||||
@@ -373,25 +447,69 @@
|
|||||||
"Track C (Deployment): T08 (after all)"
|
"Track C (Deployment): T08 (after all)"
|
||||||
],
|
],
|
||||||
"feature_coverage": {
|
"feature_coverage": {
|
||||||
"F-HMS-01": ["T02"],
|
"F-HMS-01": [
|
||||||
"F-HMS-02": ["T02"],
|
"T02"
|
||||||
"F-HMS-03": ["T02"],
|
],
|
||||||
"F-HMS-04": ["T03", "T06"],
|
"F-HMS-02": [
|
||||||
"F-HMS-05": ["T06"],
|
"T02"
|
||||||
"F-HMS-06": ["T03", "T06"],
|
],
|
||||||
"F-HMS-07": ["T05", "T07"],
|
"F-HMS-03": [
|
||||||
"F-HMS-08": ["T04"],
|
"T02"
|
||||||
"F-HMS-09": ["T04", "T05"],
|
],
|
||||||
"F-HMS-10": ["T01"],
|
"F-HMS-04": [
|
||||||
"F-HMS-11": ["T01"],
|
"T03",
|
||||||
"F-HMS-12": ["T02"],
|
"T06"
|
||||||
"F-HMS-13": ["T02"],
|
],
|
||||||
"F-HMS-14": ["T02"],
|
"F-HMS-05": [
|
||||||
"F-HMS-15": ["T01"],
|
"T06"
|
||||||
"F-HMS-16": ["T07"],
|
],
|
||||||
"F-HMS-17": ["T04"],
|
"F-HMS-06": [
|
||||||
"F-HMS-18": ["T01"],
|
"T03",
|
||||||
"F-HMS-19": ["T01"],
|
"T06"
|
||||||
"F-HMS-20": ["T05"]
|
],
|
||||||
|
"F-HMS-07": [
|
||||||
|
"T05",
|
||||||
|
"T07"
|
||||||
|
],
|
||||||
|
"F-HMS-08": [
|
||||||
|
"T04"
|
||||||
|
],
|
||||||
|
"F-HMS-09": [
|
||||||
|
"T04",
|
||||||
|
"T05"
|
||||||
|
],
|
||||||
|
"F-HMS-10": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
|
"F-HMS-11": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
|
"F-HMS-12": [
|
||||||
|
"T02"
|
||||||
|
],
|
||||||
|
"F-HMS-13": [
|
||||||
|
"T02"
|
||||||
|
],
|
||||||
|
"F-HMS-14": [
|
||||||
|
"T02"
|
||||||
|
],
|
||||||
|
"F-HMS-15": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
|
"F-HMS-16": [
|
||||||
|
"T07"
|
||||||
|
],
|
||||||
|
"F-HMS-17": [
|
||||||
|
"T04"
|
||||||
|
],
|
||||||
|
"F-HMS-18": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
|
"F-HMS-19": [
|
||||||
|
"T01"
|
||||||
|
],
|
||||||
|
"F-HMS-20": [
|
||||||
|
"T05"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user