Skip to content

Commit

Permalink
Merge pull request #22 from tnamdevnote/feature/edit-appointment
Browse files Browse the repository at this point in the history
Feature/Edit Appointment
  • Loading branch information
tnamdevnote authored May 8, 2024
2 parents 34b4bc5 + 72d1cf7 commit 07b40a8
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ function AppointmentDateTimePicker({
selected={selectedDate}
onSelect={(selectedDate) => handleSelect(selectedDate)}
disabled={(date) => {
console.log(date < new Date(), date);
return new Date(date).getTime() < new Date().setHours(0, 0, 0, 0);
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import { z } from "zod";
import { format } from "date-fns";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@/components/atoms/button";
Expand All @@ -19,12 +18,13 @@ import { useToast } from "@/components/molecules/toast";
import { FormSchema } from "@/lib/formSchema";
import { cn, formatToDisplayDate, formatToDisplayTime } from "@/lib/utils";
import AppointmentDateTimePicker from "../appointmentDateTimePicker/appointmentDateTimePicker";
import { APPOINTMENT_QUERYResult } from "@/lib/sanity/sanity.types";

/**
* Extracted async calls into its own functions to manage them separate from rendering logic.
* We can potentially make them into server actions.
*/
async function createAppointment(formValues: any) {
async function createAppointment(formValues: z.infer<typeof FormSchema>) {
const res = await fetch("/api/my-appointments", {
method: "POST",
body: JSON.stringify({
Expand All @@ -35,6 +35,21 @@ async function createAppointment(formValues: any) {
return Response.json(res);
}

async function editAppointment(
formValues: z.infer<typeof FormSchema>,
appointmentId: string,
) {
const res = await fetch("/api/my-appointments", {
method: "PATCH",
body: JSON.stringify({
id: appointmentId,
formValues,
}),
});

return Response.json(res);
}

async function sendEmail(formValues: z.infer<typeof FormSchema>) {
const { timeslot, address1, address2, city, state, zipCode, comment } =
formValues;
Expand Down Expand Up @@ -67,7 +82,7 @@ const INITIAL_FORM_VALUES: z.infer<typeof FormSchema> = {
interface AppointmentFormProps {
id?: string;
mode?: "create" | "edit";
appointment?: any;
appointment?: APPOINTMENT_QUERYResult[0];
onClose?: () => void;
}

Expand All @@ -82,29 +97,30 @@ export const AppointmentForm = ({
const submitBtnLabel = (mode === "create" ? "Book" : "Edit") + " Appointment";
const { toast } = useToast();
const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: INITIAL_FORM_VALUES,
// mode === "edit" && !!appointment
// ? {
// date: appointment.timeSlot
// ? unixToDateTimeStrings(appointment.timeSlot?.startTime).date
// : "",
// time: appointment.timeSlot
// ? unixToDateTimeStrings(appointment.timeSlot?.startTime).time
// : "",
// address1: appointment.address1,
// address2: appointment.address2,
// city: appointment.city,
// state: appointment.state,
// zipCode: appointment.zipCode,
// comment: appointment.comment,
// }
// : INITIAL_FORM_VALUES,
defaultValues:
mode === "edit" && !!appointment
? {
timeslot: {
id: appointment.timeslotId,
date: appointment.date,
time: appointment.time,
},
address1: appointment.address1,
address2: appointment.address2,
city: appointment.city,
state: appointment.state,
zipCode: appointment.zipCode,
comment: appointment.comment,
}
: INITIAL_FORM_VALUES,
resolver: zodResolver(FormSchema),
});

const onSubmit = async (values: z.infer<typeof FormSchema>) => {
try {
await createAppointment({ ...values });
mode === "edit" && !!appointment
? await editAppointment(values, appointment.id)
: await createAppointment({ ...values });
await sendEmail(values);

onClose ? onClose() : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,56 @@ export default async function UpcomingAppointments() {
// TODO: add logic for handling error and empty array state.
return (
<>
{appointments.map(
({
id,
date,
time,
address1,
address2,
city,
state,
zipCode,
comment,
}) => (
<Card key={id}>
<CardContent className="flex flex-col gap-4">
<p className="inline-flex gap-2 text-base font-bold">
<CalendarIcon />
{formatToDisplayDate(date)}
</p>
<p className="inline-flex gap-2 text-base font-bold">
<ClockIcon />
{formatToDisplayTime(time)}
</p>
<p className="text-sm">
{[address1, address2, city, state, zipCode]
.filter((address) => address)
.join(", ")}
</p>
<p className="line-clamp-2 overflow-hidden text-ellipsis text-wrap text-sm">
{comment}
</p>
</CardContent>
<CardFooter className="inline-flex justify-end gap-4">
<CancelDialog
appointmentId={id}
trigger={
<Button
variant="ghost"
size="sm"
className="flex-1 md:w-32 md:flex-none"
>
Cancel
</Button>
}
/>
<EditDialog
trigger={
<Button size="sm" className="flex-1 md:w-32 md:flex-none">
Edit
</Button>
}
/>
</CardFooter>
</Card>
),
)}
{appointments.map((appointment) => (
<Card key={appointment.id}>
<CardContent className="flex flex-col gap-4">
<p className="inline-flex gap-2 text-base font-bold">
<CalendarIcon />
{formatToDisplayDate(appointment.date)}
</p>
<p className="inline-flex gap-2 text-base font-bold">
<ClockIcon />
{formatToDisplayTime(appointment.time)}
</p>
<p className="text-sm">
{[
appointment.address1,
appointment.address2,
appointment.city,
appointment.state,
appointment.zipCode,
]
.filter((address) => address)
.join(", ")}
</p>
<p className="line-clamp-2 overflow-hidden text-ellipsis text-wrap text-sm">
{appointment.comment}
</p>
</CardContent>
<CardFooter className="inline-flex justify-end gap-4">
<CancelDialog
appointmentId={appointment.id}
trigger={
<Button
variant="ghost"
size="sm"
className="flex-1 md:w-32 md:flex-none"
>
Cancel
</Button>
}
/>
<EditDialog
appointment={appointment}
trigger={
<Button size="sm" className="flex-1 md:w-32 md:flex-none">
Edit
</Button>
}
/>
</CardFooter>
</Card>
))}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,11 @@ const cancelAppointment = async (
};

interface CancelDialogProps {
timeSlotId?: string;
appointmentId: string;
trigger: React.ReactNode;
}

export default function CancelDialog({
timeSlotId,
appointmentId,
trigger,
}: CancelDialogProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from "@/components/molecules/drawer";
import { useEffect, useState } from "react";
import { AppointmentForm } from "../appointmentForm/appointmentForm";
import { APPOINTMENT_QUERYResult } from "@/lib/sanity/sanity.types";

/**
* Renders `<AppointmentForm />` in `<Dialog />`.
Expand All @@ -27,7 +28,7 @@ export default function EditDialog({
appointment,
}: {
trigger: React.ReactNode;
appointment?: any;
appointment: APPOINTMENT_QUERYResult[0];
}) {
const [matches, setMatches] = useState(false);
const [open, setOpen] = useState(false);
Expand All @@ -46,18 +47,18 @@ export default function EditDialog({
return matches ? (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{trigger}</DialogTrigger>
<DialogContent>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Edit Appointment</DialogTitle>
<DialogDescription>
Change your appointment details.
</DialogDescription>
</DialogHeader>
{/* <AppointmentForm
<AppointmentForm
mode="edit"
appointment={appointment}
onClose={() => setOpen(false)}
/> */}
/>
</DialogContent>
</Dialog>
) : (
Expand All @@ -67,11 +68,11 @@ export default function EditDialog({
<DrawerHeader>
<DrawerTitle>Edit Appointment</DrawerTitle>
</DrawerHeader>
{/* <AppointmentForm
<AppointmentForm
mode="edit"
appointment={appointment}
onClose={() => setOpen(false)}
/> */}
/>
</DrawerContent>
</Drawer>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export default function NewAppointmentDialog({
trigger,
}: {
trigger: React.ReactNode;
appointment?: any;
}) {
const [matches, setMatches] = useState(false);
const [open, setOpen] = useState(false);
Expand All @@ -49,7 +48,7 @@ export default function NewAppointmentDialog({
<DialogTrigger asChild>{trigger}</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Edit Appointment</DialogTitle>
<DialogTitle>New Appointment</DialogTitle>
<DialogDescription>
Change your appointment details.
</DialogDescription>
Expand All @@ -69,7 +68,7 @@ export default function NewAppointmentDialog({
</DrawerTrigger>
<DrawerContent className="p-6">
<DrawerHeader>
<DrawerTitle>Edit Appointment</DrawerTitle>
<DrawerTitle>New Appointment</DrawerTitle>
</DrawerHeader>
<div className="p-2">
<AppointmentForm onClose={() => setOpen(false)} />
Expand Down
29 changes: 1 addition & 28 deletions app/(protected)/my-appointments/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { Button } from "@/components/atoms/button";
import { Container } from "@/components/templates/container";
import { PlusIcon } from "lucide-react";
import { AppointmentForm, AppointmentList } from "./components";
import { AppointmentList } from "./components";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";
import NewAppointmentDialog from "./components/newAppointmentDialog/newAppointmentDialog";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/molecules/dialog";

export default async function MyAppointments() {
const { isAuthenticated } = getKindeServerSession();
Expand All @@ -27,25 +19,6 @@ export default async function MyAppointments() {
<h1 className="text-lg font-medium text-primary-100 lg:text-lg">
My Appointments
</h1>
{/* <Dialog>
<DialogTrigger asChild>
<Button
className="hidden md:absolute md:right-32 md:top-0 md:flex"
icon={<PlusIcon />}
>
New Appointment
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Edit Appointment</DialogTitle>
<DialogDescription>
Change your appointment details.
</DialogDescription>
</DialogHeader>
<AppointmentForm />
</DialogContent>
</Dialog> */}
<NewAppointmentDialog
trigger={
<Button
Expand Down
2 changes: 1 addition & 1 deletion app/api/my-appointments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function PATCH(req: NextRequest) {
}

const payload = await req.json();
const res = await updateAppointment(payload, user.id);
const res = await updateAppointment(payload.formValues, user.id, payload.id);

return Response.json(res);
}
Expand Down
6 changes: 2 additions & 4 deletions lib/sanity/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,10 @@ export const createAppointment = async (
export const updateAppointment = async (
form: z.infer<typeof FormSchema>,
userId: string,
appointmentId: string,
) => {
const appointment = mapAppointment(form, userId);
const res = await client
.patch("YamW7iUfTGgX1JkiOPz5iP")
.set(appointment)
.commit();
const res = await client.patch(appointmentId).set(appointment).commit();

return res;
};
Expand Down
4 changes: 2 additions & 2 deletions lib/sanity/sanity.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,11 @@ export type APPOINTMENT_QUERYResult = Array<{
date: string;
time: string;
address1: string;
address2: string | null;
address2: string | undefined;
city: string;
state: string;
zipCode: string;
comment: string | null;
comment: string | undefined;
customer: {
id: string;
firstName: string;
Expand Down

0 comments on commit 07b40a8

Please sign in to comment.