Added missing translation and refactor dialog to self component
This commit is contained in:
parent
2176f9e945
commit
60c7791dfd
|
@ -2,109 +2,24 @@ import { BanIcon, CheckIcon, ClockIcon, XIcon } from "@heroicons/react/outline";
|
|||
import { PaperAirplaneIcon } from "@heroicons/react/outline";
|
||||
import { BookingStatus } from "@prisma/client";
|
||||
import dayjs from "dayjs";
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
import * as fetchWrapper from "@lib/core/http/fetch-wrapper";
|
||||
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||
|
||||
import { useMeQuery } from "@components/Shell";
|
||||
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
|
||||
import TableActions, { ActionType } from "@components/ui/TableActions";
|
||||
|
||||
type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
|
||||
|
||||
interface IRescheduleDialog {
|
||||
isOpenDialog: boolean;
|
||||
setIsOpenDialog: Dispatch<SetStateAction<boolean>>;
|
||||
bookingUId: string;
|
||||
}
|
||||
|
||||
const RescheduleDialog = (props: IRescheduleDialog) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const { isOpenDialog, setIsOpenDialog, bookingUId: bookingId } = props;
|
||||
const [rescheduleReason, setRescheduleReason] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const rescheduleApi = useMutation(
|
||||
async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const result = await fetchWrapper.post<any, any>("/api/book/request-reschedule", {
|
||||
bookingId,
|
||||
rescheduleReason,
|
||||
});
|
||||
|
||||
if (result) {
|
||||
showToast(t("reschedule_request_sent"), "success");
|
||||
setIsOpenDialog(false);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast(t("unexpected_error_try_again"), "error");
|
||||
// @TODO: notify sentry
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
{
|
||||
async onSettled() {
|
||||
await utils.invalidateQueries(["viewer.bookings"]);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={isOpenDialog} onOpenChange={setIsOpenDialog}>
|
||||
<DialogContent>
|
||||
<DialogClose asChild>
|
||||
<div className="fixed top-1 right-1 flex h-8 w-8 justify-center rounded-full hover:bg-gray-200">
|
||||
<XIcon className="w-4" />
|
||||
</div>
|
||||
</DialogClose>
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div className="flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
|
||||
<ClockIcon className="m-auto h-6 w-6"></ClockIcon>
|
||||
</div>
|
||||
<div className="px-4 pt-1">
|
||||
<DialogHeader title={t("send_reschedule_request")} />
|
||||
|
||||
<p className="-mt-8 text-sm text-gray-500">{t("reschedule_modal_description")}</p>
|
||||
<p className="mt-6 mb-2 text-sm font-bold text-black">
|
||||
{t("reason_for_reschedule_request")}
|
||||
<span className="font-normal text-gray-500"> (Optional)</span>
|
||||
</p>
|
||||
<TextArea
|
||||
name={t("reschedule_reason")}
|
||||
value={rescheduleReason}
|
||||
onChange={(e) => setRescheduleReason(e.target.value)}
|
||||
className="mb-5 sm:mb-6"
|
||||
/>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose>
|
||||
<Button color="secondary">{t("cancel")}</Button>
|
||||
</DialogClose>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
rescheduleApi.mutate();
|
||||
}}>
|
||||
{t("send_reschedule_request")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
function BookingListItem(booking: BookingItem) {
|
||||
// Get user so we can determine 12/24 hour format preferences
|
||||
const query = useMeQuery();
|
||||
|
|
|
@ -265,7 +265,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
|||
{booking?.startTime && rescheduleUid && (
|
||||
<div>
|
||||
{/* Add translation */}
|
||||
<p className="mt-8 mb-2 text-gray-600 dark:text-white">Former time</p>
|
||||
<p className="mt-8 mb-2 text-gray-600 dark:text-white">{t("former_time")}</p>
|
||||
<p className="text-gray-500 line-through dark:text-white">
|
||||
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-400" />
|
||||
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
||||
|
|
|
@ -367,7 +367,7 @@ const BookingPage = ({
|
|||
{booking?.startTime && rescheduleUid && (
|
||||
<div>
|
||||
{/* Add translation */}
|
||||
<p className="mt-8 mb-2 text-gray-600 dark:text-white">Former time</p>
|
||||
<p className="mt-8 mb-2 text-gray-600 dark:text-white">{t("former_time")}</p>
|
||||
<p className="text-gray-500 line-through dark:text-white">
|
||||
<CalendarIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
|
||||
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { ClockIcon, XIcon } from "@heroicons/react/outline";
|
||||
import { RescheduleResponse } from "pages/api/book/request-reschedule";
|
||||
import React, { useState, Dispatch, SetStateAction } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { Attendee, Booking } from "@calcom/prisma/client";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
|
||||
import * as fetchWrapper from "@lib/core/http/fetch-wrapper";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
interface IRescheduleDialog {
|
||||
isOpenDialog: boolean;
|
||||
setIsOpenDialog: Dispatch<SetStateAction<boolean>>;
|
||||
bookingUId: string;
|
||||
}
|
||||
|
||||
export const RescheduleDialog = (props: IRescheduleDialog) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const { isOpenDialog, setIsOpenDialog, bookingUId: bookingId } = props;
|
||||
const [rescheduleReason, setRescheduleReason] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const rescheduleApi = useMutation(
|
||||
async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const result = await fetchWrapper.post<
|
||||
{ bookingId: string; rescheduleReason: string },
|
||||
RescheduleResponse
|
||||
>("/api/book/request-reschedule", {
|
||||
bookingId,
|
||||
rescheduleReason,
|
||||
});
|
||||
|
||||
if (result) {
|
||||
showToast(t("reschedule_request_sent"), "success");
|
||||
setIsOpenDialog(false);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast(t("unexpected_error_try_again"), "error");
|
||||
// @TODO: notify sentry
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
{
|
||||
async onSettled() {
|
||||
await utils.invalidateQueries(["viewer.bookings"]);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={isOpenDialog} onOpenChange={setIsOpenDialog}>
|
||||
<DialogContent>
|
||||
<DialogClose asChild>
|
||||
<div className="fixed top-1 right-1 flex h-8 w-8 justify-center rounded-full hover:bg-gray-200">
|
||||
<XIcon className="w-4" />
|
||||
</div>
|
||||
</DialogClose>
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div className="flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
|
||||
<ClockIcon className="m-auto h-6 w-6"></ClockIcon>
|
||||
</div>
|
||||
<div className="px-4 pt-1">
|
||||
<DialogHeader title={t("send_reschedule_request")} />
|
||||
|
||||
<p className="-mt-8 text-sm text-gray-500">{t("reschedule_modal_description")}</p>
|
||||
<p className="mt-6 mb-2 text-sm font-bold text-black">
|
||||
{t("reason_for_reschedule_request")}
|
||||
<span className="font-normal text-gray-500"> (Optional)</span>
|
||||
</p>
|
||||
<TextArea
|
||||
name={t("reschedule_reason")}
|
||||
value={rescheduleReason}
|
||||
onChange={(e) => setRescheduleReason(e.target.value)}
|
||||
className="mb-5 sm:mb-6"
|
||||
/>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose>
|
||||
<Button color="secondary">{t("cancel")}</Button>
|
||||
</DialogClose>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
rescheduleApi.mutate();
|
||||
}}>
|
||||
{t("send_reschedule_request")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import { BookingStatus, User, SchedulingType } from "@prisma/client";
|
||||
import { BookingStatus, User, SchedulingType, Booking, Attendee } from "@prisma/client";
|
||||
import dayjs from "dayjs";
|
||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
||||
import { getSession } from "next-auth/react";
|
||||
|
@ -13,12 +13,16 @@ import { Person } from "@calcom/types/Calendar";
|
|||
import { sendRequestRescheduleEmail } from "@lib/emails/email-manager";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
export type RescheduleResponse = Booking & {
|
||||
attendees: Attendee[];
|
||||
};
|
||||
|
||||
const rescheduleSchema = z.object({
|
||||
bookingId: z.string(),
|
||||
rescheduleReason: z.string().optional(),
|
||||
});
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<RescheduleResponse> => {
|
||||
const session = await getSession({ req });
|
||||
const { bookingId, rescheduleReason: cancellationReason } = req.body;
|
||||
type PersonAttendee = Pick<User, "id" | "email" | "name" | "locale" | "timeZone" | "username">;
|
||||
|
|
|
@ -734,5 +734,6 @@
|
|||
"reason_for_reschedule_request": "Reason for reschedule request",
|
||||
"send_reschedule_request": "Send reschedule request",
|
||||
"edit_booking": "Edit booking",
|
||||
"reschedule_booking": "Reschedule booking"
|
||||
"reschedule_booking": "Reschedule booking",
|
||||
"former_time": "Former time"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user