Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Edit Appointment #22

Merged
merged 2 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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