feat(projekt): Projektmanagement-Tiefe nach Buch & Leitfaden v3.0
Operationalisiert den ToolCTRL-Projektmanagement-Leitfaden auf der Projekt-Detailseite (hybrid: Cockpit-Primitive nativ, Schwergewichtiges per Outline-Link): - Projekt-Modus (Relax/Standard/Eskalation/Taskforce) + Dashboard-Surfacing - Risiko- & Chancen-Register (Wahrscheinlichkeit×Auswirkung, Showstopper, Owner, Strategie) — offene Showstopper landen auf dem Dashboard - Stakeholder-Analyse mit Macht/Interesse-Matrix - Ziel & Definition: SMART-Ziel, Nicht-Ziele, Liefergegenstaende, Maxime, Zieldreieck (welche Ecke gibt nach) - Meilensteine editierbar inkl. Ergebnis/Kundennutzen, Freitags-/ Feiertags-Warnung, Mindest-2-Hinweis - Phasen-Lebenszyklus (Definition→Planung→Umsetzung→Abschluss) mit Gate-Reviews - Projekt-Statusbericht (Ampel/Trend/Text, extern-Flag, Verlauf) — speist statusCache & Pflicht-Minimum - Änderungs-Log (Scope-Creep-Schutz), strukturierter Abschluss mit Checkliste + Abschlussvermerk Migrationen 0009_pm_insights + 0010_leitfaden_phases. Enthält auch die gemeinsame schema.ts (inkl. Lead-Tabellen aus dem Vor-Commit) und Seed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d72aa9fbf3
commit
864a1cef6a
22 files changed
+9237
-60
No files matched your search
@@ -4,5 +4,9 @@ export * as ProjectSchemas from './schemas/project.js';
|
||||
export * as DealSchemas from './schemas/deal.js';
|
||||
export * as CampaignSchemas from './schemas/campaign.js';
|
||||
export * as TimeEntrySchemas from './schemas/time-entry.js';
|
||||
export * as LeadSchemas from './schemas/lead.js';
|
||||
export * as RiskSchemas from './schemas/risk.js';
|
||||
export * as StakeholderSchemas from './schemas/stakeholder.js';
|
||||
export * as ChangeSchemas from './schemas/change.js';
|
||||
|
||||
export const SHARED_VERSION = '0.0.3';
|
||||
export const SHARED_VERSION = '0.0.6';
|
||||
@@ -0,0 +1,24 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const changeEntscheidungSchema = z.enum(['offen', 'genehmigt', 'abgelehnt']);
|
||||
export const CHANGE_ENTSCHEIDUNGEN = changeEntscheidungSchema.options;
|
||||
|
||||
export const changeForProjectInput = z.object({ projectId: z.string().uuid() });
|
||||
|
||||
export const createChangeInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
titel: z.string().min(1).max(300),
|
||||
auswirkung: z.string().max(1000).optional(),
|
||||
entscheidung: changeEntscheidungSchema.default('offen'),
|
||||
});
|
||||
export type CreateChangeInput = z.infer<typeof createChangeInput>;
|
||||
|
||||
export const updateChangeInput = z.object({
|
||||
id: z.string().uuid(),
|
||||
titel: z.string().min(1).max(300).optional(),
|
||||
auswirkung: z.string().max(1000).nullable().optional(),
|
||||
entscheidung: changeEntscheidungSchema.optional(),
|
||||
});
|
||||
export type UpdateChangeInput = z.infer<typeof updateChangeInput>;
|
||||
|
||||
export const removeChangeInput = z.object({ id: z.string().uuid() });
|
||||
@@ -1,5 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const ampelSchema = z.enum(['green', 'amber', 'red']);
|
||||
const trendSchema = z.enum(['besser', 'gleich', 'schlechter']);
|
||||
|
||||
export const createProjectInput = z.object({
|
||||
areaId: z.string().uuid(),
|
||||
name: z.string().min(1).max(200),
|
||||
@@ -36,11 +39,98 @@ export type CreateProjectInput = z.infer<typeof createProjectInput>;
|
||||
export const archiveProjectInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
lessonsLearnedUrl: z.string().url(),
|
||||
abschlussvermerk: z.string().max(2000).optional(),
|
||||
});
|
||||
export type ArchiveProjectInput = z.infer<typeof archiveProjectInput>;
|
||||
|
||||
// --- Zieldreieck (Leitfaden Kap. 1) ----------------------------------------
|
||||
export const zieldreieckFlexSchema = z.enum(['leistung', 'zeit', 'aufwand']);
|
||||
|
||||
// --- Projektphasen + Gate-Review (Leitfaden Kap. 2 + 5) --------------------
|
||||
export const projectPhaseSchema = z.enum(['definition', 'planung', 'umsetzung', 'abschluss']);
|
||||
export const PROJECT_PHASES = projectPhaseSchema.options;
|
||||
|
||||
export const setPhaseInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
phase: projectPhaseSchema,
|
||||
});
|
||||
export type SetPhaseInput = z.infer<typeof setPhaseInput>;
|
||||
|
||||
export const addPhaseGateInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
phase: projectPhaseSchema,
|
||||
ergebnisErreicht: z.boolean(),
|
||||
notiz: z.string().max(1000).optional(),
|
||||
advance: z.boolean().default(false), // Folgephase direkt freigeben
|
||||
});
|
||||
export type AddPhaseGateInput = z.infer<typeof addPhaseGateInput>;
|
||||
|
||||
export const phaseGatesInput = z.object({ projectId: z.string().uuid() });
|
||||
|
||||
// --- Projekt-Statusbericht (Leitfaden Kap. 5) ------------------------------
|
||||
export const addProjectStatusInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
ampel: ampelSchema,
|
||||
trend: trendSchema.optional(),
|
||||
text: z.string().max(1000).optional(),
|
||||
externSichtbar: z.boolean().default(false),
|
||||
});
|
||||
export type AddProjectStatusInput = z.infer<typeof addProjectStatusInput>;
|
||||
|
||||
export const projectStatusHistoryInput = z.object({ projectId: z.string().uuid() });
|
||||
|
||||
export const setMilestoneStatusInput = z.object({
|
||||
milestoneId: z.string().uuid(),
|
||||
status: z.enum(['open', 'done', 'overdue']),
|
||||
});
|
||||
export type SetMilestoneStatusInput = z.infer<typeof setMilestoneStatusInput>;
|
||||
|
||||
// --- Projekt-Modus (Buch Kap. 4 Strategie) ---------------------------------
|
||||
export const projectModusSchema = z.enum(['relax', 'standard', 'eskalation', 'taskforce']);
|
||||
export const PROJECT_MODI = projectModusSchema.options;
|
||||
export const setModusInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
modus: projectModusSchema,
|
||||
});
|
||||
export type SetModusInput = z.infer<typeof setModusInput>;
|
||||
|
||||
// --- Ziel-Disziplin: Maxime / Beschreibung / DoD editierbar ----------------
|
||||
export const updateProjectInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
maxime: z.string().max(280).nullable().optional(),
|
||||
description: z.string().max(2000).nullable().optional(),
|
||||
dodText: z.string().max(2000).nullable().optional(),
|
||||
ziel: z.string().max(1000).nullable().optional(),
|
||||
nichtZiele: z.string().max(2000).nullable().optional(),
|
||||
liefergegenstaende: z.string().max(2000).nullable().optional(),
|
||||
zieldreieckFlex: zieldreieckFlexSchema.nullable().optional(),
|
||||
});
|
||||
export type UpdateProjectInput = z.infer<typeof updateProjectInput>;
|
||||
|
||||
// --- Meilenstein-Disziplin: anlegen, editieren, entfernen ------------------
|
||||
export const addMilestoneInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
title: z.string().min(1).max(200),
|
||||
due: z
|
||||
.string()
|
||||
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
||||
.optional(),
|
||||
});
|
||||
export type AddMilestoneInput = z.infer<typeof addMilestoneInput>;
|
||||
|
||||
export const updateMilestoneInput = z.object({
|
||||
milestoneId: z.string().uuid(),
|
||||
title: z.string().min(1).max(200).optional(),
|
||||
due: z
|
||||
.string()
|
||||
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
||||
.nullable()
|
||||
.optional(),
|
||||
ergebnis: z.string().max(2000).nullable().optional(),
|
||||
kundennutzen: z.string().max(1000).nullable().optional(),
|
||||
status: z.enum(['open', 'done', 'overdue']).optional(),
|
||||
});
|
||||
export type UpdateMilestoneInput = z.infer<typeof updateMilestoneInput>;
|
||||
|
||||
export const removeMilestoneInput = z.object({ milestoneId: z.string().uuid() });
|
||||
export type RemoveMilestoneInput = z.infer<typeof removeMilestoneInput>;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const riskKindSchema = z.enum(['risiko', 'chance']);
|
||||
export const riskLevelSchema = z.enum(['niedrig', 'mittel', 'hoch']);
|
||||
export const riskHerkunftSchema = z.enum(['intern', 'extern']);
|
||||
export const riskStatusSchema = z.enum(['offen', 'beobachtet', 'eingetreten', 'geschlossen']);
|
||||
export const RISK_STATUSES = riskStatusSchema.options;
|
||||
export const riskStrategieSchema = z.enum(['vermeiden', 'verlagern', 'mindern', 'akzeptieren']);
|
||||
export const RISK_STRATEGIEN = riskStrategieSchema.options;
|
||||
|
||||
export const riskForProjectInput = z.object({ projectId: z.string().uuid() });
|
||||
|
||||
export const createRiskInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
kind: riskKindSchema.default('risiko'),
|
||||
title: z.string().min(1).max(500),
|
||||
wahrscheinlichkeit: riskLevelSchema.default('mittel'),
|
||||
auswirkung: riskLevelSchema.default('mittel'),
|
||||
herkunft: riskHerkunftSchema.optional(),
|
||||
showstopper: z.boolean().default(false),
|
||||
massnahme: z.string().max(1000).optional(),
|
||||
strategie: riskStrategieSchema.optional(),
|
||||
ownerUserId: z.string().uuid().optional(),
|
||||
status: riskStatusSchema.default('offen'),
|
||||
});
|
||||
export type CreateRiskInput = z.infer<typeof createRiskInput>;
|
||||
|
||||
export const updateRiskInput = z.object({
|
||||
id: z.string().uuid(),
|
||||
kind: riskKindSchema.optional(),
|
||||
title: z.string().min(1).max(500).optional(),
|
||||
wahrscheinlichkeit: riskLevelSchema.optional(),
|
||||
auswirkung: riskLevelSchema.optional(),
|
||||
herkunft: riskHerkunftSchema.nullable().optional(),
|
||||
showstopper: z.boolean().optional(),
|
||||
massnahme: z.string().max(1000).nullable().optional(),
|
||||
strategie: riskStrategieSchema.nullable().optional(),
|
||||
ownerUserId: z.string().uuid().nullable().optional(),
|
||||
status: riskStatusSchema.optional(),
|
||||
});
|
||||
export type UpdateRiskInput = z.infer<typeof updateRiskInput>;
|
||||
|
||||
export const removeRiskInput = z.object({ id: z.string().uuid() });
|
||||
@@ -0,0 +1,32 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const stakeholderLevelSchema = z.enum(['niedrig', 'mittel', 'hoch']);
|
||||
export const stakeholderHaltungSchema = z.enum(['pro', 'neutral', 'contra']);
|
||||
|
||||
export const stakeholderForProjectInput = z.object({ projectId: z.string().uuid() });
|
||||
|
||||
export const createStakeholderInput = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
name: z.string().min(1).max(200),
|
||||
rolle: z.string().max(200).optional(),
|
||||
interesse: stakeholderLevelSchema.default('mittel'),
|
||||
einfluss: stakeholderLevelSchema.default('mittel'),
|
||||
haltung: stakeholderHaltungSchema.default('neutral'),
|
||||
beduerfnis: z.string().max(1000).optional(),
|
||||
notiz: z.string().max(1000).optional(),
|
||||
});
|
||||
export type CreateStakeholderInput = z.infer<typeof createStakeholderInput>;
|
||||
|
||||
export const updateStakeholderInput = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string().min(1).max(200).optional(),
|
||||
rolle: z.string().max(200).nullable().optional(),
|
||||
interesse: stakeholderLevelSchema.optional(),
|
||||
einfluss: stakeholderLevelSchema.optional(),
|
||||
haltung: stakeholderHaltungSchema.optional(),
|
||||
beduerfnis: z.string().max(1000).nullable().optional(),
|
||||
notiz: z.string().max(1000).nullable().optional(),
|
||||
});
|
||||
export type UpdateStakeholderInput = z.infer<typeof updateStakeholderInput>;
|
||||
|
||||
export const removeStakeholderInput = z.object({ id: z.string().uuid() });
|
||||
Reference in new issue
Block a user