Skip to content

Commit

Permalink
Calendar component
Browse files Browse the repository at this point in the history
  • Loading branch information
rithviknishad committed Nov 18, 2024
1 parent a54d71a commit 3a28ce7
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 49 deletions.
123 changes: 122 additions & 1 deletion src/CAREUI/interactive/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,129 @@
import React from "react";

import { cn } from "@/lib/utils";

import CareIcon from "@/CAREUI/icons/CareIcon";

interface Props {
className?: string;
initialMonth?: Date;
onMonthChange?: (month: Date) => void;
renderDay?: (date: Date) => React.ReactNode;
}

export default function Calendar(props: Props) {
return <div className={props.className}>TODO: build a calendar!</div>;
const [currentMonth, setCurrentMonth] = React.useState(
props.initialMonth ?? new Date(),
);

// Get first day of the month and total days
const firstDayOfMonth = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth(),
1,
);
const lastDayOfMonth = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
0,
);

// Calculate days to display from previous month
const startingDayOfWeek = firstDayOfMonth.getDay();

// Generate calendar days array for current month only
const calendarDays: Date[] = [];

// Add empty slots for previous month days
for (let i = 0; i < startingDayOfWeek; i++) {
calendarDays.push(null as unknown as Date);
}

// Add current month's days
for (let i = 1; i <= lastDayOfMonth.getDate(); i++) {
calendarDays.push(
new Date(currentMonth.getFullYear(), currentMonth.getMonth(), i),
);
}

const handlePrevMonth = () => {
const prevMonth = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() - 1,
);
setCurrentMonth(prevMonth);
props.onMonthChange?.(prevMonth);
};

const handleNextMonth = () => {
const nextMonth = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
);
setCurrentMonth(nextMonth);
props.onMonthChange?.(nextMonth);
};

const weekDays = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];

