diff --git a/.phase32-state.json b/.phase32-state.json index e2b00e2..d34132f 100644 --- a/.phase32-state.json +++ b/.phase32-state.json @@ -5,6 +5,7 @@ "attempted_features": [ "tooltip-component", "badge-component", - "card-component" + "card-component", + "status-dot-component" ] } \ No newline at end of file diff --git a/.phase33-state.json b/.phase33-state.json new file mode 100644 index 0000000..707b4d6 --- /dev/null +++ b/.phase33-state.json @@ -0,0 +1,5 @@ +{ + "completed_features": [], + "current_feature": "avatar-component", + "started_at": "2026-05-23T09:43:23.964164" +} \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index 5921de2..5fb3fee 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -3633,3 +3633,28 @@ src/index.ts(27,25): error TS2769: No overload matches this call. Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. Type 'Promise' provides no match for the signature '(instance: FastifyInstance, +- `09:39:17` **INFO** Committed feature status-dot-component +- `09:39:17` **INFO** Pushed: rc=0 + +## Phase-32 Run beendet (2026-05-23 09:39:17) + +- `09:39:17` **INFO** OK: 0, Attempted: 4, Total: 4 + +## 🚀 Phase-33 Codegen-Run gestartet (2026-05-23 09:43:23) + + +## Phase-3 Feature: avatar-component (2026-05-23 09:43:23) + +- `09:43:23` **INFO** Description: Avatar mit Initial-Fallback +- `09:43:23` **INFO** Generating apps/web/src/components/Avatar.tsx (Avatar-Component. Props: name (string), imageUrl?, size?: 'sm'|'md'|'l…) +- `09:43:43` **INFO** wrote 1773 chars in 19.6s (attempt 1) +- `09:43:43` **INFO** Running tsc --noEmit on api… +- `09:43:45` **WARN** tsc errors: +src/db/schema.ts(37,14): error TS7022: 'customers' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +src/db/schema.ts(45,59): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +src/db/schema.ts(49,14): error TS7022: 'projects' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +src/db/schema.ts(53,56): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +src/index.ts(27,25): error TS2769: No overload matches this call. + Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. + Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. + Type 'Promise' provides no match for the signature '(instance: FastifyInstance, diff --git a/apps/web/src/components/Avatar.tsx b/apps/web/src/components/Avatar.tsx index f4840c1..21d2ed6 100644 --- a/apps/web/src/components/Avatar.tsx +++ b/apps/web/src/components/Avatar.tsx @@ -2,44 +2,64 @@ import React from 'react'; interface AvatarProps { name: string; + imageUrl?: string; size?: 'sm' | 'md' | 'lg'; + shape?: 'circle' | 'square'; } -const COLORS = [ - 'bg-orange-500', - 'bg-blue-500', - 'bg-emerald-500', - 'bg-purple-500', - 'bg-pink-500', -]; - const SIZE_MAP = { sm: 'w-8 h-8 text-xs', - md: 'w-10 h-10 text-sm', - lg: 'w-12 h-12 text-base', + md: 'w-12 h-12 text-sm', + lg: 'w-20 h-20 text-lg', }; -export const Avatar: React.FC = ({ name, size = 'md' }) => { - const getInitials = (name: string) => { - const parts = name.trim().split(/\s+/); - if (parts.length === 1) return parts[0].substring(0, 2).toUpperCase(); - return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); - }; +const COLORS = [ + 'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500', + 'bg-lime-500', 'bg-green-500', 'bg-emerald-500', 'bg-teal-500', + 'bg-cyan-500', 'bg-sky-500', 'bg-blue-500', 'bg-indigo-500', + 'bg-violet-500', 'bg-purple-500', 'bg-fuchsia-500', 'bg-pink-500', +]; - const getColor = (name: string) => { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = name.charCodeAt(i) + ((hash << 5) - hash); - } - const index = Math.abs(hash) % COLORS.length; - return COLORS[index]; - }; +const getInitials = (name: string) => { + const parts = name.trim().split(/\s+/); + if (parts.length === 1) return parts[0].substring(0, 2).toUpperCase(); + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); +}; + +const getColorByName = (name: string) => { + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + const index = Math.abs(hash) % COLORS.length; + return COLORS[index]; +}; + +export default function Avatar({ + name, + imageUrl, + size = 'md', + shape = 'circle' +}: AvatarProps) { + const sizeClass = SIZE_MAP[size]; + const shapeClass = shape === 'circle' ? 'rounded-full' : 'rounded-md'; + const bgColor = getColorByName(name); + const initials = getInitials(name); return ( -
- {getInitials(name)} +
+ {imageUrl ? ( + {name} { + (e.target as HTMLImageElement).style.display = 'none'; + }} + /> + ) : ( + {initials} + )}
); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/scripts/phase33_features.py b/scripts/phase33_features.py new file mode 100644 index 0000000..de067ae --- /dev/null +++ b/scripts/phase33_features.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Phase-33: standalone components — Avatar, Divider, Pagination, ColorPicker.""" + +from __future__ import annotations + +import asyncio, datetime, json, sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from phase2_features import Feature, FileGen, ROOT, log, log_section +from phase3_features import run_feature_v2 + +PHASE_STATE = ROOT / ".phase33-state.json" + +FEATURES: list[Feature] = [ + Feature( + name="avatar-component", + description="Avatar mit Initial-Fallback", + files=[FileGen( + path="apps/web/src/components/Avatar.tsx", + purpose=( + "Avatar-Component. Props: name (string), imageUrl?, size?: 'sm'|'md'|'lg' (default md), shape?: 'circle'|'square'. " + "Wenn imageUrl: . Sonst: deterministisches BG-Color via name-hash + Initialen (1-2 Buchstaben). " + "Tailwind. Export default." + ), + )], + ), + Feature( + name="divider-component", + description="Divider horizontal/vertikal", + files=[FileGen( + path="apps/web/src/components/Divider.tsx", + purpose=( + "Divider-Component. Props: orientation?: 'horizontal'|'vertical' (default horizontal), label?, className?. " + "Horizontal: border-t + optional centered label. Vertical: border-l h-full inline-block. Tailwind. Export default." + ), + )], + ), + Feature( + name="pagination-component", + description="Pagination mit Prev/Next + Page-Numbers", + files=[FileGen( + path="apps/web/src/components/Pagination.tsx", + purpose=( + "Pagination-Component. Props: currentPage, totalPages, onPageChange(page). " + "Zeigt Prev-Button, Page-Numbers (max 7 visible mit ... dots), Next-Button. " + "Disabled prev/next at boundaries. Tailwind. Export default." + ), + )], + ), + Feature( + name="color-picker-component", + description="ColorPicker mit Preset-Swatches", + files=[FileGen( + path="apps/web/src/components/ColorPicker.tsx", + purpose=( + "ColorPicker-Component. Props: value (hex string), onChange(hex). " + "Zeigt 10 Preset-Color-Swatches in Grid (Tailwind palette: red-500, orange-500, etc.). " + "Klick auf Swatch ruft onChange. Aktive zeigt Ring." + ), + )], + ), +] + + +def load_state(): + if PHASE_STATE.exists(): + return json.loads(PHASE_STATE.read_text()) + return {"completed_features": [], "current_feature": None, "started_at": datetime.datetime.now().isoformat()} + + +def save_state(state): + PHASE_STATE.write_text(json.dumps(state, indent=2)) + + +async def main(): + log_section("🚀 Phase-33 Codegen-Run gestartet") + state = load_state() + for feature in FEATURES: + if feature.name in state.get("completed_features", []): + continue + state["current_feature"] = feature.name; save_state(state) + try: + success = await run_feature_v2(feature) + state.setdefault("completed_features" if success else "attempted_features", []).append(feature.name) + save_state(state) + except Exception as e: + log(f"❌ {feature.name} crashed: {e}", level="ERROR") + state.setdefault("attempted_features", []).append(feature.name); save_state(state) + log_section("Phase-33 Run beendet") + log(f"OK: {len(state.get('completed_features', []))}, Attempted: {len(state.get('attempted_features', []))}, Total: {len(FEATURES)}") + return 0 + + +if __name__ == "__main__": + sys.exit(asyncio.run(main()))