Skip to content

Commit

Permalink
Refactor consultant settings route to validate and handle schedule ty…
Browse files Browse the repository at this point in the history
…pe and availability slots
  • Loading branch information
teetangh committed Nov 24, 2024
1 parent 4da37b3 commit c7e615c
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 45 deletions.
102 changes: 65 additions & 37 deletions app/api/user/consultants/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,72 @@ const dateTimeSchema = z.string().datetime({ offset: true });

// Zod schema for weekly slot
const weeklySlotSchema = z.object({
dayOfWeekforStartTimeInUTC: z.enum(['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']),
dayOfWeekforEndTimeInUTC: z.enum(['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']),
dayOfWeekforStartTimeInUTC: z.enum([
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY",
]),
dayOfWeekforEndTimeInUTC: z.enum([
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY",
]),
slotStartTimeInUTC: dateTimeSchema,
slotEndTimeInUTC: dateTimeSchema
slotEndTimeInUTC: dateTimeSchema,
});

// Zod schema for custom slot
const customSlotSchema = z.object({
slotStartTimeInUTC: dateTimeSchema,
slotEndTimeInUTC: dateTimeSchema
slotEndTimeInUTC: dateTimeSchema,
});

// Main request body schema
const updateConsultantSchema = z.object({
description: z.string().optional(),
qualifications: z.string().optional(),
specialization: z.string().optional(),
experience: z.string().optional(),
scheduleType: z.enum(['WEEKLY', 'CUSTOM']),
language: z.string().optional(),
level: z.string().optional(),
prerequisites: z.string().optional(),
domainId: uuidSchema,
subDomainIds: z.array(uuidSchema),
tagIds: z.array(uuidSchema),
slotsOfAvailabilityWeekly: z.array(weeklySlotSchema).optional(),
slotsOfAvailabilityCustom: z.array(customSlotSchema).optional()
}).refine(
(data) => {
if (data.scheduleType === 'WEEKLY') {
return data.slotsOfAvailabilityWeekly && data.slotsOfAvailabilityWeekly.length > 0;
}
if (data.scheduleType === 'CUSTOM') {
return data.slotsOfAvailabilityCustom && data.slotsOfAvailabilityCustom.length > 0;
}
return false;
},
{
message: "Must provide corresponding slots array based on scheduleType",
path: ["scheduleType"]
}
);
const updateConsultantSchema = z
.object({
description: z.string().optional(),
qualifications: z.string().optional(),
specialization: z.string().optional(),
experience: z.string().optional(),
scheduleType: z.enum(["WEEKLY", "CUSTOM"]),
language: z.string().optional(),
level: z.string().optional(),
prerequisites: z.string().optional(),
domainId: uuidSchema,
subDomainIds: z.array(uuidSchema),
tagIds: z.array(uuidSchema),
slotsOfAvailabilityWeekly: z.array(weeklySlotSchema).optional(),
slotsOfAvailabilityCustom: z.array(customSlotSchema).optional(),
})
.refine(
(data) => {
if (data.scheduleType === "WEEKLY") {
return (
data.slotsOfAvailabilityWeekly &&
data.slotsOfAvailabilityWeekly.length > 0
);
}
if (data.scheduleType === "CUSTOM") {
return (
data.slotsOfAvailabilityCustom &&
data.slotsOfAvailabilityCustom.length > 0
);
}
return false;
},
{
message: "Must provide corresponding slots array based on scheduleType",
path: ["scheduleType"],
},
);

export async function GET(
request: NextRequest,
Expand Down Expand Up @@ -116,20 +140,24 @@ export async function PUT(
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { id } = await params;
const requestData = await request.json();

// Validate request body using zod schema
const validationResult = updateConsultantSchema.safeParse(requestData);
if (!validationResult.success) {
return NextResponse.json(
{ error: "Validation failed", details: validationResult.error.format() },
{ status: 400 }
{
error: "Validation failed",
details: validationResult.error.format(),
},
{ status: 400 },
);
}

const data = validationResult.data;
console.log("Request data:", data);
const {
description,
qualifications,
Expand Down
54 changes: 46 additions & 8 deletions app/dashboard/consultant/[consultantId]/components/SettingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import {
getInitialServiceSettings,
validateSlot,
validateAllSlots,
updateConsultantSettings,
getDaysInMonth,
getFirstDayOfMonth,
getMonthYearString,
getLocalDateString,
formatSlotsForApi,
type FormData,
type SlotType,
type SlotsType,
Expand Down Expand Up @@ -205,6 +205,22 @@ export function SettingsTab({ consultant }: SettingsTabProps) {
}));
}, []);

// Update schedule type and clear irrelevant slots
const handleScheduleTypeChange = useCallback((value: ScheduleType) => {
setScheduleType(value);
setFormData((prev) => ({
...prev,
scheduleType: value,
}));

// Clear slots for the inactive schedule type
if (value === ScheduleType.WEEKLY) {
setCustomSlots({});
} else {
setWeeklySlots({});
}
}, []);

const handleAddSlot = useCallback(
(day: string) => {
const updateSlots = (prev: SlotsType) => ({
Expand Down Expand Up @@ -360,12 +376,34 @@ export function SettingsTab({ consultant }: SettingsTabProps) {

setIsLoading(true);
try {
await updateConsultantSettings(
consultant.id,
formData,
weeklySlots,
customSlots,
);
// Only send slots for the current schedule type
const updatedData = {
...formData,
language: serviceSettings.language,
level: serviceSettings.level,
prerequisites: serviceSettings.prerequisites,
scheduleType, // Ensure we're using the current scheduleType
slotsOfAvailabilityWeekly:
scheduleType === ScheduleType.WEEKLY
? formatSlotsForApi(weeklySlots, true)
: [],
slotsOfAvailabilityCustom:
scheduleType === ScheduleType.CUSTOM
? formatSlotsForApi(customSlots, false)
: [],
};

const response = await fetch(`/api/user/consultants/${consultant.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updatedData),
});

if (!response.ok) {
throw new Error("Failed to update settings");
}

toast({
title: "Settings updated",
Expand Down Expand Up @@ -507,7 +545,7 @@ export function SettingsTab({ consultant }: SettingsTabProps) {

<RadioGroup
value={scheduleType}
onValueChange={(value: ScheduleType) => setScheduleType(value)}
onValueChange={handleScheduleTypeChange}
className="flex flex-col md:flex-row md:space-x-8 space-y-4 md:space-y-0"
>
{/* Weekly Schedule */}
Expand Down

0 comments on commit c7e615c

Please sign in to comment.