Add print cover fields and office profile

- Project settings gain siteAddress, workStage, projectLead and
  projectEditor (project.update-settings schema version 3, migration 0009);
  versions 2 and 1 stay executable and keep the current cover values.
  Snapshot schema 6 carries the fields; older versions upgrade with null.
- Global office profile (office name/address) next to the document logo:
  table document_branding_settings, PUT /api/document-branding/office,
  editable from the print view toolbar.
- Cover page reads all fields; empty values show the "[ergänzen]" marker.
  The Planstände editor column shows the project editor.
- Shared logo constants moved to a dependency-free module so the Next.js
  bundler can import them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Grovy311 2026-09-25 20:42:44 +02:00
parent 94688d7a8c
commit bf58296ba5
35 changed files with 3571 additions and 106 deletions

View file

@ -4,8 +4,22 @@ import {
distributionBoardSupplyTypes,
type DistributionBoardInfoField,
} from "../constants/distribution-board.js";
import {
projectCoverFieldMaxLengths,
type ProjectCoverField,
} from "../constants/project-cover.js";
import { expectedProjectRevisionSchema } from "./project-command.schemas.js";
// Empty text is stored as null so "not entered" has one representation.
const optionalCoverValueSchema = (field: ProjectCoverField) =>
z
.string()
.trim()
.max(projectCoverFieldMaxLengths[field])
.nullable()
.transform((value) => (value ? value : null))
.optional();
export const createProjectSchema = z.object({
name: z.string().trim().min(1).max(200),
internalProjectNumber: z.string().trim().max(100).optional(),
@ -34,6 +48,11 @@ export const updateProjectSettingsSchema = z
.refine((values) => new Set(values).size === values.length, {
message: "Netzarten dürfen nicht mehrfach ausgewählt werden.",
}),
// Print cover fields; omitted fields keep their current value.
siteAddress: optionalCoverValueSchema("siteAddress"),
workStage: optionalCoverValueSchema("workStage"),
projectLead: optionalCoverValueSchema("projectLead"),
projectEditor: optionalCoverValueSchema("projectEditor"),
})
.strict();