From e46d107b2dd5fd51b404597d6327a972b2b00a90 Mon Sep 17 00:00:00 2001 From: DeeCee03 Date: Sat, 6 Jun 2026 20:11:44 +0200 Subject: [PATCH] Verlinkungen im Projekt-Detail editierbar machen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Risikoliste und Lessons Learned konnten bisher nur beim Anlegen bzw. Abschluss gesetzt werden. Jetzt ist die ganze Verlinkungen-Karte über "Bearbeiten" editierbar (Outline, Plane, Risikoliste, Lessons Learned). Leeres Feld entfernt den Link, URLs werden serverseitig validiert. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/web/src/routes/projekt.tsx | 89 ++++++++++++++++++++++---- packages/shared/src/schemas/project.ts | 4 ++ 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/apps/web/src/routes/projekt.tsx b/apps/web/src/routes/projekt.tsx index db0a991..6341abc 100644 --- a/apps/web/src/routes/projekt.tsx +++ b/apps/web/src/routes/projekt.tsx @@ -143,17 +143,7 @@ export function ProjektRoute() {
- - -

Verlinkungen

-
- - - - - - -
+ @@ -1850,6 +1840,83 @@ function AbschlussDialog({ ); } +const LINK_FIELDS = [ + { key: 'outlineSteckbriefUrl', label: 'Outline-Steckbrief', placeholder: 'https://wiki.tool-ctrl.de/doc/…' }, + { key: 'planeProjectUrl', label: 'Plane-Board', placeholder: 'https://project.tool-ctrl.de/…' }, + { key: 'risikolisteUrl', label: 'Risikoliste', placeholder: 'https://… (z. B. Outline / Plane)' }, + { key: 'lessonsLearnedUrl', label: 'Lessons Learned', placeholder: 'https://… (Retro / Doku)' }, +] as const; + +function VerlinkungenSection({ projectId, project: p }: { projectId: string; project: ProjectDetail }) { + const utils = trpc.useUtils(); + const [editing, setEditing] = useState(false); + const [vals, setVals] = useState>({}); + const update = trpc.project.update.useMutation({ + onSuccess: async () => { + toast(voice.toast.saved); + await utils.project.byId.invalidate({ id: projectId }); + setEditing(false); + }, + }); + + function startEdit() { + setVals(Object.fromEntries(LINK_FIELDS.map((f) => [f.key, p[f.key] ?? '']))); + setEditing(true); + } + function save() { + update.mutate({ + projectId, + ...Object.fromEntries(LINK_FIELDS.map((f) => [f.key, (vals[f.key] ?? '').trim() || null])), + }); + } + + return ( + + +

Verlinkungen

+ {!editing && ( + + )} +
+ + {editing ? ( +
+ {LINK_FIELDS.map((f) => ( + + ))} +

Leer lassen entfernt den Link. URLs müssen mit http(s):// beginnen.

+
+ + +
+
+ ) : ( + LINK_FIELDS.map((f) => ) + )} +
+
+ ); +} + function LinkRow({ label, url }: { label: string; url: string | null }) { return (

diff --git a/packages/shared/src/schemas/project.ts b/packages/shared/src/schemas/project.ts index 0df9388..eb8b1c4 100644 --- a/packages/shared/src/schemas/project.ts +++ b/packages/shared/src/schemas/project.ts @@ -104,6 +104,10 @@ export const updateProjectInput = z.object({ nichtZiele: z.string().max(2000).nullable().optional(), liefergegenstaende: z.string().max(2000).nullable().optional(), zieldreieckFlex: zieldreieckFlexSchema.nullable().optional(), + outlineSteckbriefUrl: z.string().url().nullable().optional(), + planeProjectUrl: z.string().url().nullable().optional(), + risikolisteUrl: z.string().url().nullable().optional(), + lessonsLearnedUrl: z.string().url().nullable().optional(), }); export type UpdateProjectInput = z.infer;