chore(repo): initialize planarchy workspace

This commit is contained in:
2026-03-14 14:31:09 +01:00
commit dd55d0e78b
769 changed files with 166461 additions and 0 deletions
@@ -0,0 +1,356 @@
import { z } from "zod";
import {
EstimateExportFormat,
EstimateStatus,
EstimateVersionStatus,
} from "../types/enums.js";
const jsonRecordSchema = z.record(z.string(), z.unknown());
const numericRecordSchema = z.record(z.string(), z.number());
const demandLineRateModeSchema = z.enum(["resource", "manual"]);
export const EstimateDemandLineCalculationMetadataSchema = z.object({
costRateMode: demandLineRateModeSchema.default("manual"),
billRateMode: demandLineRateModeSchema.default("manual"),
totalMode: z.literal("computed").default("computed"),
liveCostRateCents: z.number().int().min(0).nullable().optional(),
liveBillRateCents: z.number().int().min(0).nullable().optional(),
liveCurrency: z.string().length(3).nullable().optional(),
});
export const EstimateDemandLineMetadataSchema = z
.object({
calculation: EstimateDemandLineCalculationMetadataSchema.optional(),
})
.catchall(z.unknown());
export const EstimateAssumptionSchema = z.object({
id: z.string().optional(),
category: z.string().min(1).max(100),
key: z.string().min(1).max(100),
label: z.string().min(1).max(200),
valueType: z.string().min(1).max(50).default("json"),
value: z.unknown(),
sortOrder: z.number().int().min(0).default(0),
notes: z.string().max(2_000).optional(),
});
export const ScopeItemSchema = z.object({
id: z.string().optional(),
sequenceNo: z.number().int().min(0),
scopeType: z.string().min(1).max(100),
packageCode: z.string().max(100).optional(),
name: z.string().min(1).max(500),
description: z.string().max(5_000).optional(),
scene: z.string().max(200).optional(),
page: z.string().max(100).optional(),
location: z.string().max(200).optional(),
assumptionCategory: z.string().max(100).optional(),
technicalSpec: jsonRecordSchema.default({}),
frameCount: z.number().int().min(0).optional(),
itemCount: z.number().min(0).optional(),
unitMode: z.string().max(100).optional(),
internalComments: z.string().max(5_000).optional(),
externalComments: z.string().max(5_000).optional(),
sortOrder: z.number().int().min(0).default(0),
metadata: jsonRecordSchema.default({}),
});
export const EstimateDemandLineSchema = z.object({
id: z.string().optional(),
scopeItemId: z.string().optional(),
roleId: z.string().optional(),
resourceId: z.string().optional(),
lineType: z.string().min(1).max(100).default("LABOR"),
name: z.string().min(1).max(500),
chapter: z.string().max(200).optional(),
hours: z.number().min(0),
days: z.number().min(0).optional(),
fte: z.number().min(0).optional(),
rateSource: z.string().max(200).optional(),
costRateCents: z.number().int().min(0).default(0),
billRateCents: z.number().int().min(0).default(0),
currency: z.string().length(3).default("EUR"),
costTotalCents: z.number().int().min(0).default(0),
priceTotalCents: z.number().int().min(0).default(0),
monthlySpread: numericRecordSchema.default({}),
staffingAttributes: jsonRecordSchema.default({}),
metadata: EstimateDemandLineMetadataSchema.default({}),
});
export const ResourceCostSnapshotSchema = z.object({
id: z.string().optional(),
resourceId: z.string().optional(),
sourceEid: z.string().max(100).optional(),
displayName: z.string().min(1).max(500),
chapter: z.string().max(200).optional(),
roleId: z.string().optional(),
currency: z.string().length(3).default("EUR"),
lcrCents: z.number().int().min(0),
ucrCents: z.number().int().min(0),
fte: z.number().min(0).optional(),
location: z.string().max(200).optional(),
country: z.string().max(200).optional(),
level: z.string().max(100).optional(),
workType: z.string().max(100).optional(),
attributes: jsonRecordSchema.default({}),
});
export const EstimateMetricSchema = z.object({
id: z.string().optional(),
key: z.string().min(1).max(100),
label: z.string().min(1).max(200),
metricGroup: z.string().max(100).optional(),
valueDecimal: z.number(),
valueCents: z.number().int().optional(),
currency: z.string().length(3).optional(),
metadata: jsonRecordSchema.default({}),
});
export const EstimateExportSummarySchema = z.object({
estimateId: z.string(),
estimateName: z.string().min(1).max(500),
versionId: z.string(),
versionNumber: z.number().int().min(1),
versionStatus: z.nativeEnum(EstimateVersionStatus),
projectId: z.string().nullable().optional(),
projectName: z.string().nullable().optional(),
baseCurrency: z.string().length(3),
assumptionCount: z.number().int().min(0),
scopeItemCount: z.number().int().min(0),
demandLineCount: z.number().int().min(0),
resourceSnapshotCount: z.number().int().min(0),
totalHours: z.number().min(0),
totalCostCents: z.number().int(),
totalPriceCents: z.number().int(),
marginCents: z.number().int(),
marginPercent: z.number(),
});
export const EstimateExportArtifactPayloadSchema = z.object({
schemaVersion: z.number().int().min(1).default(1),
format: z.nativeEnum(EstimateExportFormat),
mimeType: z.string().min(1).max(200),
encoding: z.enum(["utf8", "base64"]),
fileExtension: z.string().min(1).max(20),
generatedAt: z.string().datetime(),
byteLength: z.number().int().min(0),
rowCount: z.number().int().min(0).nullable().optional(),
lineCount: z.number().int().min(0).nullable().optional(),
sheetNames: z.array(z.string().min(1).max(200)).optional(),
previewText: z.string().nullable().optional(),
content: z.string(),
summary: EstimateExportSummarySchema,
});
export const EstimateExportSchema = z.object({
id: z.string().optional(),
format: z.nativeEnum(EstimateExportFormat),
fileName: z.string().min(1).max(500),
storageKey: z.string().max(500).optional(),
payload: z.union([EstimateExportArtifactPayloadSchema, jsonRecordSchema]).optional(),
});
export const EstimateVersionSchema = z.object({
id: z.string().optional(),
versionNumber: z.number().int().min(1).default(1),
label: z.string().max(200).optional(),
status: z.nativeEnum(EstimateVersionStatus).default(
EstimateVersionStatus.WORKING,
),
notes: z.string().max(5_000).optional(),
lockedAt: z.coerce.date().optional(),
projectSnapshot: jsonRecordSchema.default({}),
assumptions: z.array(EstimateAssumptionSchema).default([]),
scopeItems: z.array(ScopeItemSchema).default([]),
demandLines: z.array(EstimateDemandLineSchema).default([]),
resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
metrics: z.array(EstimateMetricSchema).default([]),
exports: z.array(EstimateExportSchema).default([]),
});
export const CreateEstimateSchema = z.object({
projectId: z.string().optional(),
name: z.string().min(1).max(500),
opportunityId: z.string().max(200).optional(),
baseCurrency: z.string().length(3).default("EUR"),
status: z.nativeEnum(EstimateStatus).default(EstimateStatus.DRAFT),
versionLabel: z.string().max(200).optional(),
versionNotes: z.string().max(5_000).optional(),
assumptions: z.array(EstimateAssumptionSchema).default([]),
scopeItems: z.array(ScopeItemSchema).default([]),
demandLines: z.array(EstimateDemandLineSchema).default([]),
resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
metrics: z.array(EstimateMetricSchema).default([]),
});
export const UpdateEstimateSchema = CreateEstimateSchema.partial();
export const UpdateEstimateDraftSchema = CreateEstimateSchema.partial().extend({
id: z.string(),
assumptions: z.array(EstimateAssumptionSchema).default([]),
scopeItems: z.array(ScopeItemSchema).default([]),
demandLines: z.array(EstimateDemandLineSchema).default([]),
resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
metrics: z.array(EstimateMetricSchema).default([]),
});
export const SubmitEstimateVersionSchema = z.object({
estimateId: z.string(),
versionId: z.string().optional(),
});
export const ApproveEstimateVersionSchema = z.object({
estimateId: z.string(),
versionId: z.string().optional(),
});
export const CreateEstimateRevisionSchema = z.object({
estimateId: z.string(),
sourceVersionId: z.string().optional(),
label: z.string().max(200).optional(),
notes: z.string().max(5_000).optional(),
});
export const CreateEstimateExportSchema = z.object({
estimateId: z.string(),
versionId: z.string().optional(),
format: z.nativeEnum(EstimateExportFormat),
});
export const CreateEstimatePlanningHandoffSchema = z.object({
estimateId: z.string(),
versionId: z.string().optional(),
});
export const CloneEstimateSchema = z.object({
sourceEstimateId: z.string(),
name: z.string().min(1).max(500).optional(),
projectId: z.string().optional(),
});
export const EffortUnitModeSchema = z.enum(["per_frame", "per_item", "flat"]);
export const EffortRuleSchema = z.object({
id: z.string().optional(),
scopeType: z.string().min(1).max(100),
discipline: z.string().min(1).max(200),
chapter: z.string().max(200).optional(),
unitMode: EffortUnitModeSchema,
hoursPerUnit: z.number().min(0),
description: z.string().max(1000).optional(),
sortOrder: z.number().int().min(0).default(0),
});
export const CreateEffortRuleSetSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().max(2000).optional(),
isDefault: z.boolean().default(false),
rules: z.array(EffortRuleSchema).default([]),
});
export const UpdateEffortRuleSetSchema = z.object({
id: z.string(),
name: z.string().min(1).max(200).optional(),
description: z.string().max(2000).optional(),
isDefault: z.boolean().optional(),
rules: z.array(EffortRuleSchema).optional(),
});
export const ApplyEffortRulesSchema = z.object({
estimateId: z.string(),
ruleSetId: z.string(),
mode: z.enum(["replace", "append"]).default("replace"),
});
export type CreateEffortRuleSetInput = z.infer<typeof CreateEffortRuleSetSchema>;
export type UpdateEffortRuleSetInput = z.infer<typeof UpdateEffortRuleSetSchema>;
export type ApplyEffortRulesInput = z.infer<typeof ApplyEffortRulesSchema>;
// ─── Experience Multipliers ──────────────────────────────────────────────────
export const ExperienceMultiplierRuleSchema = z.object({
id: z.string().optional(),
chapter: z.string().max(200).optional(),
location: z.string().max(200).optional(),
level: z.string().max(100).optional(),
costMultiplier: z.number().min(0).default(1.0),
billMultiplier: z.number().min(0).default(1.0),
shoringRatio: z.number().min(0).max(1).optional(),
additionalEffortRatio: z.number().min(0).optional(),
description: z.string().max(1000).optional(),
sortOrder: z.number().int().min(0).default(0),
});
export const CreateExperienceMultiplierSetSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().max(2000).optional(),
isDefault: z.boolean().default(false),
rules: z.array(ExperienceMultiplierRuleSchema).default([]),
});
export const UpdateExperienceMultiplierSetSchema = z.object({
id: z.string(),
name: z.string().min(1).max(200).optional(),
description: z.string().max(2000).optional(),
isDefault: z.boolean().optional(),
rules: z.array(ExperienceMultiplierRuleSchema).optional(),
});
export const ApplyExperienceMultipliersSchema = z.object({
estimateId: z.string(),
multiplierSetId: z.string(),
});
export type CreateExperienceMultiplierSetInput = z.infer<typeof CreateExperienceMultiplierSetSchema>;
export type UpdateExperienceMultiplierSetInput = z.infer<typeof UpdateExperienceMultiplierSetSchema>;
export type ApplyExperienceMultipliersInput = z.infer<typeof ApplyExperienceMultipliersSchema>;
export const EstimateListFiltersSchema = z.object({
projectId: z.string().optional(),
status: z.nativeEnum(EstimateStatus).optional(),
query: z.string().max(200).optional(),
});
export const PhasingPatternSchema = z.enum([
"even",
"front_loaded",
"back_loaded",
"custom",
]);
export const GenerateWeeklyPhasingSchema = z.object({
estimateId: z.string(),
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
pattern: PhasingPatternSchema.default("even"),
});
export const UpdateWeeklyPhasingSchema = z.object({
estimateId: z.string(),
demandLineId: z.string(),
weeklyHours: z.record(z.string(), z.number().min(0)),
});
export type GenerateWeeklyPhasingInput = z.infer<typeof GenerateWeeklyPhasingSchema>;
export type UpdateWeeklyPhasingInput = z.infer<typeof UpdateWeeklyPhasingSchema>;
export type CreateEstimateInput = z.infer<typeof CreateEstimateSchema>;
export type UpdateEstimateInput = z.infer<typeof UpdateEstimateSchema>;
export type UpdateEstimateDraftInput = z.infer<typeof UpdateEstimateDraftSchema>;
export type EstimateListFilters = z.infer<typeof EstimateListFiltersSchema>;
export type SubmitEstimateVersionInput = z.infer<
typeof SubmitEstimateVersionSchema
>;
export type ApproveEstimateVersionInput = z.infer<
typeof ApproveEstimateVersionSchema
>;
export type CreateEstimateRevisionInput = z.infer<
typeof CreateEstimateRevisionSchema
>;
export type CreateEstimateExportInput = z.infer<
typeof CreateEstimateExportSchema
>;
export type CreateEstimatePlanningHandoffInput = z.infer<
typeof CreateEstimatePlanningHandoffSchema
>;
export type CloneEstimateInput = z.infer<typeof CloneEstimateSchema>;