import { sql } from "drizzle-orm"; import { relations } from "drizzle-orm"; import { pgTable, varchar, text, timestamp, index, 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", ]); // ============= 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"), 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"), // 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(), }); // ============= 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), // 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(), }); // ============= 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), })); 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), })); 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], }), })); // ============= INSERT SCHEMAS ============= export const insertUserSchema = createInsertSchema(users).pick({ email: true, firstName: true, lastName: true, profileImageUrl: true, role: true, }); 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 insertSiteSchema = createInsertSchema(sites).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertShiftSchema = createInsertSchema(shifts).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertShiftAssignmentSchema = createInsertSchema(shiftAssignments).omit({ id: true, assignedAt: true, }); export const insertNotificationSchema = createInsertSchema(notifications).omit({ id: true, createdAt: true, }); // ============= TYPES ============= export type UpsertUser = typeof users.$inferInsert; export type User = typeof users.$inferSelect; export type InsertGuard = z.infer; export type Guard = typeof guards.$inferSelect; export type InsertCertification = z.infer; export type Certification = typeof certifications.$inferSelect; export type InsertSite = z.infer; export type Site = typeof sites.$inferSelect; export type InsertShift = z.infer; export type Shift = typeof shifts.$inferSelect; export type InsertShiftAssignment = z.infer; export type ShiftAssignment = typeof shiftAssignments.$inferSelect; export type InsertNotification = z.infer; export type Notification = typeof notifications.$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; };