From d33864c119e73ad9c0229ea730a6f8993fe9e056 Mon Sep 17 00:00:00 2001 From: Oliver Hofmann Date: Mon, 27 Apr 2026 09:02:00 +0200 Subject: [PATCH] docs: add landing page design spec and implementation plan --- .../plans/2026-04-27-landing-page.md | 370 ++++++++++++++++++ .../specs/2026-04-27-landing-page-design.md | 121 ++++++ 2 files changed, 491 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-landing-page.md create mode 100644 docs/superpowers/specs/2026-04-27-landing-page-design.md diff --git a/docs/superpowers/plans/2026-04-27-landing-page.md b/docs/superpowers/plans/2026-04-27-landing-page.md new file mode 100644 index 0000000..5a0cd5a --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-landing-page.md @@ -0,0 +1,370 @@ +# Landing Page & Shared Layout Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a shared Jinja2/Tailwind CSS layout and a module-card landing page for the University Process Hub. + +**Architecture:** FastAPI serves HTML via `Jinja2Templates`. `base.html` defines the red navbar (`#e2001a`), a `{% block content %}` slot, and a footer. `index.html` extends it with a hero section, responsive 3-column module-card grid, and an info strip. Module metadata is a static list in `main.py` — no DB access on the landing page. + +**Tech Stack:** FastAPI, Jinja2, Tailwind CSS Play CDN (no build step), pytest, httpx + +--- + +### Task 1: Add dependencies and update .gitignore + +**Files:** +- Modify: `requirements.txt` +- Modify: `.gitignore` + +- [ ] **Step 1: Update `requirements.txt`** + +``` +fastapi +uvicorn[standard] +pydantic-settings +sqlalchemy +jinja2 +pytest +httpx +``` + +- [ ] **Step 2: Add `.superpowers/` to `.gitignore`** + +Append to the `# Project specific` section: +``` +.superpowers/ +``` + +- [ ] **Step 3: Install new dependencies** + +```bash +.venv/bin/pip install -r requirements.txt +``` + +Expected: `jinja2`, `pytest`, `httpx` installed without error. + +- [ ] **Step 4: Commit** + +```bash +git add requirements.txt .gitignore +git commit -m "chore: add jinja2 and test dependencies" +``` + +--- + +### Task 2: Create base layout template + +**Files:** +- Create: `app/templates/base.html` +- Create: `app/static/.gitkeep` + +- [ ] **Step 1: Create directories** + +```bash +mkdir -p app/templates app/static +touch app/static/.gitkeep +``` + +- [ ] **Step 2: Write `app/templates/base.html`** + +```html + + + + + + {% block title %}EFI Hub{% endblock %} + + + + + + + +
+ {% block content %}{% endblock %} +
+ + + + + +``` + +- [ ] **Step 3: Commit** + +```bash +git add app/templates/base.html app/static/.gitkeep +git commit -m "feat: add base layout template with red navbar and footer" +``` + +--- + +### Task 3: Create landing page template + +**Files:** +- Create: `app/templates/index.html` + +- [ ] **Step 1: Write `app/templates/index.html`** + +```html +{% extends "base.html" %} + +{% block title %}University Process Hub{% endblock %} + +{% block content %} + +
+

+ Fakultät EFI · TH Nürnberg +

+

University Process Hub

+

+ Modulares Informationssystem für Fakultätsprozesse. + Startpunkt für studentische Arbeiten und Schnittstelle für interne Dienste. +

+
+ +
+

+ Verfügbare Module +

