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
This commit is contained in:
parent
133be08785
commit
177ad892f0
4
.replit
4
.replit
@ -22,6 +22,10 @@ externalPort = 3001
|
|||||||
localPort = 41343
|
localPort = 41343
|
||||||
externalPort = 3000
|
externalPort = 3000
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 45115
|
||||||
|
externalPort = 3002
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
PORT = "5000"
|
PORT = "5000"
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
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 { Plus, Calendar, MapPin, Users, Clock } from "lucide-react";
|
||||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
@ -29,16 +29,13 @@ export default function Shifts() {
|
|||||||
queryKey: ["/api/sites"],
|
queryKey: ["/api/sites"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<InsertShift>({
|
const form = useForm({
|
||||||
resolver: zodResolver(insertShiftSchema.extend({
|
resolver: zodResolver(insertShiftFormSchema),
|
||||||
startTime: insertShiftSchema.shape.startTime,
|
|
||||||
endTime: insertShiftSchema.shape.endTime,
|
|
||||||
})),
|
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
siteId: "",
|
siteId: "",
|
||||||
startTime: new Date(),
|
startTime: "",
|
||||||
endTime: new Date(),
|
endTime: "",
|
||||||
status: "planned",
|
status: "planned" as const,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,13 +62,7 @@ export default function Shifts() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (data: InsertShift) => {
|
const onSubmit = (data: InsertShift) => {
|
||||||
// Ensure dates are Date objects, not strings
|
createMutation.mutate(data);
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusLabel = (status: string) => {
|
const getStatusLabel = (status: string) => {
|
||||||
@ -155,8 +146,8 @@ export default function Shifts() {
|
|||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm"
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm"
|
||||||
value={field.value ? format(new Date(field.value), "yyyy-MM-dd'T'HH:mm") : ""}
|
value={field.value}
|
||||||
onChange={(e) => field.onChange(new Date(e.target.value))}
|
onChange={(e) => field.onChange(e.target.value)}
|
||||||
data-testid="input-start-time"
|
data-testid="input-start-time"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -175,8 +166,8 @@ export default function Shifts() {
|
|||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm"
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm"
|
||||||
value={field.value ? format(new Date(field.value), "yyyy-MM-dd'T'HH:mm") : ""}
|
value={field.value}
|
||||||
onChange={(e) => field.onChange(new Date(e.target.value))}
|
onChange={(e) => field.onChange(e.target.value)}
|
||||||
data-testid="input-end-time"
|
data-testid="input-end-time"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { createServer, type Server } from "http";
|
|||||||
import { storage } from "./storage";
|
import { storage } from "./storage";
|
||||||
import { setupAuth, isAuthenticated } from "./replitAuth";
|
import { setupAuth, isAuthenticated } from "./replitAuth";
|
||||||
import { db } from "./db";
|
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 { eq } from "drizzle-orm";
|
||||||
import { differenceInDays } from "date-fns";
|
import { differenceInDays } from "date-fns";
|
||||||
|
|
||||||
@ -197,7 +197,28 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
app.post("/api/shifts", isAuthenticated, async (req, res) => {
|
app.post("/api/shifts", isAuthenticated, async (req, res) => {
|
||||||
try {
|
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);
|
res.json(shift);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating shift:", error);
|
console.error("Error creating shift:", error);
|
||||||
|
|||||||
@ -262,6 +262,18 @@ export const insertShiftSchema = createInsertSchema(shifts).omit({
|
|||||||
updatedAt: 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({
|
export const insertShiftAssignmentSchema = createInsertSchema(shiftAssignments).omit({
|
||||||
id: true,
|
id: true,
|
||||||
assignedAt: true,
|
assignedAt: true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user