From cc83207ff8c0e1035f4adff29605603384fbda18 Mon Sep 17 00:00:00 2001
From: Taek Been Nam
Date: Tue, 7 May 2024 12:25:57 -0400
Subject: [PATCH 1/5] update sanity groq and schema to use dateTime
---
.../appointmentDateTimePicker.tsx | 2 +-
.../appointmentTimeslots.tsx | 4 ++--
.../appointmentForm/appointmentForm.tsx | 6 +-----
.../appointments/upcomingAppointments.tsx | 2 +-
lib/sanity/client.ts | 17 +++++++++--------
lib/sanity/sanity.types.ts | 7 ++++---
6 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentDateTimePicker.tsx b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentDateTimePicker.tsx
index cd76854..c5641e6 100644
--- a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentDateTimePicker.tsx
+++ b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentDateTimePicker.tsx
@@ -7,7 +7,7 @@ import { mutate } from "swr";
import AppointmentTimeslots from "./appointmentTimeslots";
interface AppointmentDateTimePickerProps {
- availableDates: string[];
+ availableDates?: string[];
}
function AppointmentDateTimePicker({
diff --git a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
index 4e4edde..0f7489f 100644
--- a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
+++ b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
@@ -1,6 +1,7 @@
import { Badge } from "@/components/atoms/badge";
import { FormControl } from "@/components/molecules/form";
import { TimeslotSchema } from "@/lib/formSchema";
+import { format } from "date-fns";
import { useState } from "react";
import { useController } from "react-hook-form";
import useSWR from "swr";
@@ -27,7 +28,6 @@ function AppointmentTimeslots({ currentDate }: AppointmentTimeslotsProps) {
e: React.SyntheticEvent,
timeslot: z.infer,
) => {
- // getValues('timeslot').id
field.onChange(timeslot);
if (selected !== timeslot.id) {
return setSelected(timeslot.id);
@@ -52,7 +52,7 @@ function AppointmentTimeslots({ currentDate }: AppointmentTimeslotsProps) {
className="shrink flex-grow-0"
key={timeslot.id}
selected={selected === timeslot.id}
- label={timeslot.time}
+ label={format(new Date(`${timeslot.time}`), "p")}
onClick={(e) => handleSelect(e, timeslot)}
/>
))
diff --git a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
index ddbb317..59dc12c 100644
--- a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
+++ b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
@@ -71,8 +71,6 @@ interface AppointmentFormProps {
onClose?: () => void;
}
-const AVAILABLE_DATES = ["2024-05-06", "2024-05-02", "2024-05-13"];
-
/**
* A client side form component that handles both creating and editing appointments.
*/
@@ -143,9 +141,7 @@ export const AppointmentForm = ({
Pick your appointment date
-
+
diff --git a/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx b/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
index 84476e5..46eb47d 100644
--- a/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
+++ b/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
@@ -37,7 +37,7 @@ export default async function UpcomingAppointments() {
- {format(new Date(`${date} ${time}`), "p")}
+ {format(new Date(`${time}`), "p")}
{[address1, address2, city, state, zipCode]
diff --git a/lib/sanity/client.ts b/lib/sanity/client.ts
index 7f84d4c..ec35c15 100644
--- a/lib/sanity/client.ts
+++ b/lib/sanity/client.ts
@@ -49,13 +49,14 @@ export const getAvailableDate = async () => {
export const getAvailableTimeSlot = async (date: string) => {
const timeSlots = await client.fetch(
`*[_type=='timeslot'
- && date=='${date}'
&& !(_id in *[_type=='appointment'].timeslot._ref)
- ]{
- "id": _id,
- date,
- "time": duration.start
- }`,
+ && date == '${date}'
+ && dateTime(time) >= dateTime(now())
+ ]{
+ "id": _id,
+ date,
+ time,
+ }`,
{},
{ cache: "no-store" },
);
@@ -79,12 +80,12 @@ export const getAppointments = async (userId = "") => {
const res = await client.fetch(
`*[_type=='appointment'
&& customer->_id == '${userId}'
- && timeslot->date >= now()
+ && dateTime(timeslot->time) >= dateTime(now())
]{
"id":_id,
"timeslotId":timeslot->_id,
"date":timeslot->date,
- "time":timeslot->duration.start,
+ "time":timeslot->time,
address1,
address2,
city,
diff --git a/lib/sanity/sanity.types.ts b/lib/sanity/sanity.types.ts
index 766cce3..ec74b44 100644
--- a/lib/sanity/sanity.types.ts
+++ b/lib/sanity/sanity.types.ts
@@ -262,18 +262,19 @@ export type AVAILABLE_DATE_QUERYResult = Array<{
id: string;
date: string;
}>;
+// Source: ./groq/groq.ts
// Variable: AVAILABLE_TIMESLOT_QUERY
-// Query: *[_type=='timeslot' && date=='' && !(_id in *[_type=='appointment'].timeslot._ref)]{ "id": _id, date, "startTime": duration.start}
+// Query: *[_type=='timeslot' && !(_id in *[_type=='appointment'].timeslot._ref) && date == '' && dateTime(timeslot) >= dateTime(now())]{ "id": _id, date, timeslot,}
export type AVAILABLE_TIMESLOT_QUERYResult = Array<{
id: string;
date: string;
- time: string;
+ timeslot: string;
}>;
// Variable: IS_TIMESLOT_RESERVED_QUERY
// Query: count(*[_type=='appointment' && references('') ]) > 0
export type IS_TIMESLOT_RESERVED_QUERYResult = unknown;
// Variable: APPOINTMENT_QUERY
-// Query: *[_type=='appointment' && customer->_id == '']{ "id":_id, "timeslotId":timeslot->_id, "date":timeslot->date, "time":timeslot->duration.start, address1, address2, city, state, zipCode, comment, customer->{"id": _id, firstName, lastName}, stylist->{"id": _id, firstName, lastName}}
+// Query: *[_type=='appointment' && customer->_id == '' && dateTime(timeslot->timeslot) >= dateTime(now())]{ "id":_id, "timeslotId":timeslot->_id, "date":timeslot->date, "time":timeslot->timeslot, address1, address2, city, state, zipCode, comment, customer->{"id": _id, firstName, lastName}, stylist->{"id": _id, firstName, lastName}}
export type APPOINTMENT_QUERYResult = Array<{
id: string;
timeslotId: string;
From eb4a42a4a96c171b2114484b1365ed60effc54a1 Mon Sep 17 00:00:00 2001
From: Taek Been Nam
Date: Tue, 7 May 2024 12:36:29 -0400
Subject: [PATCH 2/5] adjust sendEmail date time format
---
.../components/appointmentForm/appointmentForm.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
index 59dc12c..291c71f 100644
--- a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
+++ b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
@@ -42,10 +42,10 @@ async function sendEmail(formValues: z.infer) {
const res = await fetch("/api/email", {
method: "POST",
headers: { "Content-type": "application/json" },
- // This is a mock data. Replace with proper form values later.
+
body: JSON.stringify({
date: new Date(timeslot.date).toUTCString().slice(0, 16),
- time: format(new Date(`${timeslot.date} ${timeslot.time}`), "p"),
+ time: format(new Date(`${timeslot.time}`), "p"),
location,
comment,
}),
@@ -104,6 +104,7 @@ export const AppointmentForm = ({
const onSubmit = async (values: z.infer) => {
try {
+ console.log(values);
await createAppointment({ ...values });
await sendEmail(values);
From fbb507d89f21ffc8823ce8edc83a432826b0d06d Mon Sep 17 00:00:00 2001
From: Taek Been Nam
Date: Tue, 7 May 2024 12:56:51 -0400
Subject: [PATCH 3/5] util functions to format date and time
---
.../appointmentTimeslots.tsx | 3 ++-
.../appointmentForm/appointmentForm.tsx | 6 +++---
.../appointments/upcomingAppointments.tsx | 5 +++--
lib/utils.ts | 18 +++++++++++++-----
4 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
index 0f7489f..7d4e7dc 100644
--- a/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
+++ b/app/(protected)/my-appointments/components/appointmentDateTimePicker/appointmentTimeslots.tsx
@@ -1,6 +1,7 @@
import { Badge } from "@/components/atoms/badge";
import { FormControl } from "@/components/molecules/form";
import { TimeslotSchema } from "@/lib/formSchema";
+import { formatToDisplayTime } from "@/lib/utils";
import { format } from "date-fns";
import { useState } from "react";
import { useController } from "react-hook-form";
@@ -52,7 +53,7 @@ function AppointmentTimeslots({ currentDate }: AppointmentTimeslotsProps) {
className="shrink flex-grow-0"
key={timeslot.id}
selected={selected === timeslot.id}
- label={format(new Date(`${timeslot.time}`), "p")}
+ label={formatToDisplayTime(timeslot.time)}
onClick={(e) => handleSelect(e, timeslot)}
/>
))
diff --git a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
index 291c71f..bc919ae 100644
--- a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
+++ b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
@@ -17,7 +17,7 @@ import {
} from "@/components/molecules/form";
import { useToast } from "@/components/molecules/toast";
import { FormSchema } from "@/lib/formSchema";
-import { cn } from "@/lib/utils";
+import { cn, formatToDisplayDate, formatToDisplayTime } from "@/lib/utils";
import AppointmentDateTimePicker from "../appointmentDateTimePicker/appointmentDateTimePicker";
/**
@@ -44,8 +44,8 @@ async function sendEmail(formValues: z.infer) {
headers: { "Content-type": "application/json" },
body: JSON.stringify({
- date: new Date(timeslot.date).toUTCString().slice(0, 16),
- time: format(new Date(`${timeslot.time}`), "p"),
+ date: formatToDisplayDate(timeslot.date),
+ time: formatToDisplayTime(timeslot.time),
location,
comment,
}),
diff --git a/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx b/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
index 46eb47d..178ea21 100644
--- a/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
+++ b/app/(protected)/my-appointments/components/appointments/upcomingAppointments.tsx
@@ -8,6 +8,7 @@ import CancelDialog from "../cancelDialog/cancelDialog";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { getAppointments } from "@/lib/sanity/client";
import { format } from "date-fns";
+import { formatToDisplayDate, formatToDisplayTime } from "@/lib/utils";
export default async function UpcomingAppointments() {
const { getUser } = getKindeServerSession();
@@ -33,11 +34,11 @@ export default async function UpcomingAppointments() {
- {new Date(date).toUTCString().slice(0, 16)}
+ {formatToDisplayDate(date)}
- {format(new Date(`${time}`), "p")}
+ {formatToDisplayTime(time)}
{[address1, address2, city, state, zipCode]
diff --git a/lib/utils.ts b/lib/utils.ts
index 289d9e6..ce2f99c 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,16 +1,24 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { get, set, ref, child } from "firebase/database";
+import { format } from "date-fns";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
-export function dateTimeToUnixTimeStamp(date: string, time: string) {
- const combinedDateTimeString = date + "T" + time;
- const dateTime = new Date(combinedDateTimeString);
- const unixTimeStamp = dateTime.getTime() / 1000;
- return unixTimeStamp;
+/** Convert incoming date string to UTC format. This only returns date portion. */
+export function formatToDisplayDate(date: string): string {
+ const res = new Date(date).toUTCString().slice(0, 16);
+
+ return res;
+}
+
+/** Convert incoming date string to HH:mm AM/PM format*/
+export function formatToDisplayTime(time: string): string {
+ const res = format(new Date(`${time}`), "p");
+
+ return res;
}
export function unixToDateTimeStrings(unixTimeStamp: number) {
From 15b412db4fed902f338ce86c336a231aab7506a7 Mon Sep 17 00:00:00 2001
From: Taek Been Nam
Date: Tue, 7 May 2024 13:08:08 -0400
Subject: [PATCH 4/5] getAvailableTimeSlot -> getAvailableTimeSlots
---
app/api/timeslots/[date]/route.ts | 4 ++--
lib/sanity/client.ts | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/api/timeslots/[date]/route.ts b/app/api/timeslots/[date]/route.ts
index a5ea925..21384a1 100644
--- a/app/api/timeslots/[date]/route.ts
+++ b/app/api/timeslots/[date]/route.ts
@@ -1,4 +1,4 @@
-import { getAvailableTimeSlot } from "@/lib/sanity/client";
+import { getAvailableTimeSlots } from "@/lib/sanity/client";
import { formatISO } from "date-fns";
import { NextRequest } from "next/server";
@@ -12,6 +12,6 @@ export async function GET(
representation: "date",
});
- const res = await getAvailableTimeSlot(formattedDate);
+ const res = await getAvailableTimeSlots(formattedDate);
return Response.json(res, { status: 200 });
}
diff --git a/lib/sanity/client.ts b/lib/sanity/client.ts
index ec35c15..19829b5 100644
--- a/lib/sanity/client.ts
+++ b/lib/sanity/client.ts
@@ -46,7 +46,7 @@ export const getAvailableDate = async () => {
return Object.keys(distinctDates);
};
-export const getAvailableTimeSlot = async (date: string) => {
+export const getAvailableTimeSlots = async (date: string) => {
const timeSlots = await client.fetch(
`*[_type=='timeslot'
&& !(_id in *[_type=='appointment'].timeslot._ref)
From 7460924e61cdd901d4132cd27a8a2289d56043f5 Mon Sep 17 00:00:00 2001
From: Taek Been Nam
Date: Tue, 7 May 2024 13:08:29 -0400
Subject: [PATCH 5/5] remove console log
---
.../components/appointmentForm/appointmentForm.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
index bc919ae..4510dc7 100644
--- a/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
+++ b/app/(protected)/my-appointments/components/appointmentForm/appointmentForm.tsx
@@ -104,7 +104,6 @@ export const AppointmentForm = ({
const onSubmit = async (values: z.infer) => {
try {
- console.log(values);
await createAppointment({ ...values });
await sendEmail(values);