+
+ + {% for module in modules %} +
+
+
{{ module.icon }}
+
{{ module.name }}
+
{{ module.description }}
+ {% if module.status == "active" %} + Aktiv + {% else %} + Geplant + {% endif %} +
+ {% endfor %} + +
+
+
Neues Modul
+
Erweiterbar durch studentische Arbeiten.
+
+ +
+
+ +
+ API: FastAPI + Datenbank: {{ db_mode }} + WebSocket: aktiv +
● System online
+
+ +{% endblock %} +``` + +- [ ] **Step 2: Commit** + +```bash +git add app/templates/index.html +git commit -m "feat: add landing page template with module grid and info strip" +``` + +--- + +### Task 4: Wire templates into FastAPI and write tests + +**Files:** +- Modify: `app/main.py` +- Create: `tests/__init__.py` +- Create: `tests/test_landing.py` + +- [ ] **Step 1: Write the failing tests** + +```bash +mkdir -p tests && touch tests/__init__.py +``` + +Schreibe `tests/test_landing.py`: + +```python +from fastapi.testclient import TestClient +from app.main import app + +client = TestClient(app) + + +def test_landing_returns_html(): + response = client.get("/") + assert response.status_code == 200 + assert "text/html" in response.headers["content-type"] + + +def test_landing_contains_title(): + response = client.get("/") + assert "University Process Hub" in response.text + + +def test_landing_contains_rss_module(): + response = client.get("/") + assert "RSS-Feed Server" in response.text + + +def test_landing_navbar_links_present(): + response = client.get("/") + assert "Übersicht" in response.text + assert "RSS-Feeds" in response.text + + +def test_landing_info_strip_shows_db_mode(): + response = client.get("/") + assert "SQLite" in response.text or "MariaDB" in response.text +``` + +- [ ] **Step 2: Run tests to confirm they fail** + +```bash +.venv/bin/pytest tests/test_landing.py -v +``` + +Expected: FAIL — `GET /` returns JSON, not HTML. + +- [ ] **Step 3: Replace `app/main.py` with template-serving version** + +```python +from fastapi import FastAPI, Request, WebSocket +from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates + +from app.core.config import get_settings + +settings = get_settings() + +app = FastAPI( + title="University Process Hub", + root_path=settings.APP_PREFIX, +) + +app.mount("/static", StaticFiles(directory="app/static"), name="static") +templates = Jinja2Templates(directory="app/templates") + +MODULES = [ + { + "icon": "📡", + "name": "RSS-Feed Server", + "description": "Aggregiert und verteilt Neuigkeiten der Fakultät als standardkonformen RSS 2.0 Feed.", + "status": "active", + }, + { + "icon": "📅", + "name": "Kalender", + "description": "Veranstaltungen, Prüfungstermine und Fristen im iCal-Format.", + "status": "planned", + }, + { + "icon": "🔔", + "name": "Benachrichtigungen", + "description": "Push-Nachrichten und WebSocket-basierte Echtzeit-Alerts für Studierende.", + "status": "planned", + }, + { + "icon": "📊", + "name": "Auslastung", + "description": "Raum- und Ressourcenauslastung der Fakultät in Echtzeit.", + "status": "planned", + }, + { + "icon": "📚", + "name": "Lehrveranstaltungen", + "description": "Stundenplan-API und Kursinformationen aus dem Campus-System.", + "status": "planned", + }, +] + +NAV_ITEMS = [ + {"label": "Übersicht", "url": "/", "active": True}, + {"label": "RSS-Feeds", "url": "/rss", "active": False}, + {"label": "Kalender", "url": "/kalender", "active": False}, + {"label": "Docs", "url": "/docs", "active": False}, +] + + +def _db_mode() -> str: + url = settings.DATABASE_URL + return "SQLite (dev)" if url.startswith("sqlite") else "MariaDB (prod)" + + +@app.get("/", response_class=HTMLResponse) +async def root(request: Request): + return templates.TemplateResponse( + request, + "index.html", + { + "nav_items": NAV_ITEMS, + "modules": MODULES, + "db_mode": _db_mode(), + "app_version": "0.1.0", + }, + ) + + +@app.websocket("/ws/hello/{name}") +async def websocket_hello(websocket: WebSocket, name: str): + await websocket.accept() + await websocket.send_text(f"Hello, {name}!") + await websocket.close() +``` + +- [ ] **Step 4: Run tests to confirm they pass** + +```bash +.venv/bin/pytest tests/test_landing.py -v +``` + +Expected: All 5 tests PASS. + +- [ ] **Step 5: Verify in browser** + +```bash +.venv/bin/uvicorn app.main:app --reload +``` + +Öffne `http://localhost:8000` — Navbar rot, Hero-Bereich, 5 Modul-Karten + Placeholder-Karte, Info-Strip, Footer. + +- [ ] **Step 6: Commit** + +```bash +git add app/main.py tests/ +git commit -m "feat: serve landing page via Jinja2 with module grid" +``` diff --git a/docs/superpowers/specs/2026-04-27-landing-page-design.md b/docs/superpowers/specs/2026-04-27-landing-page-design.md new file mode 100644 index 0000000..f5cff3f --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-landing-page-design.md @@ -0,0 +1,121 @@ +# Design Spec: Landing Page & Shared Layout + +**Datum:** 2026-04-27 +**Status:** Genehmigt + +--- + +## Ziel + +Aufbau einer Landing Page mit gemeinsamem Layout-System für alle zukünftigen Seiten des University Process Hub (UPH). Das Layout ersetzt Bootstrap der Vorversion durch Tailwind CSS. + +--- + +## Farbpalette + +| Rolle | Farbe | Hex | +|---|---|---| +| Primär / Navbar | TH-Rot | `#e2001a` | +| Hintergrund Seite | Hellgrau | `#f5f5f5` | +| Hintergrund Karten / Navbar-Content | Weiß | `#ffffff` | +| Text primär | Fast-Schwarz | `#1a1a1a` | +| Text sekundär | Mittelgrau | `#666666` | +| Text deaktiviert / Labels | Hellgrau | `#888888` | +| Trennlinien / Borders | Sehr hell | `#e8e8e8` | +| Akzent hover (Karten) | Rot 12% opacity | `rgba(226,0,26,0.12)` | + +Kein Dunkelgrau in der Navbar — Rot trägt allein die Markenpräsenz. + +--- + +## Layout-System (Base Template) + +**Aufbau jeder Seite (top → bottom):** + +``` +┌─────────────────────────────────────────┐ +│ NAVBAR (rot, 56px) │ +│ Logo · Nav-Links · Anmelden-Button │ +├─────────────────────────────────────────┤ +│ PAGE CONTENT (slot — variabel) │ +│ - Hero / Section-Header │ +│ - Hauptinhalt │ +├─────────────────────────────────────────┤ +│ FOOTER (hellgrau, 44px) │ +│ Brand · Hochschulname · Version │ +└─────────────────────────────────────────┘ +``` + +**Navbar-Details:** +- Hintergrund `#e2001a` +- Logo: `EFIHub` (Hub in 70% Opacity) +- Nav-Links: `rgba(255,255,255,0.85)`, aktiver Link weiß mit weißem Bottom-Border +- Login-Button: Ghost-Style (`rgba(255,255,255,0.15)`, weißer Border) +- Höhe: `56px`, Padding `0 32px` + +**Footer:** +- Hintergrund `#f5f5f5`, Border-Top `#e0e0e0` +- Dreispaltiges Layout: Brand | Hochschulname | Version + +--- + +## Landing Page + +### Hero-Bereich +- Hintergrund Weiß, Border-Bottom `#e8e8e8` +- Kleines rotes Label oben: `Fakultät EFI · TH Nürnberg` +- H1: `University Process Hub` +- Beschreibungstext (max. 560px Breite) + +### Modul-Karten +- 3-spaltiges Grid auf Desktop, responsive (2-spaltig tablet, 1-spaltig mobile) +- Weiße Karten, Border `#e8e8e8`, 3px roter Top-Stripe +- Hover: Schatten `rgba(226,0,26,0.12)`, Border-Farbe `#e2001a` +- Jede Karte: Icon · Name · Kurzbeschreibung · Status-Badge +- Status-Badges: Aktiv (grün), Geplant (grau), + leere Placeholder-Karte (gestrichelt) + +### Info-Strip (zwischen Grid und Footer) +- Weiß, Border-Top `#e8e8e8` +- Systeminfos: API-Version, Datenbank-Modus, WebSocket-Status +- Rechts: `● System online` in Rot + +--- + +## Template-Architektur + +**Technologie:** Jinja2 (FastAPI-nativ) + Tailwind CSS via Play CDN (kein Build-Step nötig) + +**Dateien:** +``` +app/ +├── templates/ +│ ├── base.html # Navbar + Footer + Tailwind-Imports +│ └── index.html # Landing Page (extends base.html) +└── static/ # Für custom CSS / Assets (später) +``` + +**FastAPI-Integration:** +- `Jinja2Templates` in `main.py` konfigurieren +- `StaticFiles` für `app/static/` mounten +- `GET /` liefert `index.html` mit Modul-Liste aus Config + +**Modul-Liste:** Zur Laufzeit aus einer Liste in `config.py` oder einem statischen Dict in `main.py` — kein DB-Zugriff auf der Landing Page. + +--- + +## Responsive Breakpoints + +| Breakpoint | Modul-Grid | +|---|---| +| `< 640px` | 1 Spalte | +| `640px – 1024px` | 2 Spalten | +| `> 1024px` | 3 Spalten | + +--- + +## Out of Scope + +- Authentifizierung (separates Modul) +- Dynamische Modul-Metadaten aus DB +- Dark Mode +- Animationen / Transitions \ No newline at end of file