diff --git a/package.json b/package.json index e798e98c..45c92053 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": { "@headlessui/react": "^1.4.1", "@heroicons/react": "^1.0.4", + "@hookform/resolvers": "^2.8.1", "@jitsu/sdk-js": "^2.2.4", "@prisma/client": "^2.30.2", "@radix-ui/react-avatar": "^0.1.0", @@ -73,6 +74,7 @@ "react": "17.0.2", "react-dom": "17.0.2", "react-easy-crop": "^3.5.2", + "react-hook-form": "^7.16.1", "react-hot-toast": "^2.1.0", "react-intl": "^5.20.7", "react-multi-email": "^0.5.3", diff --git a/pages/availability/index.tsx b/pages/availability/index.tsx index 568462b2..3b9df01c 100644 --- a/pages/availability/index.tsx +++ b/pages/availability/index.tsx @@ -1,36 +1,57 @@ import { ClockIcon } from "@heroicons/react/outline"; -import { useSession } from "next-auth/client"; import Link from "next/link"; import { useRouter } from "next/router"; -import { useRef, useState } from "react"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { useToggleQuery } from "@lib/hooks/useToggleQuery"; +import showToast from "@lib/notification"; import { trpc } from "@lib/trpc"; +import { Dialog, DialogContent } from "@components/Dialog"; import Loader from "@components/Loader"; -import Modal from "@components/Modal"; import Shell from "@components/Shell"; import { Alert } from "@components/ui/Alert"; +import Button from "@components/ui/Button"; +function convertMinsToHrsMins(mins: number) { + const h = Math.floor(mins / 60); + const m = mins % 60; + const hours = h < 10 ? "0" + h : h; + const minutes = m < 10 ? "0" + m : m; + return `${hours}:${minutes}`; +} export default function Availability() { const queryMe = trpc.useQuery(["viewer.me"]); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [session, loading] = useSession(); - const router = useRouter(); - const [showAddModal, setShowAddModal] = useState(false); - const [successModalOpen, setSuccessModalOpen] = useState(false); - const [showChangeTimesModal, setShowChangeTimesModal] = useState(false); - const titleRef = useRef(); - const slugRef = useRef(); - const descriptionRef = useRef(); - const lengthRef = useRef(); - const isHiddenRef = useRef(); + const formModal = useToggleQuery("edit"); - const startHoursRef = useRef(); - const startMinsRef = useRef(); - const endHoursRef = useRef(); - const endMinsRef = useRef(); - const bufferHoursRef = useRef(); - const bufferMinsRef = useRef(); + const formMethods = useForm<{ + startHours: string; + startMins: string; + endHours: string; + endMins: string; + bufferHours: string; + bufferMins: string; + }>({}); + const router = useRouter(); + + useEffect(() => { + /** + * This hook populates the form with new values as soon as the user is loaded or changes + */ + const user = queryMe.data; + if (formMethods.formState.isDirty || !user) { + return; + } + formMethods.reset({ + startHours: convertMinsToHrsMins(user.startTime).split(":")[0], + startMins: convertMinsToHrsMins(user.startTime).split(":")[1], + endHours: convertMinsToHrsMins(user.endTime).split(":")[0], + endMins: convertMinsToHrsMins(user.endTime).split(":")[1], + bufferHours: convertMinsToHrsMins(user.bufferTime).split(":")[0], + bufferMins: convertMinsToHrsMins(user.bufferTime).split(":")[1], + }); + }, [formMethods, queryMe.data]); if (queryMe.status === "loading") { return ; @@ -40,86 +61,6 @@ export default function Availability() { } const user = queryMe.data; - function toggleAddModal() { - setShowAddModal(!showAddModal); - } - - function toggleChangeTimesModal() { - setShowChangeTimesModal(!showChangeTimesModal); - } - - const closeSuccessModal = () => { - setSuccessModalOpen(false); - router.replace(router.asPath); - }; - - function convertMinsToHrsMins(mins) { - let h = Math.floor(mins / 60); - let m = mins % 60; - h = h < 10 ? "0" + h : h; - m = m < 10 ? "0" + m : m; - return `${h}:${m}`; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async function createEventTypeHandler(event) { - event.preventDefault(); - - const enteredTitle = titleRef.current.value; - const enteredSlug = slugRef.current.value; - const enteredDescription = descriptionRef.current.value; - const enteredLength = lengthRef.current.value; - const enteredIsHidden = isHiddenRef.current.checked; - - // TODO: Add validation - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const response = await fetch("/api/availability/eventtype", { - method: "POST", - body: JSON.stringify({ - title: enteredTitle, - slug: enteredSlug, - description: enteredDescription, - length: enteredLength, - hidden: enteredIsHidden, - }), - headers: { - "Content-Type": "application/json", - }, - }); - - if (enteredTitle && enteredLength) { - router.replace(router.asPath); - toggleAddModal(); - } - } - - async function updateStartEndTimesHandler(event) { - event.preventDefault(); - - const enteredStartHours = parseInt(startHoursRef.current.value); - const enteredStartMins = parseInt(startMinsRef.current.value); - const enteredEndHours = parseInt(endHoursRef.current.value); - const enteredEndMins = parseInt(endMinsRef.current.value); - const enteredBufferHours = parseInt(bufferHoursRef.current.value); - const enteredBufferMins = parseInt(bufferMinsRef.current.value); - - const startMins = enteredStartHours * 60 + enteredStartMins; - const endMins = enteredEndHours * 60 + enteredEndMins; - const bufferMins = enteredBufferHours * 60 + enteredBufferMins; - - // TODO: Add validation - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const response = await fetch("/api/availability/day", { - method: "PATCH", - body: JSON.stringify({ start: startMins, end: endMins, buffer: bufferMins }), - headers: { - "Content-Type": "application/json", - }, - }); - - setShowChangeTimesModal(false); - setSuccessModalOpen(true); - } - return (
@@ -136,9 +77,7 @@ export default function Availability() {

- +
@@ -159,153 +98,148 @@ export default function Availability() { - {showChangeTimesModal && ( -
-
- - - -
-
-
- -
-
- -
-

- Set the start and end time of your day and a minimum buffer between your meetings. -

-
-
+ { + router.push(isOpen ? formModal.hrefOn : formModal.hrefOff); + }}> + +
+
+ +
+
+ +
+

+ Set the start and end time of your day and a minimum buffer between your meetings. +

-
-
- -
- - -
- : -
- - -
-
-
- -
- - -
- : -
- - -
-
-
- -
- - -
- : -
- - -
-
-
- - -
-
-
- )} - +
{ + const startMins = parseInt(values.startHours) * 60 + parseInt(values.startMins); + const endMins = parseInt(values.endHours) * 60 + parseInt(values.endMins); + const bufferMins = parseInt(values.bufferHours) * 60 + parseInt(values.bufferMins); + + // TODO: Add validation + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const response = await fetch("/api/availability/day", { + method: "PATCH", + body: JSON.stringify({ start: startMins, end: endMins, buffer: bufferMins }), + headers: { + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + showToast("Something went wrong", "error"); + return; + } + await queryMe.refetch(); + router.push(formModal.hrefOff); + + showToast("The start and end times for your day have been changed successfully.", "success"); + })}> +
+ +
+ + +
+ : +
+ + +
+
+
+ +
+ + +
+ : +
+ + +
+
+
+ +
+ + +
+ : +
+ + +
+
+
+ + +
+
+ +
); diff --git a/yarn.lock b/yarn.lock index f40ba6c6..7e76b76c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -548,6 +548,11 @@ version "1.0.4" resolved "https://registry.npmjs.org/@heroicons/react/-/react-1.0.4.tgz" +"@hookform/resolvers@^2.8.1": + version "2.8.1" + resolved "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.8.1.tgz#0d4fdd25bdeb4b98bf4e177c63fc4efa173454dd" + integrity sha512-U5lgaCkvD+0e5X8iQmCHiF+jOqjTX6OHUA7zPdeIHI6xdAOoi3rH9MKNuwMwv5Hly2LL6XtDgDkS/k+YG9hOew== + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz" @@ -6462,6 +6467,11 @@ react-fit@^1.0.3: detect-element-overflow "^1.2.0" prop-types "^15.6.0" +react-hook-form@^7.16.1: + version "7.16.1" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.16.1.tgz#669046df378a71949e5cf8a2398cbe20d5cb27bc" + integrity sha512-kcLDmSmlyLUFx2UU5bG/o4+3NeK753fhKodJa8gkplXohGkpAq0/p+TR24OWjZmkEc3ES7ppC5v5d6KUk+fJTA== + react-hot-toast@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.1.1.tgz"