chore(repo): initialize planarchy workspace
This commit is contained in:
@@ -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>;
|
||||
Reference in New Issue
Block a user