Skip to content

Commit

Permalink
appointmet form updated
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 14, 2024
1 parent 792e2f9 commit 890c4a6
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 117 deletions.
16 changes: 13 additions & 3 deletions client/components/doctor/slots/MultiSlots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { useAddSlotsAllDaysDoctor, useDeleteSlotsAllDaysDoctor, useGetAllSlotsDo
import { toast } from "@/components/ui/use-toast";
import ConfirmDeleteSlotsModel from "@/components/models/ConfirmDeleteSlotsModel";
import { AvailableTimes } from "@/constants";
import { useQueryClient } from "@tanstack/react-query";
import { Days } from "@/types";

const availableTimeOptions = Object.values(AvailableTimes).flat().map(time => ({ label: time, value: time }));

const SlotManager = () => {
const [slotsToAdd, setSlotsToAdd] = useState<string[]>([]);
const [slotsToRemove, setSlotsToRemove] = useState<string[]>([]);
const [isDeleteModelOpen,setDeleteModelOpen] = useState(false)
const { refetch } = useGetAllSlotsDoctor();
const query = useQueryClient()
const { mutate: addSlots, isPending: isAdding } = useAddSlotsAllDaysDoctor();
const { mutate: deleteSlots, isPending: isDeleting } = useDeleteSlotsAllDaysDoctor();

Expand Down Expand Up @@ -40,6 +42,11 @@ const SlotManager = () => {
description: `Successfully added ${slotsToAdd.length} slots.`,
variant: "success"
});
setTimeout(()=>{
query.invalidateQueries({
queryKey:['slotsByDay',...Object.values(Days)]
})
})
},
onError: (error) => {
console.error("Failed to add slots", error);
Expand All @@ -50,7 +57,6 @@ const SlotManager = () => {
});
}
});
refetch()
}
};

Expand All @@ -65,6 +71,11 @@ const SlotManager = () => {
variant: "warning"
});
setDeleteModelOpen(false)
setTimeout(()=>{
query.invalidateQueries({
queryKey:['slotsByDay',...Object.values(Days)]
})
})
},
onError: (error) => {
console.error("Failed to delete slots", error);
Expand All @@ -75,7 +86,6 @@ const SlotManager = () => {
});
}
});
refetch()
}
};

Expand Down
21 changes: 16 additions & 5 deletions client/components/doctor/slots/SlotsTabContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import React, { useEffect, useState } from 'react';
import { RenderTimeSlots } from './RenderTimeSlots';
import ConfirmDeleteSlotsModal from '@/components/models/ConfirmDeleteSlotsModel';
import { AvailableTimes } from '@/constants';
import { useQueryClient } from '@tanstack/react-query';

type Props = {
day: Days;
};

