Deadline nachträglich editierbar, Arbeitsmodus aus UI entfernen
ci / quality (push) Failing after 28s
ci / build (push) Has been skipped
deploy / deploy (push) Successful in 53s

Deadline lässt sich jetzt direkt im Projekt-Header bearbeiten (Inline-
Datepicker mit Freitag/Wochenende-Warnung), via project.update. Der
Arbeitsmodus (Kanban/Cycle) ist in ebw bedeutungslos und fliegt aus der
Projekterstellung und der Header-Zeile raus. DB-Spalte bleibt ungenutzt
bestehen (keine destruktive Migration nötig).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dejanandClaude Opus 4.8 committed 2026-06-06 20:52:16 +02:00
1 parent 684841ffb4
commit ba8e524261
4 files changed
+72 -18

No files matched your search

-1
View File
@@ -74,7 +74,6 @@ export const projectRouter = router({
logoUrl: input.logoUrl ?? null,
kind: input.kind,
verantwortlicheUserId: input.verantwortlicheUserId ?? null,
arbeitsmodus: input.arbeitsmodus ?? null,
outlineSteckbriefUrl: input.outlineSteckbriefUrl,
planeProjectUrl: input.planeProjectUrl,
risikolisteUrl: input.risikolisteUrl ?? null,
-12
View File
@@ -192,7 +192,6 @@ function ProjectCreateDialog({
}
}, [areas, areaId, defaultSlug]);
const [kind, setKind] = useState<'kundenprojekt' | 'internes_projekt'>('internes_projekt');
const [arbeitsmodus, setArbeitsmodus] = useState<'cycle' | 'kanban'>('kanban');
const [steckbrief, setSteckbrief] = useState('');
const [plane, setPlane] = useState('');
@@ -215,7 +214,6 @@ function ProjectCreateDialog({
description: description.trim() || undefined,
logoUrl: logoUrl ?? undefined,
kind,
arbeitsmodus,
outlineSteckbriefUrl: steckbrief,
planeProjectUrl: plane,
milestones: [],
@@ -280,16 +278,6 @@ function ProjectCreateDialog({
<option value="internes_projekt">Internes Projekt</option>
</select>
</Field>
<Field label="Arbeitsmodus">
<select
value={arbeitsmodus}
onChange={(e) => setArbeitsmodus(e.target.value as 'cycle' | 'kanban')}
className="w-full rounded-[var(--radius-md)] border border-[var(--color-border)] bg-white px-3 py-2 text-sm"
>
<option value="kanban">Kanban</option>
<option value="cycle">Cycle</option>
</select>
</Field>
<Field label="Outline-Steckbrief-URL">
<input
required
+62 -4
View File
@@ -123,10 +123,11 @@ export function ProjektRoute() {
<span className="rounded-full bg-black/5 px-2.5 py-1 text-xs">archiviert</span>
)}
</div>
<p className="text-[var(--color-ink-muted)]">
{p.kind === 'kundenprojekt' ? 'Kundenprojekt' : 'Internes Projekt'} ·{' '}
{p.arbeitsmodus ?? '—'} · {p.deadline ? `Deadline ${p.deadline}` : 'kein Deadline'}
</p>
<div className="flex flex-wrap items-center gap-2 text-[var(--color-ink-muted)]">
<span>{p.kind === 'kundenprojekt' ? 'Kundenprojekt' : 'Internes Projekt'}</span>
<span>·</span>
<DeadlineControl projectId={p.id} deadline={p.deadline} />
</div>
<div className="mt-3">
<ModusControl projectId={p.id} modus={p.modus as ProjectModus} />
</div>
@@ -243,6 +244,63 @@ function ModusControl({ projectId, modus }: { projectId: string; modus: ProjectM
}
// --- Ziel & Maxime + SMART-Ziel, Nicht-Ziele, Liefergegenstände, Zieldreieck -
function DeadlineControl({ projectId, deadline }: { projectId: string; deadline: string | null }) {
const utils = trpc.useUtils();
const [editing, setEditing] = useState(false);
const [draft, setDraft] = useState(deadline ?? '');
const update = trpc.project.update.useMutation({
onSuccess: async () => {
toast(voice.toast.saved);
await utils.project.byId.invalidate({ id: projectId });
setEditing(false);
},
});
if (editing) {
const warnings = dateWarnings(draft || undefined);
return (
<span className="inline-flex flex-wrap items-center gap-2">
<input
type="date"
value={draft}
onChange={(e) => setDraft(e.target.value)}
className="rounded-[var(--radius-md)] border border-[var(--color-border)] bg-white px-2 py-1 text-sm text-[var(--color-ink)]"
/>
<Button
type="button"
size="sm"
disabled={update.isPending}
onClick={() => update.mutate({ projectId, deadline: draft || null })}
>
Speichern
</Button>
<Button type="button" variant="ghost" size="sm" onClick={() => setEditing(false)}>
Abbrechen
</Button>
{warnings.length > 0 && (
<span className="flex items-center gap-1 text-xs text-[var(--color-ampel-amber-fg)]">
<AlertTriangle size={12} /> {warnings[0]}
</span>
)}
</span>
);
}
return (
<button
type="button"
onClick={() => {
setDraft(deadline ?? '');
setEditing(true);
}}
className="inline-flex items-center gap-1 hover:text-[var(--color-ink)] hover:underline"
title="Deadline bearbeiten"
>
{deadline ? `Deadline ${deadline}` : 'kein Deadline'}
<Pencil size={12} />
</button>
);
}
function HeaderLogo({
projectId,
logoUrl,
+10 -1
View File
@@ -18,7 +18,6 @@ export const createProjectInput = z.object({
logoUrl: logoUrlSchema.optional(),
kind: z.enum(['kundenprojekt', 'internes_projekt']),
verantwortlicheUserId: z.string().uuid().optional(),
arbeitsmodus: z.enum(['cycle', 'kanban']).optional(),
outlineSteckbriefUrl: z.string().url(),
planeProjectUrl: z.string().url(),
risikolisteUrl: z.string().url().optional(),
@@ -114,6 +113,16 @@ export const updateProjectInput = z.object({
liefergegenstaende: z.string().max(2000).nullable().optional(),
zieldreieckFlex: zieldreieckFlexSchema.nullable().optional(),
logoUrl: logoUrlSchema.nullable().optional(),
start: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.nullable()
.optional(),
deadline: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.nullable()
.optional(),
outlineSteckbriefUrl: z.string().url().nullable().optional(),
planeProjectUrl: z.string().url().nullable().optional(),
risikolisteUrl: z.string().url().nullable().optional(),