From 141c7f81dd0665ebba58a428f3afae528c7fa46b Mon Sep 17 00:00:00 2001 From: "Dennis (via Claude+Gemma)" Date: Sat, 23 May 2026 09:33:36 +0200 Subject: [PATCH] feat(toast-undo-pattern): Toast mit Undo-Button bei Delete-Actions [tsc:fail] --- .phase30-state.json | 3 +- .phase31-state.json | 5 ++ GENERATION_LOG.md | 25 +++++++++ apps/web/src/lib/toastUndo.tsx | 38 ++++++++++++++ scripts/phase31_features.py | 95 ++++++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 .phase31-state.json create mode 100644 apps/web/src/lib/toastUndo.tsx create mode 100644 scripts/phase31_features.py diff --git a/.phase30-state.json b/.phase30-state.json index 736d9db..961bcfa 100644 --- a/.phase30-state.json +++ b/.phase30-state.json @@ -6,6 +6,7 @@ "404-not-found-page", "api-error-pages", "accessibility-final", - "loading-everywhere" + "loading-everywhere", + "meta-tags-and-title" ] } \ No newline at end of file diff --git a/.phase31-state.json b/.phase31-state.json new file mode 100644 index 0000000..13e90bc --- /dev/null +++ b/.phase31-state.json @@ -0,0 +1,5 @@ +{ + "completed_features": [], + "current_feature": "toast-undo-pattern", + "started_at": "2026-05-23T09:33:26.338003" +} \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index eeeebe1..41cb3a8 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -3475,3 +3475,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:30:29` **INFO** Committed feature meta-tags-and-title +- `09:30:30` **INFO** Pushed: rc=0 + +## Phase-30 Run beendet — TOTAL FINISH (2026-05-23 09:30:30) + +- `09:30:30` **INFO** OK: 0, Attempted: 5, Total: 5 + +## 🚀 Phase-31 Codegen-Run gestartet — UX polish (2026-05-23 09:33:26) + + +## Phase-3 Feature: toast-undo-pattern (2026-05-23 09:33:26) + +- `09:33:26` **INFO** Description: Toast mit Undo-Button bei Delete-Actions +- `09:33:26` **INFO** Generating apps/web/src/lib/toastUndo.tsx (toastUndo(message, onUndo) hook/util. Wenn toast lib vorhanden: toast …) +- `09:33:35` **INFO** wrote 1008 chars in 8.7s (attempt 1) +- `09:33:35` **INFO** Running tsc --noEmit on api… +- `09:33:36` **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/lib/toastUndo.tsx b/apps/web/src/lib/toastUndo.tsx new file mode 100644 index 0000000..53be589 --- /dev/null +++ b/apps/web/src/lib/toastUndo.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { toast } from 'react-hot-toast'; + +/** + * toastUndo provides a way to notify the user of an action + * and offer a way to undo it. + * + * @param message - The message to display in the toast + * @param onUndo - The callback function to execute when 'Undo' is clicked + */ +export const toastUndo = (message: string, onUndo: () => void) => { + try { + toast((t) => ( + + {message} + + + ), { + duration: 5000, + position: 'bottom-right', + }); + } catch (e) { + // Fallback if toast library is not initialized or fails + if (window.confirm(`${message}\n\nWould you like to undo this action?`)) { + onUndo(); + } + } +}; + +export default toastUndo; \ No newline at end of file diff --git a/scripts/phase31_features.py b/scripts/phase31_features.py new file mode 100644 index 0000000..060abe0 --- /dev/null +++ b/scripts/phase31_features.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Phase-31: simple UX-tweaks — toasts, button-states, empty-states, copy-improvements.""" + +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 / ".phase31-state.json" + +FEATURES: list[Feature] = [ + Feature( + name="toast-undo-pattern", + description="Toast mit Undo-Button bei Delete-Actions", + files=[FileGen( + path="apps/web/src/lib/toastUndo.tsx", + purpose=( + "toastUndo(message, onUndo) hook/util. Wenn toast lib vorhanden: toast mit action-button 'Rückgängig'. " + "Sonst: window.confirm fallback. Export default. Keep imports minimal." + ), + )], + ), + Feature( + name="empty-state-illustrations", + description="EmptyState-Component für leere Listen", + files=[FileGen( + path="apps/web/src/components/EmptyState.tsx", + purpose=( + "EmptyState-Component. Props: icon (lucide-react component), title, description, actionLabel?, onAction?. " + "Zentriert in container: großes Icon (grey), title (text-lg bold), description (text-sm grey), optional CTA-Button. " + "Tailwind." + ), + )], + ), + Feature( + name="button-loading-states", + description="Button mit loading-prop zeigt Spinner", + files=[FileGen( + path="apps/web/src/components/Button.tsx", + purpose=( + "Button-Component. Props: variant ('primary'|'secondary'|'danger'), loading?: boolean, disabled?, children, onClick. " + "Tailwind classes per variant. Wenn loading: zeigt Spinner-SVG (lucide Loader2 mit animate-spin) statt children. " + "Auto-disabled wenn loading. Export default." + ), + )], + ), + Feature( + name="copy-to-clipboard-component", + description="CopyButton mit visual feedback", + files=[FileGen( + path="apps/web/src/components/CopyButton.tsx", + purpose=( + "CopyButton-Component. Props: text (string to copy), label?. Klick: navigator.clipboard.writeText(text). " + "Zeigt 2s lang Check-Icon (grün) + 'Kopiert!', dann zurück zu Copy-Icon. Lucide-react: Copy, Check. Tailwind." + ), + )], + ), +] + + +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-31 Codegen-Run gestartet — UX polish") + 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-31 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()))