feat: make responsiblePerson required on project creation/edit

- Zod schema: responsiblePerson now min(1) required, no longer optional
- ProjectModal: required indicator (*), HTML required attribute, no undefined fallback
- ProjectWizard: same fix for create flow
- Existing projects with null responsiblePerson still work (DB allows null)
- Validation enforced at API boundary on new creates/updates

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-23 07:35:42 +01:00
parent 3c0befb7db
commit 624badebad
3 changed files with 7 additions and 6 deletions
@@ -204,7 +204,7 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
startDate: new Date(form.startDate), startDate: new Date(form.startDate),
endDate: new Date(form.endDate), endDate: new Date(form.endDate),
status: form.status as unknown as ProjectStatus, status: form.status as unknown as ProjectStatus,
responsiblePerson: form.responsiblePerson.trim() || undefined, responsiblePerson: form.responsiblePerson.trim(),
...(form.color ? { color: form.color } : {}), ...(form.color ? { color: form.color } : {}),
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}), ...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
...(form.clientId ? { clientId: form.clientId } : {}), ...(form.clientId ? { clientId: form.clientId } : {}),
@@ -223,7 +223,7 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
status: form.status as unknown as ProjectStatus, status: form.status as unknown as ProjectStatus,
staffingReqs: [], staffingReqs: [],
dynamicFields: {}, dynamicFields: {},
responsiblePerson: form.responsiblePerson.trim() || undefined, responsiblePerson: form.responsiblePerson.trim(),
...(form.color ? { color: form.color } : {}), ...(form.color ? { color: form.color } : {}),
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}), ...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
...(form.clientId ? { clientId: form.clientId } : {}), ...(form.clientId ? { clientId: form.clientId } : {}),
@@ -521,8 +521,8 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
</div> </div>
<div> <div>
<label className={labelClass} htmlFor="responsiblePerson"> <label className={labelClass} htmlFor="responsiblePerson">
Responsible Person Responsible Person <span className="text-red-500">*</span>
<InfoTooltip content="Project lead or account manager responsible for this project." /> <InfoTooltip content="Project lead or account manager responsible for this project. Required." />
</label> </label>
<input <input
id="responsiblePerson" id="responsiblePerson"
@@ -530,6 +530,7 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
value={form.responsiblePerson} value={form.responsiblePerson}
onChange={(e) => setField("responsiblePerson", e.target.value)} onChange={(e) => setField("responsiblePerson", e.target.value)}
placeholder="Name or EID" placeholder="Name or EID"
required
className={inputClass} className={inputClass}
/> />
</div> </div>
@@ -1089,7 +1089,7 @@ export function ProjectWizard({ open, onClose }: ProjectWizardProps) {
endDate: new Date(state.endDate), endDate: new Date(state.endDate),
staffingReqs: state.staffingReqs, staffingReqs: state.staffingReqs,
status: state.saveAsDraft ? ProjectStatus.DRAFT : ProjectStatus.ACTIVE, status: state.saveAsDraft ? ProjectStatus.DRAFT : ProjectStatus.ACTIVE,
responsiblePerson: state.responsiblePerson.trim() || undefined, responsiblePerson: state.responsiblePerson.trim(),
blueprintId: state.blueprintId ?? undefined, blueprintId: state.blueprintId ?? undefined,
dynamicFields: {}, dynamicFields: {},
}); });
@@ -28,7 +28,7 @@ export const CreateProjectBaseSchema = z.object({
dynamicFields: z.record(z.string(), z.unknown()).default({}), dynamicFields: z.record(z.string(), z.unknown()).default({}),
blueprintId: z.string().optional(), blueprintId: z.string().optional(),
status: z.nativeEnum(ProjectStatus).default(ProjectStatus.DRAFT), status: z.nativeEnum(ProjectStatus).default(ProjectStatus.DRAFT),
responsiblePerson: z.string().max(200).optional(), responsiblePerson: z.string().min(1, "Responsible person is required").max(200),
color: z.string().regex(/^#[0-9a-fA-F]{6}$/, "Must be a hex color like #3b82f6").optional(), color: z.string().regex(/^#[0-9a-fA-F]{6}$/, "Must be a hex color like #3b82f6").optional(),
utilizationCategoryId: z.string().optional(), utilizationCategoryId: z.string().optional(),
clientId: z.string().optional(), clientId: z.string().optional(),