VigilanzaTurni/shared/schema.ts
marco370 a5355ed881 Add system to manage different types of security services and their details
Introduce new "serviceTypes" table and CRUD operations in the backend. Update frontend to fetch and display service types dynamically. Modify vehicle assignment logic to handle null guards.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e5565357-90e1-419f-b9a8-6ee8394636df
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e5565357-90e1-419f-b9a8-6ee8394636df/IgrJ2ut
2025-10-17 09:12:29 +00:00

796 lines
27 KiB
TypeScript

import { sql } from "drizzle-orm";
import { relations } from "drizzle-orm";
import {
pgTable,
varchar,
text,
timestamp,
index,
uniqueIndex,
jsonb,
boolean,
integer,
date,
pgEnum,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
// ============= ENUMS =============
export const userRoleEnum = pgEnum("user_role", [
"admin",
"coordinator",
"guard",
"client",
]);
export const shiftTypeEnum = pgEnum("shift_type", [
"fixed_post", // Presidio fisso
"patrol", // Pattugliamento/ronde
"night_inspection", // Ispettorato notturno
"quick_response", // Pronto intervento
]);
export const shiftStatusEnum = pgEnum("shift_status", [
"planned",
"active",
"completed",
"cancelled",
]);
export const certificationStatusEnum = pgEnum("certification_status", [
"valid",
"expiring_soon", // < 30 days
"expired",
]);
export const shiftPreferenceEnum = pgEnum("shift_preference", [
"morning", // 06:00-14:00
"afternoon", // 14:00-22:00
"night", // 22:00-06:00
"any",
]);
export const absenceTypeEnum = pgEnum("absence_type", [
"sick_leave", // Malattia
"vacation", // Ferie
"personal_leave", // Permesso
"injury", // Infortunio
]);
export const trainingStatusEnum = pgEnum("training_status", [
"scheduled",
"completed",
"expired",
"cancelled",
]);
export const sitePreferenceTypeEnum = pgEnum("site_preference_type", [
"preferred", // Continuità - operatore preferito per questo sito
"blacklisted", // Non assegnare mai questo operatore a questo sito
]);
export const vehicleStatusEnum = pgEnum("vehicle_status", [
"available", // Disponibile
"in_use", // In uso
"maintenance", // In manutenzione
"out_of_service", // Fuori servizio
]);
export const vehicleTypeEnum = pgEnum("vehicle_type", [
"car", // Auto
"van", // Furgone
"motorcycle", // Moto
"suv", // SUV
]);
export const locationEnum = pgEnum("location", [
"roccapiemonte", // Sede Roccapiemonte (Salerno)
"milano", // Sede Milano
"roma", // Sede Roma
]);
// ============= SESSION & AUTH TABLES (Replit Auth) =============
// Session storage table - mandatory for Replit Auth
export const sessions = pgTable(
"sessions",
{
sid: varchar("sid").primaryKey(),
sess: jsonb("sess").notNull(),
expire: timestamp("expire").notNull(),
},
(table) => [index("IDX_session_expire").on(table.expire)]
);
// User storage table - mandatory for Replit Auth
export const users = pgTable("users", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
email: varchar("email").unique(),
firstName: varchar("first_name"),
lastName: varchar("last_name"),
profileImageUrl: varchar("profile_image_url"),
passwordHash: varchar("password_hash"), // For local auth - bcrypt hash
role: userRoleEnum("role").notNull().default("guard"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= GUARDS & CERTIFICATIONS =============
export const guards = pgTable("guards", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
userId: varchar("user_id").references(() => users.id),
badgeNumber: varchar("badge_number").notNull().unique(),
phoneNumber: varchar("phone_number"),
location: locationEnum("location").notNull().default("roccapiemonte"), // Sede di appartenenza
// Skills
isArmed: boolean("is_armed").default(false),
hasFireSafety: boolean("has_fire_safety").default(false),
hasFirstAid: boolean("has_first_aid").default(false),
hasDriverLicense: boolean("has_driver_license").default(false),
languages: text("languages").array(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
export const certifications = pgTable("certifications", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
type: varchar("type").notNull(), // porto_armi, medical_exam, training_course
name: varchar("name").notNull(),
issueDate: date("issue_date").notNull(),
expiryDate: date("expiry_date").notNull(),
status: certificationStatusEnum("status").notNull().default("valid"),
documentUrl: varchar("document_url"),
createdAt: timestamp("created_at").defaultNow(),
});
// ============= VEHICLES (PARCO AUTOMEZZI) =============
export const vehicles = pgTable("vehicles", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
licensePlate: varchar("license_plate").notNull().unique(), // Targa
brand: varchar("brand").notNull(), // Marca (es: Fiat, Volkswagen)
model: varchar("model").notNull(), // Modello
vehicleType: vehicleTypeEnum("vehicle_type").notNull(),
year: integer("year"), // Anno immatricolazione
location: locationEnum("location").notNull().default("roccapiemonte"), // Sede di appartenenza
// Assegnazione
assignedGuardId: varchar("assigned_guard_id").references(() => guards.id, { onDelete: "set null" }),
// Stato e manutenzione
status: vehicleStatusEnum("status").notNull().default("available"),
lastMaintenanceDate: date("last_maintenance_date"),
nextMaintenanceDate: date("next_maintenance_date"),
mileage: integer("mileage"), // Chilometraggio
notes: text("notes"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= SERVICE TYPES =============
export const serviceTypes = pgTable("service_types", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
code: varchar("code").notNull().unique(), // fixed_post, patrol, etc.
label: varchar("label").notNull(), // Presidio Fisso, Pattugliamento, etc.
description: text("description"), // Descrizione dettagliata
icon: varchar("icon").notNull().default("Building2"), // Nome icona Lucide
color: varchar("color").notNull().default("blue"), // blue, green, purple, orange
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= SITES & CONTRACTS =============
export const sites = pgTable("sites", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
name: varchar("name").notNull(),
address: varchar("address").notNull(),
clientId: varchar("client_id").references(() => users.id),
location: locationEnum("location").notNull().default("roccapiemonte"), // Sede gestionale
// Service requirements
shiftType: shiftTypeEnum("shift_type").notNull(),
minGuards: integer("min_guards").notNull().default(1),
requiresArmed: boolean("requires_armed").default(false),
requiresDriverLicense: boolean("requires_driver_license").default(false),
// Coordinates for geofencing (future use)
latitude: varchar("latitude"),
longitude: varchar("longitude"),
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= SHIFTS & ASSIGNMENTS =============
export const shifts = pgTable("shifts", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
siteId: varchar("site_id").notNull().references(() => sites.id, { onDelete: "cascade" }),
startTime: timestamp("start_time").notNull(),
endTime: timestamp("end_time").notNull(),
status: shiftStatusEnum("status").notNull().default("planned"),
notes: text("notes"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
export const shiftAssignments = pgTable("shift_assignments", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
shiftId: varchar("shift_id").notNull().references(() => shifts.id, { onDelete: "cascade" }),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
assignedAt: timestamp("assigned_at").defaultNow(),
confirmedAt: timestamp("confirmed_at"),
// Actual check-in/out times
checkInTime: timestamp("check_in_time"),
checkOutTime: timestamp("check_out_time"),
});
// ============= NOTIFICATIONS =============
export const notifications = pgTable("notifications", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
title: varchar("title").notNull(),
message: text("message").notNull(),
type: varchar("type").notNull(), // shift_assigned, certification_expiring, shift_reminder
isRead: boolean("is_read").default(false),
relatedEntityId: varchar("related_entity_id"), // shift_id, certification_id, etc.
createdAt: timestamp("created_at").defaultNow(),
});
// ============= GUARD CONSTRAINTS & PREFERENCES =============
export const guardConstraints = pgTable("guard_constraints", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }).unique(),
// Preferenze turni
preferredShiftType: shiftPreferenceEnum("preferred_shift_type").default("any"),
// Limiti contrattuali personalizzati (se diversi dal CCNL base)
maxHoursPerDay: integer("max_hours_per_day").default(10), // 8 + 2 straordinario
maxHoursPerWeek: integer("max_hours_per_week").default(48), // 40 + 8 straordinario
// Giorni riposo preferiti (1=Lunedì...7=Domenica)
preferredDaysOff: integer("preferred_days_off").array(),
// Disponibilità festività
availableOnHolidays: boolean("available_on_holidays").default(true),
// Note aggiuntive
notes: text("notes"),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= SITE PREFERENCES =============
export const sitePreferences = pgTable("site_preferences", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
siteId: varchar("site_id").notNull().references(() => sites.id, { onDelete: "cascade" }),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
// Preferenza: preferred = continuità, blacklisted = non assegnare mai
preference: sitePreferenceTypeEnum("preference").notNull(),
priority: integer("priority").default(0), // Più alto = più preferito
reason: text("reason"), // Motivazione preferenza/blacklist
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
// Unique constraint: una sola preferenza per coppia sito-guardia
uniqueSiteGuard: uniqueIndex("unique_site_guard_preference").on(table.siteId, table.guardId),
}));
// ============= CONTRACT PARAMETERS (CCNL) =============
export const contractParameters = pgTable("contract_parameters", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
// Identificatore tipo contratto (default = CCNL Vigilanza Privata)
contractType: varchar("contract_type").notNull().unique().default("CCNL_VIGILANZA_2023"),
// Limiti orari
maxHoursPerDay: integer("max_hours_per_day").notNull().default(8),
maxOvertimePerDay: integer("max_overtime_per_day").notNull().default(2),
maxHoursPerWeek: integer("max_hours_per_week").notNull().default(40),
maxOvertimePerWeek: integer("max_overtime_per_week").notNull().default(8),
// Riposi obbligatori
minDailyRestHours: integer("min_daily_rest_hours").notNull().default(11),
minDailyRestHoursReduced: integer("min_daily_rest_hours_reduced").notNull().default(9), // Deroga CCNL
maxDailyRestReductionsPerMonth: integer("max_daily_rest_reductions_per_month").notNull().default(3),
maxDailyRestReductionsPerYear: integer("max_daily_rest_reductions_per_year").notNull().default(12),
minWeeklyRestHours: integer("min_weekly_rest_hours").notNull().default(24),
// Pause obbligatorie
pauseMinutesIfOver6Hours: integer("pause_minutes_if_over_6_hours").notNull().default(10),
// Buoni pasto
mealVoucherEnabled: boolean("meal_voucher_enabled").default(true), // Buoni pasto attivi
mealVoucherAfterHours: integer("meal_voucher_after_hours").default(6), // Ore minime per diritto buono pasto
mealVoucherAmount: integer("meal_voucher_amount").default(8), // Importo buono pasto in euro (facoltativo)
// Limiti notturni (22:00-06:00)
maxNightHoursPerWeek: integer("max_night_hours_per_week").default(48),
// Maggiorazioni (percentuali)
holidayPayIncrease: integer("holiday_pay_increase").default(30), // +30% festivi
nightPayIncrease: integer("night_pay_increase").default(20), // +20% notturni
overtimePayIncrease: integer("overtime_pay_increase").default(15), // +15% straordinari
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// ============= TRAINING & COURSES =============
export const trainingCourses = pgTable("training_courses", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
courseName: varchar("course_name").notNull(),
courseType: varchar("course_type").notNull(), // "mandatory" | "optional"
scheduledDate: date("scheduled_date"),
completionDate: date("completion_date"),
expiryDate: date("expiry_date"), // Per corsi con scadenza (es. primo soccorso)
status: trainingStatusEnum("status").notNull().default("scheduled"),
certificateUrl: varchar("certificate_url"),
provider: varchar("provider"), // Ente formatore
hours: integer("hours"), // Ore corso
notes: text("notes"),
createdAt: timestamp("created_at").defaultNow(),
});
// ============= HOLIDAYS =============
export const holidays = pgTable("holidays", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
name: varchar("name").notNull(), // "Natale", "Capodanno", "1 Maggio", etc.
date: date("date").notNull(),
year: integer("year").notNull(), // Anno specifico per tracking rotazioni
isNational: boolean("is_national").default(true), // Nazionale o regionale
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
// Unique constraint: una festività per data e anno
uniqueDateYear: uniqueIndex("unique_holiday_date_year").on(table.date, table.year),
}));
// Join table per tracking rotazioni festività con integrità referenziale
export const holidayAssignments = pgTable("holiday_assignments", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
holidayId: varchar("holiday_id").notNull().references(() => holidays.id, { onDelete: "cascade" }),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
shiftId: varchar("shift_id").references(() => shifts.id, { onDelete: "set null" }), // Turno specifico lavorato
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
// Unique constraint: una guardia non può essere assegnata due volte alla stessa festività
uniqueHolidayGuard: uniqueIndex("unique_holiday_guard").on(table.holidayId, table.guardId),
}));
// ============= ABSENCES & SUBSTITUTIONS =============
export const absences = pgTable("absences", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
guardId: varchar("guard_id").notNull().references(() => guards.id, { onDelete: "cascade" }),
type: absenceTypeEnum("type").notNull(),
startDate: date("start_date").notNull(),
endDate: date("end_date").notNull(),
// Documentazione
certificateUrl: varchar("certificate_url"), // Certificato medico, etc.
notes: text("notes"),
// Se approvata e serve sostituto
isApproved: boolean("is_approved").default(false),
needsSubstitute: boolean("needs_substitute").default(true),
substituteGuardId: varchar("substitute_guard_id").references(() => guards.id),
createdAt: timestamp("created_at").defaultNow(),
approvedAt: timestamp("approved_at"),
approvedBy: varchar("approved_by").references(() => users.id),
});
// Join table per tracking turni impattati da assenze (per sistema sostituzione)
export const absenceAffectedShifts = pgTable("absence_affected_shifts", {
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
absenceId: varchar("absence_id").notNull().references(() => absences.id, { onDelete: "cascade" }),
shiftId: varchar("shift_id").notNull().references(() => shifts.id, { onDelete: "cascade" }),
// Se sostituto trovato
isSubstituted: boolean("is_substituted").default(false),
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
// Unique: uno shift non può essere impattato due volte dalla stessa assenza
uniqueAbsenceShift: uniqueIndex("unique_absence_shift").on(table.absenceId, table.shiftId),
}));
// ============= RELATIONS =============
export const usersRelations = relations(users, ({ one, many }) => ({
guard: one(guards, {
fields: [users.id],
references: [guards.userId],
}),
managedSites: many(sites),
notifications: many(notifications),
}));
export const guardsRelations = relations(guards, ({ one, many }) => ({
user: one(users, {
fields: [guards.userId],
references: [users.id],
}),
certifications: many(certifications),
shiftAssignments: many(shiftAssignments),
constraints: one(guardConstraints),
sitePreferences: many(sitePreferences),
trainingCourses: many(trainingCourses),
absences: many(absences),
assignedVehicles: many(vehicles),
}));
export const vehiclesRelations = relations(vehicles, ({ one }) => ({
assignedGuard: one(guards, {
fields: [vehicles.assignedGuardId],
references: [guards.id],
}),
}));
export const certificationsRelations = relations(certifications, ({ one }) => ({
guard: one(guards, {
fields: [certifications.guardId],
references: [guards.id],
}),
}));
export const sitesRelations = relations(sites, ({ one, many }) => ({
client: one(users, {
fields: [sites.clientId],
references: [users.id],
}),
shifts: many(shifts),
preferences: many(sitePreferences),
}));
export const shiftsRelations = relations(shifts, ({ one, many }) => ({
site: one(sites, {
fields: [shifts.siteId],
references: [sites.id],
}),
assignments: many(shiftAssignments),
}));
export const shiftAssignmentsRelations = relations(shiftAssignments, ({ one }) => ({
shift: one(shifts, {
fields: [shiftAssignments.shiftId],
references: [shifts.id],
}),
guard: one(guards, {
fields: [shiftAssignments.guardId],
references: [guards.id],
}),
}));
export const notificationsRelations = relations(notifications, ({ one }) => ({
user: one(users, {
fields: [notifications.userId],
references: [users.id],
}),
}));
export const guardConstraintsRelations = relations(guardConstraints, ({ one }) => ({
guard: one(guards, {
fields: [guardConstraints.guardId],
references: [guards.id],
}),
}));
export const sitePreferencesRelations = relations(sitePreferences, ({ one }) => ({
site: one(sites, {
fields: [sitePreferences.siteId],
references: [sites.id],
}),
guard: one(guards, {
fields: [sitePreferences.guardId],
references: [guards.id],
}),
}));
export const trainingCoursesRelations = relations(trainingCourses, ({ one }) => ({
guard: one(guards, {
fields: [trainingCourses.guardId],
references: [guards.id],
}),
}));
export const absencesRelations = relations(absences, ({ one, many }) => ({
guard: one(guards, {
fields: [absences.guardId],
references: [guards.id],
}),
substituteGuard: one(guards, {
fields: [absences.substituteGuardId],
references: [guards.id],
}),
approver: one(users, {
fields: [absences.approvedBy],
references: [users.id],
}),
affectedShifts: many(absenceAffectedShifts),
}));
export const absenceAffectedShiftsRelations = relations(absenceAffectedShifts, ({ one }) => ({
absence: one(absences, {
fields: [absenceAffectedShifts.absenceId],
references: [absences.id],
}),
shift: one(shifts, {
fields: [absenceAffectedShifts.shiftId],
references: [shifts.id],
}),
}));
export const holidaysRelations = relations(holidays, ({ many }) => ({
assignments: many(holidayAssignments),
}));
export const holidayAssignmentsRelations = relations(holidayAssignments, ({ one }) => ({
holiday: one(holidays, {
fields: [holidayAssignments.holidayId],
references: [holidays.id],
}),
guard: one(guards, {
fields: [holidayAssignments.guardId],
references: [guards.id],
}),
shift: one(shifts, {
fields: [holidayAssignments.shiftId],
references: [shifts.id],
}),
}));
// ============= INSERT SCHEMAS =============
export const insertUserSchema = createInsertSchema(users).pick({
email: true,
firstName: true,
lastName: true,
profileImageUrl: true,
passwordHash: true,
role: true,
});
// Form schema with plain password (will be hashed on backend)
export const insertUserFormSchema = z.object({
email: z.string().email("Email non valida"),
firstName: z.string().min(1, "Nome obbligatorio"),
lastName: z.string().min(1, "Cognome obbligatorio"),
password: z.string().min(6, "Password minimo 6 caratteri"),
role: z.enum(["admin", "coordinator", "guard", "client"]).default("guard"),
});
// Update user form schema (password optional)
export const updateUserFormSchema = z.object({
email: z.string().email("Email non valida").optional(),
firstName: z.string().min(1, "Nome obbligatorio").optional(),
lastName: z.string().min(1, "Cognome obbligatorio").optional(),
password: z.string().min(6, "Password minimo 6 caratteri").optional(),
role: z.enum(["admin", "coordinator", "guard", "client"]).optional(),
});
export const insertGuardSchema = createInsertSchema(guards).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertCertificationSchema = createInsertSchema(certifications).omit({
id: true,
createdAt: true,
status: true,
});
export const insertVehicleSchema = createInsertSchema(vehicles).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertServiceTypeSchema = createInsertSchema(serviceTypes).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertSiteSchema = createInsertSchema(sites).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertShiftSchema = createInsertSchema(shifts).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Form schema that accepts datetime strings and transforms to Date
export const insertShiftFormSchema = z.object({
siteId: z.string().min(1, "Sito obbligatorio"),
startTime: z.string().min(1, "Data inizio obbligatoria").refine((val) => !isNaN(new Date(val).getTime()), {
message: "Data inizio non valida",
}).transform((val) => new Date(val)),
endTime: z.string().min(1, "Data fine obbligatoria").refine((val) => !isNaN(new Date(val).getTime()), {
message: "Data fine non valida",
}).transform((val) => new Date(val)),
status: z.enum(["planned", "active", "completed", "cancelled"]).default("planned"),
});
export const insertShiftAssignmentSchema = createInsertSchema(shiftAssignments).omit({
id: true,
assignedAt: true,
});
export const insertNotificationSchema = createInsertSchema(notifications).omit({
id: true,
createdAt: true,
});
export const insertGuardConstraintsSchema = createInsertSchema(guardConstraints).omit({
id: true,
updatedAt: true,
});
export const insertSitePreferenceSchema = createInsertSchema(sitePreferences).omit({
id: true,
createdAt: true,
});
export const insertContractParametersSchema = createInsertSchema(contractParameters).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertTrainingCourseSchema = createInsertSchema(trainingCourses).omit({
id: true,
createdAt: true,
});
export const insertHolidaySchema = createInsertSchema(holidays).omit({
id: true,
createdAt: true,
});
export const insertAbsenceSchema = createInsertSchema(absences).omit({
id: true,
createdAt: true,
approvedAt: true,
});
export const insertHolidayAssignmentSchema = createInsertSchema(holidayAssignments).omit({
id: true,
createdAt: true,
});
export const insertAbsenceAffectedShiftSchema = createInsertSchema(absenceAffectedShifts).omit({
id: true,
createdAt: true,
});
// ============= TYPES =============
export type UpsertUser = typeof users.$inferInsert;
export type User = typeof users.$inferSelect;
export type InsertGuard = z.infer<typeof insertGuardSchema>;
export type Guard = typeof guards.$inferSelect;
export type InsertCertification = z.infer<typeof insertCertificationSchema>;
export type Certification = typeof certifications.$inferSelect;
export type InsertVehicle = z.infer<typeof insertVehicleSchema>;
export type Vehicle = typeof vehicles.$inferSelect;
export type InsertServiceType = z.infer<typeof insertServiceTypeSchema>;
export type ServiceType = typeof serviceTypes.$inferSelect;
export type InsertSite = z.infer<typeof insertSiteSchema>;
export type Site = typeof sites.$inferSelect;
export type InsertShift = z.infer<typeof insertShiftSchema>;
export type Shift = typeof shifts.$inferSelect;
export type InsertShiftAssignment = z.infer<typeof insertShiftAssignmentSchema>;
export type ShiftAssignment = typeof shiftAssignments.$inferSelect;
export type InsertNotification = z.infer<typeof insertNotificationSchema>;
export type Notification = typeof notifications.$inferSelect;
export type InsertGuardConstraints = z.infer<typeof insertGuardConstraintsSchema>;
export type GuardConstraints = typeof guardConstraints.$inferSelect;
export type InsertSitePreference = z.infer<typeof insertSitePreferenceSchema>;
export type SitePreference = typeof sitePreferences.$inferSelect;
export type InsertContractParameters = z.infer<typeof insertContractParametersSchema>;
export type ContractParameters = typeof contractParameters.$inferSelect;
export type InsertTrainingCourse = z.infer<typeof insertTrainingCourseSchema>;
export type TrainingCourse = typeof trainingCourses.$inferSelect;
export type InsertHoliday = z.infer<typeof insertHolidaySchema>;
export type Holiday = typeof holidays.$inferSelect;
export type InsertAbsence = z.infer<typeof insertAbsenceSchema>;
export type Absence = typeof absences.$inferSelect;
export type InsertHolidayAssignment = z.infer<typeof insertHolidayAssignmentSchema>;
export type HolidayAssignment = typeof holidayAssignments.$inferSelect;
export type InsertAbsenceAffectedShift = z.infer<typeof insertAbsenceAffectedShiftSchema>;
export type AbsenceAffectedShift = typeof absenceAffectedShifts.$inferSelect;
// ============= EXTENDED TYPES FOR FRONTEND =============
export type GuardWithCertifications = Guard & {
certifications: Certification[];
user?: User;
};
export type ShiftWithDetails = Shift & {
site: Site;
assignments: (ShiftAssignment & {
guard: GuardWithCertifications;
})[];
};
export type SiteWithShifts = Site & {
shifts: Shift[];
client?: User;
};
export type GuardWithDetails = Guard & {
user?: User;
certifications: Certification[];
constraints?: GuardConstraints;
trainingCourses: TrainingCourse[];
absences: Absence[];
};
export type AbsenceWithDetails = Absence & {
guard: Guard;
substituteGuard?: Guard;
approver?: User;
affectedShifts: (AbsenceAffectedShift & {
shift: Shift;
})[];
};