feat(planning): ship holiday-aware planning and assistant upgrades

This commit is contained in:
2026-03-28 22:49:28 +01:00
parent 2a005794e7
commit 4f48afe7b4
151 changed files with 17738 additions and 1940 deletions
+15 -11
View File
@@ -36,40 +36,44 @@ function clamp(value: number, min: number, max: number): number {
const dashboardWidgetTypeSchema = z.enum(DASHBOARD_WIDGET_TYPES);
const resourceTableWidgetConfigSchema = z.object({
const widgetChromeConfigSchema = z.object({
showDetails: z.boolean().optional(),
});
const resourceTableWidgetConfigSchema = widgetChromeConfigSchema.extend({
chapter: z.preprocess(toNonEmptyString, z.string().optional()),
});
const projectTableWidgetConfigSchema = z.object({
const projectTableWidgetConfigSchema = widgetChromeConfigSchema.extend({
search: z.preprocess(toNonEmptyString, z.string().optional()),
status: z.nativeEnum(ProjectStatus).optional(),
});
const peakTimesWidgetConfigSchema = z.object({
const peakTimesWidgetConfigSchema = widgetChromeConfigSchema.extend({
granularity: z.enum(["week", "month"]).optional(),
groupBy: z.enum(["project", "chapter", "resource"]).optional(),
});
const demandWidgetConfigSchema = z.object({
const demandWidgetConfigSchema = widgetChromeConfigSchema.extend({
groupBy: z.enum(["project", "person", "chapter"]).optional(),
});
const topValueWidgetConfigSchema = z.object({
const topValueWidgetConfigSchema = widgetChromeConfigSchema.extend({
limit: z.number().int().min(1).max(100).optional(),
});
const chargeabilityWidgetConfigSchema = z.object({
const chargeabilityWidgetConfigSchema = widgetChromeConfigSchema.extend({
topN: z.number().int().min(1).max(100).optional(),
watchlistThreshold: z.number().int().min(0).max(100).optional(),
});
const myProjectsWidgetConfigSchema = z.object({
const myProjectsWidgetConfigSchema = widgetChromeConfigSchema.extend({
showFavorites: z.boolean().optional(),
showResponsible: z.boolean().optional(),
});
export const dashboardWidgetConfigSchemas = {
"stat-cards": z.object({}),
"stat-cards": widgetChromeConfigSchema,
"resource-table": resourceTableWidgetConfigSchema,
"project-table": projectTableWidgetConfigSchema,
"peak-times-chart": peakTimesWidgetConfigSchema,
@@ -77,9 +81,9 @@ export const dashboardWidgetConfigSchemas = {
"top-value-resources": topValueWidgetConfigSchema,
"chargeability-overview": chargeabilityWidgetConfigSchema,
"my-projects": myProjectsWidgetConfigSchema,
"budget-forecast": z.object({}),
"skill-gap": z.object({}),
"project-health": z.object({}),
"budget-forecast": widgetChromeConfigSchema,
"skill-gap": widgetChromeConfigSchema,
"project-health": widgetChromeConfigSchema,
} as const;
type DashboardWidgetConfigSchemaMap = typeof dashboardWidgetConfigSchemas;
@@ -0,0 +1,50 @@
import { z } from "zod";
export const HolidayCalendarScopeSchema = z.enum(["COUNTRY", "STATE", "CITY"]);
export const CreateHolidayCalendarSchema = z.object({
name: z.string().min(1).max(120),
scopeType: HolidayCalendarScopeSchema,
countryId: z.string(),
stateCode: z.string().trim().min(1).max(16).optional(),
metroCityId: z.string().optional(),
isActive: z.boolean().optional(),
priority: z.number().int().min(-100).max(100).optional(),
});
export const UpdateHolidayCalendarSchema = z.object({
name: z.string().min(1).max(120).optional(),
stateCode: z.string().trim().min(1).max(16).nullable().optional(),
metroCityId: z.string().nullable().optional(),
isActive: z.boolean().optional(),
priority: z.number().int().min(-100).max(100).optional(),
});
export const CreateHolidayCalendarEntrySchema = z.object({
holidayCalendarId: z.string(),
date: z.coerce.date(),
name: z.string().min(1).max(120),
isRecurringAnnual: z.boolean().optional(),
source: z.string().max(120).optional(),
});
export const UpdateHolidayCalendarEntrySchema = z.object({
date: z.coerce.date().optional(),
name: z.string().min(1).max(120).optional(),
isRecurringAnnual: z.boolean().optional(),
source: z.string().max(120).nullable().optional(),
});
export const PreviewResolvedHolidaysSchema = z.object({
countryId: z.string(),
stateCode: z.string().trim().min(1).max(16).optional(),
metroCityId: z.string().optional(),
year: z.number().int().min(2000).max(2100),
});
export type HolidayCalendarScopeInput = z.infer<typeof HolidayCalendarScopeSchema>;
export type CreateHolidayCalendarInput = z.infer<typeof CreateHolidayCalendarSchema>;
export type UpdateHolidayCalendarInput = z.infer<typeof UpdateHolidayCalendarSchema>;
export type CreateHolidayCalendarEntryInput = z.infer<typeof CreateHolidayCalendarEntrySchema>;
export type UpdateHolidayCalendarEntryInput = z.infer<typeof UpdateHolidayCalendarEntrySchema>;
export type PreviewResolvedHolidaysInput = z.infer<typeof PreviewResolvedHolidaysSchema>;
+1
View File
@@ -7,6 +7,7 @@ export * from "./role.schema.js";
export * from "./dashboard.schema.js";
export * from "./estimate.schema.js";
export * from "./country.schema.js";
export * from "./holiday-calendar.schema.js";
export * from "./org-unit.schema.js";
export * from "./utilization-category.schema.js";
export * from "./client.schema.js";