const TabContent = ({ day }: Props) => {
const { data: slots, isLoading, refetch } = useGetSlotsByDayDoctor(day);
const [selectedMorningSlots, setSelectedMorningSlots] = useState<string[]>([]);
const [selectedAfternoonSlots, setSelectedAfternoonSlots] = useState<string[]>([]);
const [selectedEveningSlots, setSelectedEveningSlots] = useState<string[]>([]);
const [openConfirmDelete, setOpenConfirmDelete] = useState(false);
const [slotsToDelete, setSlotsToDelete] = useState<string[]>([]);
const query = useQueryClient();

const { data: slots, isLoading, refetch } = useGetSlotsByDayDoctor(day);
const { mutate: addSlots, isPending } = useAddSlotsDoctor();
const { mutate: deleteSlots, isPending: isDeleting } = useDeleteDoctorByDay();

Expand All @@ -32,7 +34,10 @@ const TabContent = ({ day }: Props) => {

const handleAddSlot = (slot: string) => {
addSlots({ slots: [{ startTime: slot }], day });
setTimeout(() => refetch(), 500);
query.invalidateQueries({
queryKey: ['slotsByDay', day]
})
setTimeout(() => refetch());
};

const handleRemoveSlot = (slot: string) => {
Expand All @@ -45,13 +50,16 @@ const TabContent = ({ day }: Props) => {
const addAllSlots = times.filter(time => !slots.some(slot => slot.startTime === time));
if (addAllSlots.length > 0) {
addSlots({ slots: addAllSlots.map(time => ({ startTime: time })), day });
query.invalidateQueries({
queryKey: ['slotsByDay', day]
})
setTimeout(() => refetch());
}
};

const handleClearAll = (times: string[]) => {
if (!slots || slots.length === 0) return;

const clearAllSlots = times.filter(time => slots.some(slot => slot.startTime === time));

if (clearAllSlots.length > 0) {
Expand All @@ -64,6 +72,9 @@ const TabContent = ({ day }: Props) => {
deleteSlots({ slots: slotsToDelete.map(time => ({ startTime: time })), day });
setOpenConfirmDelete(false);
setSlotsToDelete([]);
query.invalidateQueries({
queryKey: ['slotsByDay', day]
})
setTimeout(() => refetch());
};

Expand Down Expand Up @@ -105,7 +116,7 @@ const TabContent = ({ day }: Props) => {
() => handleClearAll(AvailableTimes.Evening)
)}
</div>
<ConfirmDeleteSlotsModal
<ConfirmDeleteSlotsModal
open={openConfirmDelete}
setOpen={setOpenConfirmDelete}
handleDeleteSlots={handleConfirmDelete}
Expand Down
7 changes: 2 additions & 5 deletions client/components/forms/actions/userValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ export const appointmentFormValidation = z.object({
.max(1000, "Notes must not exceed 1000 characters")
.optional(),

date: z.coerce.date()
.refine((date) => date > new Date(), {
message: "Appointment date must be in the future",
}),
date: z.coerce.date(),

doctor: z.string({
required_error: "Doctor selection is required",
Expand All @@ -103,4 +100,4 @@ export const appointmentFormValidation = z.object({
slotId: z.string({
required_error: "Time slot selection is required",
}).min(1, "Time slot selection is required"),
})
});
11 changes: 7 additions & 4 deletions client/components/forms/patient/AppointmentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import { formatDate } from "@/lib/utils";
import { FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { toast } from "@/components/ui/use-toast";
import { AppointmentType } from "@/types";
import { useQueryClient } from "@tanstack/react-query";

const AppointmentForm = () => {
const { data: doctorsData, isLoading: isDoctorsLoading } = useGetDoctorsList();
const [isDoctorSelected, setIsDoctorSelected] = useState(false);
const { mutate: createAppointment, isPending } = useCreateAppointment();
const query = useQueryClient();

const form = useForm<z.infer<typeof appointmentFormValidation>>({
resolver: zodResolver(appointmentFormValidation),
Expand All @@ -42,7 +44,7 @@ const AppointmentForm = () => {
date: new Date(),
});

const { data: slots, isLoading: isSlotsLoading , refetch} = useGetSlotsOfDoctor(
const { data: slots, isLoading: isSlotsLoading } = useGetSlotsOfDoctor(
slotFilter.doctorId,
slotFilter.date instanceof Date ? slotFilter.date.toISOString() : ""
);
Expand Down Expand Up @@ -80,10 +82,11 @@ const AppointmentForm = () => {
description: "We will notify you once the doctor approves your appointment",
variant: "success",
});
refetch();
query.invalidateQueries({
queryKey: ["doctorSlots", slotFilter.doctorId, slotFilter.date instanceof Date ? slotFilter.date.toISOString() : ""]
})
},
onError(error) {
refetch();
const message =
error?.response?.status === 403
? "This action is only allowed for verified users."
Expand All @@ -100,7 +103,7 @@ const AppointmentForm = () => {

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-12">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 m">
<section className="mb-12 space-y-4">
<h1 className="text-2xl font-bold text-gray-200">New Appointment</h1>
<p className="text-gray-400">Request New Appointment in 10 seconds</p>
Expand Down
123 changes: 26 additions & 97 deletions client/components/ui/tabs-v2.tsx
Original file line number Diff line number Diff line change
@@ -1,127 +1,56 @@
"use client";
"use client"

import { useState } from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import { useState } from "react"
import { cn } from "@/lib/utils"

type Tab = {
title: string;
value: string;
content?: string | React.ReactNode | any;
};
title: string
value: string
content?: string | React.ReactNode | any
}

export const Tabs = ({
export function Tabs({
tabs: propTabs,
containerClassName,
activeTabClassName,
tabClassName,
contentClassName,
}: {
tabs: Tab[];
containerClassName?: string;
activeTabClassName?: string;
tabClassName?: string;
contentClassName?: string;
}) => {
const [active, setActive] = useState<Tab>(propTabs[0]);
const [tabs, setTabs] = useState<Tab[]>(propTabs);

const moveSelectedTabToTop = (idx: number) => {
const newTabs = [...propTabs];
const selectedTab = newTabs.splice(idx, 1);
newTabs.unshift(selectedTab[0]);
setTabs(newTabs);
setActive(newTabs[0]);
};

const [hovering, setHovering] = useState(false);
tabs: Tab[]
containerClassName?: string
activeTabClassName?: string
tabClassName?: string
contentClassName?: string
}) {
const [activeTab, setActiveTab] = useState<Tab>(propTabs[0])

return (
<>
<div
className={cn(
"flex flex-row items-center justify-start [perspective:1000px] relative overflow-x-auto sm:overflow-visible no-visible-scrollbar max-w-full w-full",
"flex flex-row items-center justify-start overflow-x-auto sm:overflow-visible no-visible-scrollbar max-w-full w-full",
containerClassName
)}
>
{propTabs.map((tab, idx) => (
{propTabs.map((tab) => (
<button
key={tab.title}
onClick={() => {
moveSelectedTabToTop(idx);
}}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
key={tab.value}
onClick={() => setActiveTab(tab)}
className={cn(
"relative px-2 py-1 sm:px-3 sm:py-2 md:px-4 md:py-2 rounded-full text-xs sm:text-sm md:text-base whitespace-nowrap",
tabClassName
tabClassName,
activeTab.value === tab.value && activeTabClassName
)}
style={{
transformStyle: "preserve-3d",
}}
>
{active.value === tab.value && (
<motion.div
layoutId="clickedbutton"
transition={{ type: "spring", bounce: 0.3, duration: 0.6 }}
className={cn(
"absolute inset-0 bg-gray-200 dark:bg-zinc-800 rounded-full",
activeTabClassName
)}
/>
)}

<span className="relative block text-black dark:text-white">
{tab.title}
</span>
</button>
))}
</div>
<FadeInDiv
tabs={tabs}
active={active}
key={active.value}
hovering={hovering}
className={cn("mt-8 sm:mt-16 md:mt-24 lg:mt-32", contentClassName)}
/>
<div className={cn("mt-8 sm:mt-16 md:mt-24 lg:mt-32", contentClassName)}>
{activeTab.content}
</div>
</>
);
};

export const FadeInDiv = ({
className,
tabs,
hovering,
}: {
className?: string;
key?: string;
tabs: Tab[];
active: Tab;
hovering?: boolean;
}) => {
const isActive = (tab: Tab) => {
return tab.value === tabs[0].value;
};
return (
<div className="relative w-full h-full">
{tabs.map((tab, idx) => (
<motion.div
key={tab.value}
layoutId={tab.value}
style={{
scale: 1 - idx * 0.1,
top: hovering ? idx * -50 : 0,
zIndex: -idx,
opacity: idx < 3 ? 1 - idx * 0.1 : 0,
}}
animate={{
y: isActive(tab) ? [0, 40, 0] : 0,
}}
className={cn("w-full h-full absolute top-0 left-0", className)}
>
{tab.content}
</motion.div>
))}
</div>
);
};
)
}
13 changes: 10 additions & 3 deletions client/lib/hooks/slots/useSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,19 @@ export const useDeleteSlotsAllDaysDoctor = ()=>{
export const useGetSlotsByDayDoctor = (day: Days) => {
return useQuery<ISlot[], AxiosError<ErrorResponse>>({
queryFn: () => getSlotsByDayDoctor(day),
queryKey: ["slotsByDay","allSlots", day],
queryKey: ["slotsByDay", day],
refetchOnMount:false,
refetchOnWindowFocus:false,
refetchInterval:1000*30,

});
};


export const useGetAllSlotsDoctor = () => {
return useQuery<ISlot[], AxiosError<ErrorResponse>>({
queryFn: () => getAllSlotsDoctor(),
queryKey: ["slotsByDay","allSlots",Object.values(Days)],
queryKey: ["allSlots",Object.values(Days)],
});
};

Expand All @@ -55,6 +59,9 @@ export const useGetAllSlotsDoctor = () => {
export const useGetSlotsOfDoctor = (doctorId:string,date:string)=>{
return useQuery<ISlot[],AxiosError<ErrorResponse>>({
queryFn:()=>getSlotsOfDoctor(doctorId,date),
queryKey:["doctorSlots",doctorId,date]
queryKey:["doctorSlots",doctorId,date],
retry:1,
refetchOnWindowFocus:false,
refetchOnMount:false
})
}

1 comment on commit 890c4a6

@vercel
Copy link

@vercel vercel bot commented on 890c4a6 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.