From 177ad892f0e989b3dbe6d5c7e6434a1641a709f5 Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Sat, 11 Oct 2025 10:31:19 +0000 Subject: [PATCH] Update shift creation to use a new form schema and improve validation Refactor shift creation endpoint to use `insertShiftFormSchema` for validation and data transformation, and update client-side components to handle datetime strings. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 99f0fce6-9386-489a-9632-1d81223cab44 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/99f0fce6-9386-489a-9632-1d81223cab44/cpTvSfP --- .replit | 4 ++++ client/src/pages/shifts.tsx | 31 +++++++++++-------------------- server/routes.ts | 25 +++++++++++++++++++++++-- shared/schema.ts | 12 ++++++++++++ 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/.replit b/.replit index 03709f0..880652b 100644 --- a/.replit +++ b/.replit @@ -22,6 +22,10 @@ externalPort = 3001 localPort = 41343 externalPort = 3000 +[[ports]] +localPort = 45115 +externalPort = 3002 + [env] PORT = "5000" diff --git a/client/src/pages/shifts.tsx b/client/src/pages/shifts.tsx index 3ceb15f..ad7da5a 100644 --- a/client/src/pages/shifts.tsx +++ b/client/src/pages/shifts.tsx @@ -8,7 +8,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { insertShiftSchema } from "@shared/schema"; +import { insertShiftFormSchema } from "@shared/schema"; import { Plus, Calendar, MapPin, Users, Clock } from "lucide-react"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; @@ -29,16 +29,13 @@ export default function Shifts() { queryKey: ["/api/sites"], }); - const form = useForm({ - resolver: zodResolver(insertShiftSchema.extend({ - startTime: insertShiftSchema.shape.startTime, - endTime: insertShiftSchema.shape.endTime, - })), + const form = useForm({ + resolver: zodResolver(insertShiftFormSchema), defaultValues: { siteId: "", - startTime: new Date(), - endTime: new Date(), - status: "planned", + startTime: "", + endTime: "", + status: "planned" as const, }, }); @@ -65,13 +62,7 @@ export default function Shifts() { }); const onSubmit = (data: InsertShift) => { - // Ensure dates are Date objects, not strings - const shiftData = { - ...data, - startTime: data.startTime instanceof Date ? data.startTime : new Date(data.startTime), - endTime: data.endTime instanceof Date ? data.endTime : new Date(data.endTime), - }; - createMutation.mutate(shiftData); + createMutation.mutate(data); }; const getStatusLabel = (status: string) => { @@ -155,8 +146,8 @@ export default function Shifts() { field.onChange(new Date(e.target.value))} + value={field.value} + onChange={(e) => field.onChange(e.target.value)} data-testid="input-start-time" /> @@ -175,8 +166,8 @@ export default function Shifts() { field.onChange(new Date(e.target.value))} + value={field.value} + onChange={(e) => field.onChange(e.target.value)} data-testid="input-end-time" /> diff --git a/server/routes.ts b/server/routes.ts index 3ab82c1..5083cbe 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -3,7 +3,7 @@ import { createServer, type Server } from "http"; import { storage } from "./storage"; import { setupAuth, isAuthenticated } from "./replitAuth"; import { db } from "./db"; -import { guards, certifications, sites, shifts, shiftAssignments, users } from "@shared/schema"; +import { guards, certifications, sites, shifts, shiftAssignments, users, insertShiftSchema } from "@shared/schema"; import { eq } from "drizzle-orm"; import { differenceInDays } from "date-fns"; @@ -197,7 +197,28 @@ export async function registerRoutes(app: Express): Promise { app.post("/api/shifts", isAuthenticated, async (req, res) => { try { - const shift = await storage.createShift(req.body); + // Validate that required fields are present and dates are valid strings + if (!req.body.siteId || !req.body.startTime || !req.body.endTime) { + return res.status(400).json({ message: "Missing required fields" }); + } + + // Convert and validate dates + const startTime = new Date(req.body.startTime); + const endTime = new Date(req.body.endTime); + + if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) { + return res.status(400).json({ message: "Invalid date format" }); + } + + // Validate and transform the request body + const validatedData = insertShiftSchema.parse({ + siteId: req.body.siteId, + startTime, + endTime, + status: req.body.status || "planned", + }); + + const shift = await storage.createShift(validatedData); res.json(shift); } catch (error) { console.error("Error creating shift:", error); diff --git a/shared/schema.ts b/shared/schema.ts index 4fc004f..cb1f89b 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -262,6 +262,18 @@ export const insertShiftSchema = createInsertSchema(shifts).omit({ 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,