return (
<div className={`${props.className} w-full`}>
<div className="mb-4 flex items-center justify-between">
<h2 className="text-xl font-bold uppercase">
{currentMonth.toLocaleString("default", {
month: "long",
year: "numeric",
})}
</h2>
<div className="flex gap-2">
<button
onClick={handlePrevMonth}
className="rounded-lg bg-gray-100 p-2 hover:bg-gray-200"
>
<CareIcon icon="l-angle-left" />
</button>
<button
onClick={handleNextMonth}
className="rounded-lg bg-gray-100 p-2 hover:bg-gray-200"
>
<CareIcon icon="l-angle-right" />
</button>
</div>
</div>

<div className="grid grid-cols-7 gap-0.5 md:gap-1.5">
{weekDays.map((day) => (
<div key={day} className="text-center font-medium">
{day}
</div>
))}

{calendarDays.map((date, index) => {
if (!date) {
return <div key={`empty-${index}`} className="min-h-[80px]" />;
}

const isToday = date.toDateString() === new Date().toDateString();

return (
<div
key={index}
className={cn(
"relative min-h-[80px] rounded-lg",
isToday && "ring-2 ring-primary-400",
)}
>
{props.renderDay ? (
props.renderDay(date)
) : (
<span className="block text-right p-2 transition-all rounded-lg bg-white text-gray-900">
{date.getDate()}
</span>
)}
</div>
);
})}
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions src/Utils/request/handleResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export default function handleResponse(
return;
}

// 404 Not Found
if (res.status === 404) {
notify?.Error({ msg: "Not Found" });
return;
}

// 400/406 Bad Request
if (res.status === 400 || res.status === 406) {
notify?.BadRequest({ errs: error });
Expand Down
58 changes: 41 additions & 17 deletions src/components/Schedule/ScheduleTemplateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ import {
SheetTrigger,
} from "@/components/ui/sheet";

import { ScheduleAPIs } from "@/components/Schedule/api";
import { ScheduleSlotTypes } from "@/components/Schedule/schemas";

import useAuthUser from "@/hooks/useAuthUser";

import useMutation from "@/Utils/request/useMutation";
import { Time } from "@/Utils/types";

const formSchema = z.object({
Expand Down Expand Up @@ -66,6 +70,8 @@ const formSchema = z.object({

export default function ScheduleTemplateForm() {
const { t } = useTranslation();
const authUser = useAuthUser();

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
Expand All @@ -85,26 +91,40 @@ export default function ScheduleTemplateForm() {
},
});

function onSubmit(values: z.infer<typeof formSchema>) {
console.log({
doctor_username: "",
valid_from: values.valid_from.toISOString(),
valid_to: values.valid_to.toISOString(),
name: values.name,
availability: values.availability.map((availability) => ({
...availability,
id: "",
slot_size_in_minutes: 0,
tokens_per_slot: 0,
days_of_week: values.weekdays,
})),
const { mutate, isProcessing } = useMutation(
ScheduleAPIs.createScheduleTemplate,
{
pathParams: {
facility_id: authUser.home_facility_object!.id!,
},
},
);

async function onSubmit(values: z.infer<typeof formSchema>) {
await mutate({
body: {
doctor_username: authUser.username,
valid_from: values.valid_from.toISOString(),
valid_to: values.valid_to.toISOString(),
name: values.name,
availability: values.availability.map((availability) => ({
...availability,
slot_type: 1,
id: undefined as unknown as string,
slot_size_in_minutes: 0,
tokens_per_slot: 0,
days_of_week: values.weekdays,
})),
},
});
}

return (
<Sheet>
<SheetTrigger asChild>
<Button variant="primary">Create Template</Button>
<Button variant="primary" disabled={isProcessing}>
Create Template
</Button>
</SheetTrigger>
<SheetContent className="flex min-w-full flex-col bg-gray-100 sm:min-w-[45rem]">
<SheetHeader>
Expand Down Expand Up @@ -348,13 +368,17 @@ export default function ScheduleTemplateForm() {

<SheetFooter className="absolute inset-x-0 bottom-0 border-t bg-white p-6">
<SheetClose asChild>
<Button variant="outline" type="button">
<Button
variant="outline"
type="button"
disabled={isProcessing}
>
Cancel
</Button>
</SheetClose>

<Button variant="primary" type="submit">
Save & Generate Slots
<Button variant="primary" type="submit" disabled={isProcessing}>
{isProcessing ? "Saving..." : "Save & Generate Slots"}
</Button>
</SheetFooter>
</form>
Expand Down
64 changes: 40 additions & 24 deletions src/components/Schedule/ScheduleTemplatesList.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import ColoredIndicator from "@/CAREUI/display/ColoredIndicator";
import CareIcon from "@/CAREUI/icons/CareIcon";

import ScheduleTemplateForm from "@/components/Schedule/ScheduleTemplateForm";
import { ScheduleTemplate } from "@/components/Schedule/schemas";

export default function ScheduleTemplatesList() {
interface Props {
items: ScheduleTemplate[];
}

export default function ScheduleTemplatesList(props: Props) {
return (
<div>
<div className="flex items-end justify-between">
<h4 className="text-lg font-semibold">Schedule Templates</h4>
<ScheduleTemplateForm />
</div>
<ul className="flex flex-col gap-4 py-6">
<li>
<ScheduleTemplateItem id="5" />
</li>
<li>
<ScheduleTemplateItem id="1" />
</li>
{props.items.map((template) => (
<li key={template.id}>
<ScheduleTemplateItem {...template} />
</li>
))}
</ul>
{props.items.length === 0 && (
<div className="flex flex-col items-center text-center text-gray-500 py-10">
<CareIcon icon="l-calendar-slash" className="size-10 mb-2" />
<p>No schedule templates found</p>
<p>Create a schedule template to get started</p>
</div>
)}
</div>
);
}

const ScheduleTemplateItem = (props: { id: string }) => {
const ScheduleTemplateItem = (props: ScheduleTemplate) => {
return (
<div className="rounded-lg bg-white py-2 shadow">
<div className="flex items-center justify-between py-2 pr-4">
Expand All @@ -31,30 +43,34 @@ const ScheduleTemplateItem = (props: { id: string }) => {
id={props.id}
/>
<div className="flex flex-col">
<span className="text-lg font-semibold">Regular OP Day</span>
<span className="text-lg font-semibold">{props.name}</span>
<span className="text-sm text-gray-700">Scheduled for Monday</span>
</div>
</div>
<div>menu</div>
</div>
<div className="flex flex-col gap-2 px-4 py-2">
<ul className="flex flex-col gap-2">
<li className="w-full">
<div className="rounded-lg bg-gray-50 px-3 py-2">
<div className="flex w-full items-center justify-between">
<div className="flex flex-col">
<span>Morning Consultations</span>
<p className="text-gray-600">
<span className="text-sm">Outpatient Schedule</span>
<span className="px-2 text-gray-300">|</span>
<span className="text-sm">5 slots (20 mins.)</span>
</p>
{props.availability.map((slot) => (
<li className="w-full">
<div className="rounded-lg bg-gray-50 px-3 py-2">
<div className="flex w-full items-center justify-between">
<div className="flex flex-col">
<span>{slot.name}</span>
<p className="text-gray-600">
<span className="text-sm">{slot.slot_type}</span>
<span className="px-2 text-gray-300">|</span>
<span className="text-sm">5 slots (20 mins.)</span>
</p>
</div>
<span className="text-sm">
{slot.start_time} - {slot.end_time}
</span>
</div>
<span className="text-sm">09:00 AM - 12:00 PM</span>
</div>
</div>
</li>
<li className="w-full">
</li>
))}
{/* <li className="w-full">
<div className="rounded-lg bg-gray-50 px-3 py-2">
<div className="flex w-full items-center justify-between">
<div className="flex flex-col">
Expand All @@ -68,7 +84,7 @@ const ScheduleTemplateItem = (props: { id: string }) => {
<span className="text-sm">09:00 AM - 12:00 PM</span>
</div>
</div>
</li>
</li> */}
</ul>
<span className="text-sm text-gray-500">
Valid from <strong className="font-semibold">01 Nov 2024</strong> till{" "}
Expand Down
Loading

0 comments on commit 3a28ce7

Please sign in to comment.