Refactor old dialog to radix Dialog (#2151)
* --init * refactored more dialogs --WIP * more modals replaced by dialogs --WIP * fix for new dialog location import * --WIP * lint fix * final dialog refactor * added more width to max-w for dialog in sm screen * clean-up Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
parent
df64af2aba
commit
bcbf8390e0
17 changed files with 724 additions and 820 deletions
|
@ -2,14 +2,15 @@ import { MembershipRole } from "@prisma/client";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import React, { SyntheticEvent } from "react";
|
import React, { SyntheticEvent } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
|
||||||
import ModalContainer from "@components/ui/ModalContainer";
|
import ModalContainer from "@components/ui/ModalContainer";
|
||||||
|
|
||||||
export default function MemberChangeRoleModal(props: {
|
export default function MemberChangeRoleModal(props: {
|
||||||
|
isOpen: boolean;
|
||||||
memberId: number;
|
memberId: number;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
initialRole: MembershipRole;
|
initialRole: MembershipRole;
|
||||||
|
@ -41,7 +42,7 @@ export default function MemberChangeRoleModal(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContainer>
|
<ModalContainer isOpen={props.isOpen} onExit={props.onExit}>
|
||||||
<>
|
<>
|
||||||
<div className="mb-4 sm:flex sm:items-start">
|
<div className="mb-4 sm:flex sm:items-start">
|
||||||
<div className="text-center sm:text-left">
|
<div className="text-center sm:text-left">
|
||||||
|
|
|
@ -5,13 +5,20 @@ import { useState } from "react";
|
||||||
import React, { SyntheticEvent } from "react";
|
import React, { SyntheticEvent } from "react";
|
||||||
|
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
import { TextField } from "@calcom/ui/form/fields";
|
import { TextField } from "@calcom/ui/form/fields";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import { TeamWithMembers } from "@lib/queries/teams";
|
import { TeamWithMembers } from "@lib/queries/teams";
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
|
||||||
export default function MemberInvitationModal(props: { team: TeamWithMembers | null; onExit: () => void }) {
|
type MemberInvitationModalProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
team: TeamWithMembers | null;
|
||||||
|
onExit: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function MemberInvitationModal(props: MemberInvitationModalProps) {
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
|
@ -48,102 +55,88 @@ export default function MemberInvitationModal(props: { team: TeamWithMembers | n
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Dialog open={props.isOpen} onOpenChange={props.onExit}>
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
<DialogContent>
|
||||||
aria-labelledby="modal-title"
|
<div className="mb-4 sm:flex sm:items-start">
|
||||||
role="dialog"
|
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
||||||
aria-modal="true">
|
<UserIcon className="text-brandcontrast h-6 w-6" />
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
</div>
|
||||||
<div
|
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||||
aria-hidden="true"></div>
|
{t("invite_new_member")}
|
||||||
|
</h3>
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
<div>
|
||||||
​
|
<p className="text-sm text-gray-400">{t("invite_new_team_member")}</p>
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className="inline-block transform rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
|
|
||||||
<div className="mb-4 sm:flex sm:items-start">
|
|
||||||
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<UserIcon className="text-brandcontrast h-6 w-6" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
|
||||||
{t("invite_new_member")}
|
|
||||||
</h3>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-400">{t("invite_new_team_member")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={inviteMember}>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<TextField
|
|
||||||
label={t("email_or_username")}
|
|
||||||
id="inviteUser"
|
|
||||||
name="inviteUser"
|
|
||||||
placeholder="email@example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<label className="mb-1 block text-sm font-medium tracking-wide text-gray-700" htmlFor="role">
|
|
||||||
{t("role")}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="role"
|
|
||||||
className="focus:border-brand mt-1 block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black sm:text-sm">
|
|
||||||
<option value="MEMBER">{t("member")}</option>
|
|
||||||
<option value="ADMIN">{t("admin")}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex items-start">
|
|
||||||
<div className="flex h-5 items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
name="sendInviteEmail"
|
|
||||||
defaultChecked
|
|
||||||
id="sendInviteEmail"
|
|
||||||
className="focus:border-brand rounded-sm border-gray-300 text-black shadow-sm focus:ring-black sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-sm ltr:ml-2 rtl:mr-2">
|
|
||||||
<label htmlFor="sendInviteEmail" className="font-medium text-gray-700">
|
|
||||||
{t("send_invite_email")}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row rounded-sm bg-gray-50 px-3 py-2">
|
|
||||||
<InformationCircleIcon className="h-5 w-5 flex-shrink-0 fill-gray-400" aria-hidden="true" />
|
|
||||||
<span className="ml-2 text-sm leading-tight text-gray-500">
|
|
||||||
Note: This will cost an extra seat ($12/m) on your subscription if this invitee does not
|
|
||||||
have a pro account.{" "}
|
|
||||||
<a href="#" className="underline">
|
|
||||||
Learn More
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{errorMessage && (
|
|
||||||
<p className="text-sm text-red-700">
|
|
||||||
<span className="font-bold">Error: </span>
|
|
||||||
{errorMessage}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
className="ltr:ml-2 rtl:mr-2"
|
|
||||||
data-testid="invite-new-member-button">
|
|
||||||
{t("invite")}
|
|
||||||
</Button>
|
|
||||||
<Button type="button" color="secondary" onClick={props.onExit}>
|
|
||||||
{t("cancel")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<form onSubmit={inviteMember}>
|
||||||
</div>
|
<div className="space-y-4">
|
||||||
|
<TextField
|
||||||
|
label={t("email_or_username")}
|
||||||
|
id="inviteUser"
|
||||||
|
name="inviteUser"
|
||||||
|
placeholder="email@example.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<label className="mb-1 block text-sm font-medium tracking-wide text-gray-700" htmlFor="role">
|
||||||
|
{t("role")}
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="role"
|
||||||
|
className="focus:border-brand mt-1 block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black sm:text-sm">
|
||||||
|
<option value="MEMBER">{t("member")}</option>
|
||||||
|
<option value="ADMIN">{t("admin")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex items-start">
|
||||||
|
<div className="flex h-5 items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="sendInviteEmail"
|
||||||
|
defaultChecked
|
||||||
|
id="sendInviteEmail"
|
||||||
|
className="focus:border-brand rounded-sm border-gray-300 text-black shadow-sm focus:ring-black sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm ltr:ml-2 rtl:mr-2">
|
||||||
|
<label htmlFor="sendInviteEmail" className="font-medium text-gray-700">
|
||||||
|
{t("send_invite_email")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row rounded-sm bg-gray-50 px-3 py-2">
|
||||||
|
<InformationCircleIcon className="h-5 w-5 flex-shrink-0 fill-gray-400" aria-hidden="true" />
|
||||||
|
<span className="ml-2 text-sm leading-tight text-gray-500">
|
||||||
|
Note: This will cost an extra seat ($12/m) on your subscription if this invitee does not have
|
||||||
|
a pro account.{" "}
|
||||||
|
<a href="#" className="underline">
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{errorMessage && (
|
||||||
|
<p className="text-sm text-red-700">
|
||||||
|
<span className="font-bold">Error: </span>
|
||||||
|
{errorMessage}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="button" color="secondary" onClick={props.onExit}>
|
||||||
|
{t("cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
className="ltr:ml-2 rtl:mr-2"
|
||||||
|
data-testid="invite-new-member-button">
|
||||||
|
{t("invite")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,7 @@ export default function MemberListItem(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
{showChangeMemberRoleModal && (
|
{showChangeMemberRoleModal && (
|
||||||
<MemberChangeRoleModal
|
<MemberChangeRoleModal
|
||||||
|
isOpen={showChangeMemberRoleModal}
|
||||||
teamId={props.team?.id}
|
teamId={props.team?.id}
|
||||||
memberId={props.member.id}
|
memberId={props.member.id}
|
||||||
initialRole={props.member.role as MembershipRole}
|
initialRole={props.member.role as MembershipRole}
|
||||||
|
@ -171,9 +172,13 @@ export default function MemberListItem(props: Props) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showTeamAvailabilityModal && (
|
{showTeamAvailabilityModal && (
|
||||||
<ModalContainer wide noPadding>
|
<ModalContainer
|
||||||
|
wide
|
||||||
|
noPadding
|
||||||
|
isOpen={showTeamAvailabilityModal}
|
||||||
|
onExit={() => setShowTeamAvailabilityModal(false)}>
|
||||||
<TeamAvailabilityModal team={props.team} member={props.member} />
|
<TeamAvailabilityModal team={props.team} member={props.member} />
|
||||||
<div className="space-x-2 border-t p-5 rtl:space-x-reverse">
|
<div className="space-x-2 border-t py-5 rtl:space-x-reverse">
|
||||||
<Button onClick={() => setShowTeamAvailabilityModal(false)}>{t("done")}</Button>
|
<Button onClick={() => setShowTeamAvailabilityModal(false)}>{t("done")}</Button>
|
||||||
{props.team.membership.role !== MembershipRole.MEMBER && (
|
{props.team.membership.role !== MembershipRole.MEMBER && (
|
||||||
<Link href={`/settings/teams/${props.team.id}/availability`}>
|
<Link href={`/settings/teams/${props.team.id}/availability`}>
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import { UsersIcon } from "@heroicons/react/outline";
|
import { UsersIcon } from "@heroicons/react/outline";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import { Button } from "@calcom/ui";
|
||||||
import { Alert } from "@calcom/ui/Alert";
|
import { Alert } from "@calcom/ui/Alert";
|
||||||
|
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,24 +35,12 @@ export default function TeamCreate(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
<Dialog open={props.isOpen} onOpenChange={props.onClose}>
|
||||||
aria-labelledby="modal-title"
|
<DialogContent>
|
||||||
role="dialog"
|
|
||||||
aria-modal="true">
|
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
|
||||||
aria-hidden="true"></div>
|
|
||||||
|
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
|
||||||
​
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className="inline-block transform rounded-sm bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
|
|
||||||
<div className="mb-4 sm:flex sm:items-start">
|
<div className="mb-4 sm:flex sm:items-start">
|
||||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-neutral-100 sm:mx-0 sm:h-10 sm:w-10">
|
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
||||||
<UsersIcon className="h-6 w-6 text-neutral-900" />
|
<UsersIcon className="text-brandcontrast h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||||
|
@ -76,17 +67,21 @@ export default function TeamCreate(props: Props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errorMessage && <Alert severity="error" title={errorMessage} />}
|
{errorMessage && <Alert severity="error" title={errorMessage} />}
|
||||||
<div className="mt-5 flex flex-row-reverse sm:mt-4">
|
<DialogFooter>
|
||||||
<button type="submit" className="btn btn-primary">
|
<Button type="button" color="secondary" onClick={props.onClose}>
|
||||||
{t("create_team")}
|
|
||||||
</button>
|
|
||||||
<button onClick={props.onClose} type="button" className="btn btn-white ltr:mr-2">
|
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
className="ltr:ml-2 rtl:mr-2"
|
||||||
|
data-testid="create-new-team-button">
|
||||||
|
{t("create_team")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</DialogContent>
|
||||||
</div>
|
</Dialog>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,35 @@
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
interface Props extends React.PropsWithChildren<any> {
|
interface Props extends React.PropsWithChildren<any> {
|
||||||
wide?: boolean;
|
wide?: boolean;
|
||||||
scroll?: boolean;
|
scroll?: boolean;
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
|
isOpen: boolean;
|
||||||
|
onExit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ModalContainer(props: Props) {
|
export default function ModalContainer(props: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
<Dialog open={props.isOpen} onOpenChange={props.onExit}>
|
||||||
aria-labelledby="modal-title"
|
<DialogContent>
|
||||||
role="dialog"
|
<div
|
||||||
aria-modal="true">
|
className={classNames(
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
"inline-block transform bg-white text-left align-bottom transition-all sm:align-middle",
|
||||||
<div
|
{
|
||||||
className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
"sm:w-full sm:max-w-lg ": !props.wide,
|
||||||
aria-hidden="true"></div>
|
"sm:w-4xl sm:max-w-4xl": props.wide,
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
"overflow-scroll": props.scroll,
|
||||||
​
|
"!p-0": props.noPadding,
|
||||||
</span>
|
}
|
||||||
<div
|
)}>
|
||||||
className={classNames(
|
{props.children}
|
||||||
"min-w-96 inline-block transform rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:p-6 sm:align-middle",
|
</div>
|
||||||
{
|
</DialogContent>
|
||||||
"sm:w-full sm:max-w-lg ": !props.wide,
|
</Dialog>
|
||||||
"sm:w-4xl sm:max-w-4xl": props.wide,
|
|
||||||
"overflow-scroll": props.scroll,
|
|
||||||
"!p-0": props.noPadding,
|
|
||||||
}
|
|
||||||
)}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ export const Scheduler = ({ availability, setAvailability, timeZone, setTimeZone
|
||||||
</div>
|
</div>
|
||||||
{editSchedule >= 0 && (
|
{editSchedule >= 0 && (
|
||||||
<SetTimesModal
|
<SetTimesModal
|
||||||
|
isOpen={true}
|
||||||
startTime={
|
startTime={
|
||||||
openingHours[editSchedule]
|
openingHours[editSchedule]
|
||||||
? new Date(openingHours[editSchedule].startTime).getUTCHours() * 60 +
|
? new Date(openingHours[editSchedule].startTime).getUTCHours() * 60 +
|
||||||
|
|
|
@ -3,14 +3,15 @@ import dayjs from "dayjs";
|
||||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import showToast from "@calcom/lib/notification";
|
import showToast from "@calcom/lib/notification";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
interface SetTimesModalProps {
|
interface SetTimesModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
onChange: (times: { startTime: number; endTime: number }) => void;
|
onChange: (times: { startTime: number; endTime: number }) => void;
|
||||||
|
@ -86,153 +87,136 @@ export default function SetTimesModal(props: SetTimesModalProps) {
|
||||||
)(STEP);
|
)(STEP);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Dialog open={props.isOpen} onOpenChange={props.onExit}>
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
<DialogContent>
|
||||||
aria-labelledby="modal-title"
|
<div className="mb-4 sm:flex sm:items-start">
|
||||||
role="dialog"
|
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||||
aria-modal="true">
|
<ClockIcon className="h-6 w-6 text-black" />
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
|
||||||
aria-hidden="true"></div>
|
|
||||||
|
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
|
||||||
​
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
|
|
||||||
<div className="mb-4 sm:flex sm:items-start">
|
|
||||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<ClockIcon className="h-6 w-6 text-black" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
|
||||||
{t("change_bookings_availability")}
|
|
||||||
</h3>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-500">{t("set_work_schedule")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4 flex">
|
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label>
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||||
<div className="w-1/6">
|
{t("change_bookings_availability")}
|
||||||
<label htmlFor="startHours" className="sr-only">
|
</h3>
|
||||||
{t("hours")}
|
<div>
|
||||||
</label>
|
<p className="text-sm text-gray-500">{t("set_work_schedule")}</p>
|
||||||
<input
|
|
||||||
ref={startHoursRef}
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max={maximumStartTime.hour}
|
|
||||||
minLength={2}
|
|
||||||
name="hours"
|
|
||||||
id="startHours"
|
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
|
||||||
placeholder="9"
|
|
||||||
defaultValue={startHours}
|
|
||||||
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="mx-2 pt-1">:</span>
|
|
||||||
<div className="w-1/6">
|
|
||||||
<label htmlFor="startMinutes" className="sr-only">
|
|
||||||
{t("minutes")}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
ref={startMinsRef}
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max={maximumStartTime.minute}
|
|
||||||
step={STEP}
|
|
||||||
maxLength={2}
|
|
||||||
name="minutes"
|
|
||||||
id="startMinutes"
|
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
|
||||||
placeholder="30"
|
|
||||||
defaultValue={startMinutes}
|
|
||||||
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex">
|
|
||||||
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("end_time")}</label>
|
|
||||||
<div className="w-1/6">
|
|
||||||
<label htmlFor="endHours" className="sr-only">
|
|
||||||
{t("hours")}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
ref={endHoursRef}
|
|
||||||
type="number"
|
|
||||||
min={minimumEndTime.hour}
|
|
||||||
max="24"
|
|
||||||
maxLength={2}
|
|
||||||
name="hours"
|
|
||||||
id="endHours"
|
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
|
||||||
placeholder="17"
|
|
||||||
defaultValue={endHours}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (endHoursRef.current.value === "24") endMinsRef.current.value = "0";
|
|
||||||
setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef);
|
|
||||||
setEndMinuteDisable(endHoursRef.current.value === "24");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="mx-2 pt-1">:</span>
|
|
||||||
<div className="w-1/6">
|
|
||||||
<label htmlFor="endMinutes" className="sr-only">
|
|
||||||
{t("minutes")}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
ref={endMinsRef}
|
|
||||||
type="number"
|
|
||||||
min={minimumEndTime.minute}
|
|
||||||
max="59"
|
|
||||||
maxLength={2}
|
|
||||||
step={STEP}
|
|
||||||
name="minutes"
|
|
||||||
id="endMinutes"
|
|
||||||
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
|
||||||
placeholder="30"
|
|
||||||
defaultValue={endMinutes}
|
|
||||||
disabled={endMinuteDisable}
|
|
||||||
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
|
||||||
<Button
|
|
||||||
onClick={(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);
|
|
||||||
|
|
||||||
if (
|
|
||||||
isValidTime(
|
|
||||||
enteredStartHours * 60 + enteredStartMins,
|
|
||||||
enteredEndHours * 60 + enteredEndMins
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
props.onChange({
|
|
||||||
startTime: enteredStartHours * 60 + enteredStartMins,
|
|
||||||
endTime: enteredEndHours * 60 + enteredEndMins,
|
|
||||||
});
|
|
||||||
props.onExit(0);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
type="submit">
|
|
||||||
{t("save")}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={props.onExit} type="button" color="secondary" className="ltr:mr-2">
|
|
||||||
{t("cancel")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mb-4 flex">
|
||||||
</div>
|
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label>
|
||||||
|
<div className="w-1/6">
|
||||||
|
<label htmlFor="startHours" className="sr-only">
|
||||||
|
{t("hours")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
ref={startHoursRef}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max={maximumStartTime.hour}
|
||||||
|
minLength={2}
|
||||||
|
name="hours"
|
||||||
|
id="startHours"
|
||||||
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
|
placeholder="9"
|
||||||
|
defaultValue={startHours}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="mx-2 pt-1">:</span>
|
||||||
|
<div className="w-1/6">
|
||||||
|
<label htmlFor="startMinutes" className="sr-only">
|
||||||
|
{t("minutes")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
ref={startMinsRef}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max={maximumStartTime.minute}
|
||||||
|
step={STEP}
|
||||||
|
maxLength={2}
|
||||||
|
name="minutes"
|
||||||
|
id="startMinutes"
|
||||||
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
|
placeholder="30"
|
||||||
|
defaultValue={startMinutes}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("end_time")}</label>
|
||||||
|
<div className="w-1/6">
|
||||||
|
<label htmlFor="endHours" className="sr-only">
|
||||||
|
{t("hours")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
ref={endHoursRef}
|
||||||
|
type="number"
|
||||||
|
min={minimumEndTime.hour}
|
||||||
|
max="24"
|
||||||
|
maxLength={2}
|
||||||
|
name="hours"
|
||||||
|
id="endHours"
|
||||||
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
|
placeholder="17"
|
||||||
|
defaultValue={endHours}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (endHoursRef.current.value === "24") endMinsRef.current.value = "0";
|
||||||
|
setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef);
|
||||||
|
setEndMinuteDisable(endHoursRef.current.value === "24");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="mx-2 pt-1">:</span>
|
||||||
|
<div className="w-1/6">
|
||||||
|
<label htmlFor="endMinutes" className="sr-only">
|
||||||
|
{t("minutes")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
ref={endMinsRef}
|
||||||
|
type="number"
|
||||||
|
min={minimumEndTime.minute}
|
||||||
|
max="59"
|
||||||
|
maxLength={2}
|
||||||
|
step={STEP}
|
||||||
|
name="minutes"
|
||||||
|
id="endMinutes"
|
||||||
|
className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
|
||||||
|
placeholder="30"
|
||||||
|
defaultValue={endMinutes}
|
||||||
|
disabled={endMinuteDisable}
|
||||||
|
onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
onClick={(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);
|
||||||
|
|
||||||
|
if (
|
||||||
|
isValidTime(enteredStartHours * 60 + enteredStartMins, enteredEndHours * 60 + enteredEndMins)
|
||||||
|
) {
|
||||||
|
props.onChange({
|
||||||
|
startTime: enteredStartHours * 60 + enteredStartMins,
|
||||||
|
endTime: enteredEndHours * 60 + enteredEndMins,
|
||||||
|
});
|
||||||
|
props.onExit(0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="submit">
|
||||||
|
{t("save")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={props.onExit} type="button" color="secondary" className="ltr:mr-2">
|
||||||
|
{t("cancel")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@ import Head from "next/head";
|
||||||
import React, { FC, useEffect, useState } from "react";
|
import React, { FC, useEffect, useState } from "react";
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import getStripe from "@calcom/stripe/client";
|
import getStripe from "@calcom/stripe/client";
|
||||||
|
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||||
import PaymentComponent from "@ee/components/stripe/Payment";
|
import PaymentComponent from "@ee/components/stripe/Payment";
|
||||||
import { PaymentPageProps } from "@ee/pages/payment/[uid]";
|
import { PaymentPageProps } from "@ee/pages/payment/[uid]";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
import useTheme from "@lib/hooks/useTheme";
|
import useTheme from "@lib/hooks/useTheme";
|
||||||
import { isBrowserLocale24h } from "@lib/timeFormat";
|
import { isBrowserLocale24h } from "@lib/timeFormat";
|
||||||
|
|
||||||
|
@ -43,90 +44,86 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<main className="mx-auto max-w-3xl py-24">
|
<main className="mx-auto max-w-3xl py-24">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div>
|
||||||
<div
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
||||||
className="inline-block transform overflow-hidden rounded-sm border border-neutral-200 bg-white px-8 pt-5 pb-4 text-left align-bottom transition-all dark:border-neutral-700 dark:bg-gray-800 sm:my-8 sm:w-full sm:max-w-lg sm:py-6 sm:align-middle"
|
<CreditCardIcon className="h-8 w-8 text-green-600" />
|
||||||
role="dialog"
|
</div>
|
||||||
aria-modal="true"
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
aria-labelledby="modal-headline">
|
<h3
|
||||||
<div>
|
className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
id="modal-headline">
|
||||||
<CreditCardIcon className="h-8 w-8 text-green-600" />
|
{t("payment")}
|
||||||
</div>
|
</h3>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3">
|
||||||
<h3
|
<p className="text-sm text-neutral-600 dark:text-gray-300">{t("pay_later_instructions")}</p>
|
||||||
className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
|
|
||||||
id="modal-headline">
|
|
||||||
{t("payment")}
|
|
||||||
</h3>
|
|
||||||
<div className="mt-3">
|
|
||||||
<p className="text-sm text-neutral-600 dark:text-gray-300">
|
|
||||||
{t("pay_later_instructions")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 grid grid-cols-3 border-t border-b py-4 text-left text-gray-700 dark:border-gray-900 dark:text-gray-300">
|
|
||||||
<div className="font-medium">{t("what")}</div>
|
|
||||||
<div className="col-span-2 mb-6">{eventName}</div>
|
|
||||||
<div className="font-medium">{t("when")}</div>
|
|
||||||
<div className="col-span-2 mb-6">
|
|
||||||
{date.format("dddd, DD MMMM YYYY")}
|
|
||||||
<br />
|
|
||||||
{date.format(is24h ? "H:mm" : "h:mma")} - {props.eventType.length} mins{" "}
|
|
||||||
<span className="text-gray-500">
|
|
||||||
({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{props.booking.location && (
|
|
||||||
<>
|
|
||||||
<div className="font-medium">{t("where")}</div>
|
|
||||||
<div className="col-span-2 mb-6">{props.booking.location}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className="font-medium">{t("price")}</div>
|
|
||||||
<div className="col-span-2 mb-6">
|
|
||||||
<IntlProvider locale="en">
|
|
||||||
<FormattedNumber
|
|
||||||
value={props.eventType.price / 100.0}
|
|
||||||
style="currency"
|
|
||||||
currency={props.eventType.currency.toUpperCase()}
|
|
||||||
/>
|
|
||||||
</IntlProvider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="mt-4 grid grid-cols-3 border-t border-b py-4 text-left text-gray-700 dark:border-gray-900 dark:text-gray-300">
|
||||||
{props.payment.success && !props.payment.refunded && (
|
<div className="font-medium">{t("what")}</div>
|
||||||
<div className="mt-4 text-center text-gray-700 dark:text-gray-300">{t("paid")}</div>
|
<div className="col-span-2 mb-6">{eventName}</div>
|
||||||
|
<div className="font-medium">{t("when")}</div>
|
||||||
|
<div className="col-span-2 mb-6">
|
||||||
|
{date.format("dddd, DD MMMM YYYY")}
|
||||||
|
<br />
|
||||||
|
{date.format(is24h ? "H:mm" : "h:mma")} - {props.eventType.length} mins{" "}
|
||||||
|
<span className="text-gray-500">
|
||||||
|
({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{props.booking.location && (
|
||||||
|
<>
|
||||||
|
<div className="font-medium">{t("where")}</div>
|
||||||
|
<div className="col-span-2 mb-6">{props.booking.location}</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{!props.payment.success && (
|
<div className="font-medium">{t("price")}</div>
|
||||||
<Elements stripe={getStripe(props.payment.data.stripe_publishable_key)}>
|
<div className="col-span-2 mb-6">
|
||||||
<PaymentComponent
|
<IntlProvider locale="en">
|
||||||
payment={props.payment}
|
<FormattedNumber
|
||||||
eventType={props.eventType}
|
value={props.eventType.price / 100.0}
|
||||||
user={props.user}
|
style="currency"
|
||||||
location={props.booking.location}
|
currency={props.eventType.currency.toUpperCase()}
|
||||||
/>
|
/>
|
||||||
</Elements>
|
</IntlProvider>
|
||||||
)}
|
|
||||||
{props.payment.refunded && (
|
|
||||||
<div className="mt-4 text-center text-gray-700 dark:text-gray-300">{t("refunded")}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!props.profile.hideBranding && (
|
|
||||||
<div className="mt-4 border-t pt-4 text-center text-xs text-gray-400 dark:border-gray-900 dark:text-white">
|
|
||||||
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-5 sm:mt-6">
|
||||||
</div>
|
<div>
|
||||||
|
{props.payment.success && !props.payment.refunded && (
|
||||||
|
<div className="mt-4 text-center text-gray-700 dark:text-gray-300">{t("paid")}</div>
|
||||||
|
)}
|
||||||
|
{!props.payment.success && (
|
||||||
|
<Elements stripe={getStripe(props.payment.data.stripe_publishable_key)}>
|
||||||
|
<PaymentComponent
|
||||||
|
payment={props.payment}
|
||||||
|
eventType={props.eventType}
|
||||||
|
user={props.user}
|
||||||
|
location={props.booking.location}
|
||||||
|
/>
|
||||||
|
</Elements>
|
||||||
|
)}
|
||||||
|
{props.payment.refunded && (
|
||||||
|
<div className="mt-4 text-center text-gray-700 dark:text-gray-300">{t("refunded")}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
{!props.profile.hideBranding && (
|
||||||
|
<div className="mt-4 border-t pt-4 text-center text-xs text-gray-400 dark:border-gray-900 dark:text-white">
|
||||||
|
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DialogFooter>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { Button } from "@calcom/ui/Button";
|
import { Button } from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
import { TextField } from "@calcom/ui/form/fields";
|
import { TextField } from "@calcom/ui/form/fields";
|
||||||
|
|
||||||
import { asStringOrUndefined } from "@lib/asStringOrNull";
|
import { asStringOrUndefined } from "@lib/asStringOrNull";
|
||||||
|
@ -38,123 +39,107 @@ export default function Type(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
/>
|
/>
|
||||||
<CustomBranding lightVal={props.profile?.brandColor} darkVal={props.profile?.darkBrandColor} />
|
<CustomBranding lightVal={props.profile?.brandColor} darkVal={props.profile?.darkBrandColor} />
|
||||||
<main className="mx-auto my-24 max-w-3xl">
|
<main className="mx-auto my-24 max-w-3xl">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
{error && (
|
||||||
<div
|
<div>
|
||||||
className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
role="dialog"
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
aria-modal="true"
|
</div>
|
||||||
aria-labelledby="modal-headline">
|
<div className="mt-5 flex justify-center">
|
||||||
{error && (
|
<DialogHeader title={error} />
|
||||||
<div>
|
</div>
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
</div>
|
||||||
<XIcon className="h-6 w-6 text-red-600" />
|
)}
|
||||||
|
{!error && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 text-center">
|
||||||
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
||||||
|
{props.cancellationAllowed ? t("really_cancel_booking") : t("cannot_cancel_booking")}
|
||||||
|
</h3>
|
||||||
|
<div className="mt-2">
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
{props.cancellationAllowed ? t("reschedule_instead") : t("event_is_in_the_past")}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-4 border-t border-b py-4">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
<DialogHeader title={props.booking?.title} />
|
||||||
{error}
|
<p className="text-gray-500">
|
||||||
</h3>
|
<CalendarIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
|
||||||
|
{dayjs(props.booking?.startTime).format(
|
||||||
|
detectBrowserTimeFormat + ", dddd DD MMMM YYYY"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{props.cancellationAllowed && (
|
||||||
|
<div className="mt-5 sm:mt-6">
|
||||||
|
<TextField
|
||||||
|
name={t("cancellation_reason")}
|
||||||
|
placeholder={t("cancellation_reason_placeholder")}
|
||||||
|
value={cancellationReason}
|
||||||
|
onChange={(e) => setCancellationReason(e.target.value)}
|
||||||
|
className="mb-5 sm:mb-6"
|
||||||
|
/>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
<Button color="secondary" onClick={() => router.push("/reschedule/" + uid)}>
|
||||||
|
{t("reschedule_this")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
data-testid="cancel"
|
||||||
|
onClick={async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const payload = {
|
||||||
|
uid: uid,
|
||||||
|
reason: cancellationReason,
|
||||||
|
};
|
||||||
|
telemetry.withJitsu((jitsu) =>
|
||||||
|
jitsu.track(telemetryEventTypes.bookingCancelled, collectPageParameters())
|
||||||
|
);
|
||||||
|
const res = await fetch("/api/cancel", {
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
await router.push(
|
||||||
|
`/cancel/success?name=${props.profile.name}&title=${
|
||||||
|
props.booking.title
|
||||||
|
}&eventPage=${props.profile.slug}&team=${
|
||||||
|
props.booking.eventType?.team ? 1 : 0
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
setError(
|
||||||
|
`${t("error_with_status_code_occured", { status: res.status })} ${t(
|
||||||
|
"please_try_again"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loading={loading}>
|
||||||
|
{t("cancel_event")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!error && (
|
</>
|
||||||
<>
|
)}
|
||||||
<div>
|
</DialogContent>
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
</Dialog>
|
||||||
<XIcon className="h-6 w-6 text-red-600" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
|
||||||
{props.cancellationAllowed
|
|
||||||
? t("really_cancel_booking")
|
|
||||||
: t("cannot_cancel_booking")}
|
|
||||||
</h3>
|
|
||||||
<div className="mt-2">
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
{props.cancellationAllowed ? t("reschedule_instead") : t("event_is_in_the_past")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 border-t border-b py-4">
|
|
||||||
<h2 className="font-cal mb-2 text-lg font-medium text-gray-600">
|
|
||||||
{props.booking?.title}
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-500">
|
|
||||||
<CalendarIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
|
|
||||||
{dayjs(props.booking?.startTime).format(
|
|
||||||
detectBrowserTimeFormat + ", dddd DD MMMM YYYY"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{props.cancellationAllowed && (
|
|
||||||
<div className="mt-5 sm:mt-6">
|
|
||||||
<TextField
|
|
||||||
name={t("cancellation_reason")}
|
|
||||||
placeholder={t("cancellation_reason_placeholder")}
|
|
||||||
value={cancellationReason}
|
|
||||||
onChange={(e) => setCancellationReason(e.target.value)}
|
|
||||||
className="mb-5 sm:mb-6"
|
|
||||||
/>
|
|
||||||
<div className="space-x-2 text-center rtl:space-x-reverse">
|
|
||||||
<Button color="secondary" onClick={() => router.push("/reschedule/" + uid)}>
|
|
||||||
{t("reschedule_this")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
data-testid="cancel"
|
|
||||||
onClick={async () => {
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
uid: uid,
|
|
||||||
reason: cancellationReason,
|
|
||||||
};
|
|
||||||
|
|
||||||
telemetry.withJitsu((jitsu) =>
|
|
||||||
jitsu.track(telemetryEventTypes.bookingCancelled, collectPageParameters())
|
|
||||||
);
|
|
||||||
|
|
||||||
const res = await fetch("/api/cancel", {
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
method: "DELETE",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status >= 200 && res.status < 300) {
|
|
||||||
await router.push(
|
|
||||||
`/cancel/success?name=${props.profile.name}&title=${
|
|
||||||
props.booking.title
|
|
||||||
}&eventPage=${props.profile.slug}&team=${
|
|
||||||
props.booking.eventType?.team ? 1 : 0
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setLoading(false);
|
|
||||||
setError(
|
|
||||||
`${t("error_with_status_code_occured", { status: res.status })} ${t(
|
|
||||||
"please_try_again"
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
loading={loading}>
|
|
||||||
{t("cancel_event")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
import { HeadSeo } from "@components/seo/head-seo";
|
||||||
|
|
||||||
|
@ -23,46 +23,34 @@ export default function CancelSuccess() {
|
||||||
description={`${t("cancelled")} ${title} | ${name}`}
|
description={`${t("cancelled")} ${title} | ${name}`}
|
||||||
/>
|
/>
|
||||||
<main className="mx-auto my-24 max-w-3xl">
|
<main className="mx-auto my-24 max-w-3xl">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
||||||
<div
|
<CheckIcon className="h-6 w-6 text-green-600" />
|
||||||
className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
aria-labelledby="modal-headline">
|
|
||||||
<div>
|
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
|
||||||
<CheckIcon className="h-6 w-6 text-green-600" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
|
||||||
{t("cancellation_successful")}
|
|
||||||
</h3>
|
|
||||||
{!loading && !session?.user && (
|
|
||||||
<div className="mt-2">
|
|
||||||
<p className="text-sm text-gray-500">{t("free_to_pick_another_event_type")}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 text-center sm:mt-6">
|
|
||||||
<div className="mt-5">
|
|
||||||
{!loading && !session?.user && <Button href={eventPage as string}>Pick another</Button>}
|
|
||||||
{!loading && session?.user && (
|
|
||||||
<Button data-testid="back-to-bookings" href="/bookings" EndIcon={ArrowRightIcon}>
|
|
||||||
{t("back_to_bookings")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-5 flex justify-center">
|
||||||
</div>
|
<DialogHeader title={t("cancellation_successful")} />
|
||||||
|
</div>
|
||||||
|
{!loading && !session?.user && (
|
||||||
|
<div className="-mt-6 flex justify-center">
|
||||||
|
<p className="text-center text-sm text-gray-500">{t("free_to_pick_another_event_type")}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
{!loading && !session?.user && <Button href={eventPage as string}>Pick another</Button>}
|
||||||
|
{!loading && session?.user && (
|
||||||
|
<Button data-testid="back-to-bookings" href="/bookings" EndIcon={ArrowRightIcon}>
|
||||||
|
{t("back_to_bookings")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DialogFooter>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -142,7 +142,11 @@ export function TeamSettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showMemberInvitationModal && (
|
{showMemberInvitationModal && (
|
||||||
<MemberInvitationModal team={team} onExit={() => setShowMemberInvitationModal(false)} />
|
<MemberInvitationModal
|
||||||
|
isOpen={showMemberInvitationModal}
|
||||||
|
team={team}
|
||||||
|
onExit={() => setShowMemberInvitationModal(false)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -58,7 +58,9 @@ export default function Teams() {
|
||||||
className="my-4"
|
className="my-4"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showCreateTeamModal && <TeamCreateModal onClose={() => setShowCreateTeamModal(false)} />}
|
{showCreateTeamModal && (
|
||||||
|
<TeamCreateModal isOpen={showCreateTeamModal} onClose={() => setShowCreateTeamModal(false)} />
|
||||||
|
)}
|
||||||
<div className={classNames("my-4 flex justify-end", isFreePlan && "opacity-50")}>
|
<div className={classNames("my-4 flex justify-end", isFreePlan && "opacity-50")}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isFreePlan}
|
disabled={isFreePlan}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||||
import { EmailInput } from "@calcom/ui/form/fields";
|
import { EmailInput } from "@calcom/ui/form/fields";
|
||||||
|
|
||||||
import { asStringOrThrow, asStringOrNull } from "@lib/asStringOrNull";
|
import { asStringOrThrow, asStringOrNull } from "@lib/asStringOrNull";
|
||||||
|
@ -99,165 +100,158 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
|
||||||
/>
|
/>
|
||||||
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} />
|
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} />
|
||||||
<main className="mx-auto max-w-3xl py-24">
|
<main className="mx-auto max-w-3xl py-24">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
||||||
<div
|
{!needsConfirmation && <CheckIcon className="h-8 w-8 text-green-600" />}
|
||||||
className="inline-block transform overflow-hidden rounded-sm border border-neutral-200 bg-white px-8 pt-5 pb-4 text-left align-bottom transition-all dark:border-neutral-700 dark:bg-gray-800 sm:my-8 sm:w-full sm:max-w-lg sm:py-6 sm:align-middle"
|
{needsConfirmation && <ClockIcon className="h-8 w-8 text-green-600" />}
|
||||||
role="dialog"
|
</div>
|
||||||
aria-modal="true"
|
<div className="mt-5 flex justify-center">
|
||||||
aria-labelledby="modal-headline">
|
<h3
|
||||||
<div>
|
className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
id="modal-headline">
|
||||||
{!needsConfirmation && <CheckIcon className="h-8 w-8 text-green-600" />}
|
{needsConfirmation ? t("submitted") : t("meeting_is_scheduled")}
|
||||||
{needsConfirmation && <ClockIcon className="h-8 w-8 text-green-600" />}
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3">
|
||||||
<h3
|
<p className="text-sm text-neutral-600 dark:text-gray-300">
|
||||||
className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
|
{needsConfirmation
|
||||||
id="modal-headline">
|
? props.profile.name !== null
|
||||||
{needsConfirmation ? t("submitted") : t("meeting_is_scheduled")}
|
? t("user_needs_to_confirm_or_reject_booking", { user: props.profile.name })
|
||||||
</h3>
|
: t("needs_to_be_confirmed_or_rejected")
|
||||||
<div className="mt-3">
|
: t("emailed_you_and_attendees")}
|
||||||
<p className="text-sm text-neutral-600 dark:text-gray-300">
|
</p>
|
||||||
{needsConfirmation
|
</div>
|
||||||
? props.profile.name !== null
|
<div className="mt-4 grid grid-cols-3 border-t border-b py-4 text-left text-gray-700 dark:border-gray-900 dark:text-gray-300">
|
||||||
? t("user_needs_to_confirm_or_reject_booking", { user: props.profile.name })
|
<div className="font-medium">{t("what")}</div>
|
||||||
: t("needs_to_be_confirmed_or_rejected")
|
<div className="col-span-2 mb-6">{eventName}</div>
|
||||||
: t("emailed_you_and_attendees")}
|
<div className="font-medium">{t("when")}</div>
|
||||||
</p>
|
<div className="col-span-2">
|
||||||
</div>
|
{date.format("dddd, DD MMMM YYYY")}
|
||||||
<div className="mt-4 grid grid-cols-3 border-t border-b py-4 text-left text-gray-700 dark:border-gray-900 dark:text-gray-300">
|
<br />
|
||||||
<div className="font-medium">{t("what")}</div>
|
{date.format(is24h ? "H:mm" : "h:mma")} - {props.eventType.length} mins{" "}
|
||||||
<div className="col-span-2 mb-6">{eventName}</div>
|
<span className="text-gray-500">
|
||||||
<div className="font-medium">{t("when")}</div>
|
({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()})
|
||||||
<div className="col-span-2">
|
</span>
|
||||||
{date.format("dddd, DD MMMM YYYY")}
|
</div>
|
||||||
<br />
|
{location && (
|
||||||
{date.format(is24h ? "H:mm" : "h:mma")} - {props.eventType.length} mins{" "}
|
<>
|
||||||
<span className="text-gray-500">
|
<div className="font-medium">{t("where")}</div>
|
||||||
({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()})
|
<div className="col-span-2">
|
||||||
</span>
|
{location.startsWith("http") ? (
|
||||||
</div>
|
<a title="Meeting Link" href={location}>
|
||||||
{location && (
|
{location}
|
||||||
<>
|
</a>
|
||||||
<div className="font-medium">{t("where")}</div>
|
) : (
|
||||||
<div className="col-span-2">
|
location
|
||||||
{location.startsWith("http") ? (
|
)}
|
||||||
<a title="Meeting Link" href={location}>
|
|
||||||
{location}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
location
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!needsConfirmation && (
|
||||||
|
<div className="mt-5 flex border-b pt-2 pb-4 text-center dark:border-gray-900 sm:mt-0 sm:pt-4">
|
||||||
|
<span className="flex self-center font-medium text-gray-700 ltr:mr-2 rtl:ml-2 dark:text-gray-50">
|
||||||
|
{t("add_to_calendar")}
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-grow justify-center text-center">
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
`https://calendar.google.com/calendar/r/eventedit?dates=${date
|
||||||
|
.utc()
|
||||||
|
.format("YYYYMMDDTHHmmss[Z]")}/${date
|
||||||
|
.add(props.eventType.length, "minute")
|
||||||
|
.utc()
|
||||||
|
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
|
||||||
|
props.eventType.description
|
||||||
|
}` + (typeof location === "string" ? "&location=" + encodeURIComponent(location) : "")
|
||||||
|
}>
|
||||||
|
<a className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white">
|
||||||
|
<svg
|
||||||
|
className="-mt-1 inline-block h-4 w-4"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>Google</title>
|
||||||
|
<path d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
encodeURI(
|
||||||
|
"https://outlook.live.com/calendar/0/deeplink/compose?body=" +
|
||||||
|
props.eventType.description +
|
||||||
|
"&enddt=" +
|
||||||
|
date.add(props.eventType.length, "minute").utc().format() +
|
||||||
|
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
|
||||||
|
date.utc().format() +
|
||||||
|
"&subject=" +
|
||||||
|
eventName
|
||||||
|
) + (location ? "&location=" + location : "")
|
||||||
|
}>
|
||||||
|
<a
|
||||||
|
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
||||||
|
target="_blank">
|
||||||
|
<svg
|
||||||
|
className="mr-1 -mt-1 inline-block h-4 w-4"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>Microsoft Outlook</title>
|
||||||
|
<path d="M7.88 12.04q0 .45-.11.87-.1.41-.33.74-.22.33-.58.52-.37.2-.87.2t-.85-.2q-.35-.21-.57-.55-.22-.33-.33-.75-.1-.42-.1-.86t.1-.87q.1-.43.34-.76.22-.34.59-.54.36-.2.87-.2t.86.2q.35.21.57.55.22.34.31.77.1.43.1.88zM24 12v9.38q0 .46-.33.8-.33.32-.8.32H7.13q-.46 0-.8-.33-.32-.33-.32-.8V18H1q-.41 0-.7-.3-.3-.29-.3-.7V7q0-.41.3-.7Q.58 6 1 6h6.5V2.55q0-.44.3-.75.3-.3.75-.3h12.9q.44 0 .75.3.3.3.3.75V10.85l1.24.72h.01q.1.07.18.18.07.12.07.25zm-6-8.25v3h3v-3zm0 4.5v3h3v-3zm0 4.5v1.83l3.05-1.83zm-5.25-9v3h3.75v-3zm0 4.5v3h3.75v-3zm0 4.5v2.03l2.41 1.5 1.34-.8v-2.73zM9 3.75V6h2l.13.01.12.04v-2.3zM5.98 15.98q.9 0 1.6-.3.7-.32 1.19-.86.48-.55.73-1.28.25-.74.25-1.61 0-.83-.25-1.55-.24-.71-.71-1.24t-1.15-.83q-.68-.3-1.55-.3-.92 0-1.64.3-.71.3-1.2.85-.5.54-.75 1.3-.25.74-.25 1.63 0 .85.26 1.56.26.72.74 1.23.48.52 1.17.81.69.3 1.56.3zM7.5 21h12.39L12 16.08V17q0 .41-.3.7-.29.3-.7.3H7.5zm15-.13v-7.24l-5.9 3.54Z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
encodeURI(
|
||||||
|
"https://outlook.office.com/calendar/0/deeplink/compose?body=" +
|
||||||
|
props.eventType.description +
|
||||||
|
"&enddt=" +
|
||||||
|
date.add(props.eventType.length, "minute").utc().format() +
|
||||||
|
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
|
||||||
|
date.utc().format() +
|
||||||
|
"&subject=" +
|
||||||
|
eventName
|
||||||
|
) + (location ? "&location=" + location : "")
|
||||||
|
}>
|
||||||
|
<a
|
||||||
|
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
||||||
|
target="_blank">
|
||||||
|
<svg
|
||||||
|
className="mr-1 -mt-1 inline-block h-4 w-4"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>Microsoft Office</title>
|
||||||
|
<path d="M21.53 4.306v15.363q0 .807-.472 1.433-.472.627-1.253.85l-6.888 1.974q-.136.037-.29.055-.156.019-.293.019-.396 0-.72-.105-.321-.106-.656-.292l-4.505-2.544q-.248-.137-.391-.366-.143-.23-.143-.515 0-.434.304-.738.304-.305.739-.305h5.831V4.964l-4.38 1.563q-.533.187-.856.658-.322.472-.322 1.03v8.078q0 .496-.248.912-.25.416-.683.651l-2.072 1.13q-.286.148-.571.148-.497 0-.844-.347-.348-.347-.348-.844V6.563q0-.62.33-1.19.328-.571.874-.881L11.07.285q.248-.136.534-.21.285-.075.57-.075.211 0 .38.031.166.031.364.093l6.888 1.899q.384.11.7.329.317.217.547.52.23.305.353.67.125.367.125.764zm-1.588 15.363V4.306q0-.273-.16-.478-.163-.204-.423-.28l-3.388-.93q-.397-.111-.794-.23-.397-.117-.794-.216v19.68l4.976-1.427q.26-.074.422-.28.161-.204.161-.477z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<Link href={"data:text/calendar," + eventLink()}>
|
||||||
|
<a
|
||||||
|
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
||||||
|
download={props.eventType.title + ".ics"}>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1000 1000"
|
||||||
|
className="mr-1 -mt-1 inline-block h-4 w-4">
|
||||||
|
<title>{t("other")}</title>
|
||||||
|
<path d="M971.3,154.9c0-34.7-28.2-62.9-62.9-62.9H611.7c-1.3,0-2.6,0.1-3.9,0.2V10L28.7,87.3v823.4L607.8,990v-84.6c1.3,0.1,2.6,0.2,3.9,0.2h296.7c34.7,0,62.9-28.2,62.9-62.9V154.9z M607.8,636.1h44.6v-50.6h-44.6v-21.9h44.6v-50.6h-44.6v-92h277.9v230.2c0,3.8-3.1,7-7,7H607.8V636.1z M117.9,644.7l-50.6-2.4V397.5l50.6-2.2V644.7z M288.6,607.3c17.6,0.6,37.3-2.8,49.1-7.2l9.1,48c-11,5.1-35.6,9.9-66.9,8.3c-85.4-4.3-127.5-60.7-127.5-132.6c0-86.2,57.8-136.7,133.2-140.1c30.3-1.3,53.7,4,64.3,9.2l-12.2,48.9c-12.1-4.9-28.8-9.2-49.5-8.6c-45.3,1.2-79.5,30.1-79.5,87.4C208.8,572.2,237.8,605.7,288.6,607.3z M455.5,665.2c-32.4-1.6-63.7-11.3-79.1-20.5l12.6-50.7c16.8,9.1,42.9,18.5,70.4,19.4c30.1,1,46.3-10.7,46.3-29.3c0-17.8-14-28.1-48.8-40.6c-46.9-16.4-76.8-41.7-76.8-81.5c0-46.6,39.3-84.1,106.8-87.1c33.3-1.5,58.3,4.2,76.5,11.2l-15.4,53.3c-12.1-5.3-33.5-12.8-62.3-12c-28.3,0.8-41.9,13.6-41.9,28.1c0,17.8,16.1,25.5,53.6,39c52.9,18.5,78.4,45.3,78.4,86.4C575.6,629.7,536.2,669.2,455.5,665.2z M935.3,842.7c0,14.9-12.1,27-27,27H611.7c-1.3,0-2.6-0.2-3.9-0.4V686.2h270.9c19.2,0,34.9-15.6,34.9-34.9V398.4c0-19.2-15.6-34.9-34.9-34.9h-47.1v-32.3H808v32.3h-44.8v-32.3h-22.7v32.3h-43.3v-32.3h-22.7v32.3H628v-32.3h-20.2v-203c1.31.2,2.6-0.4,3.9-0.4h296.7c14.9,0,27,12.1,27,27L935.3,842.7L935.3,842.7z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{!needsConfirmation && (
|
</div>
|
||||||
<div className="mt-5 flex border-b pt-2 pb-4 text-center dark:border-gray-900 sm:mt-0 sm:pt-4">
|
)}
|
||||||
<span className="flex self-center font-medium text-gray-700 ltr:mr-2 rtl:ml-2 dark:text-gray-50">
|
<div className="flex justify-center">
|
||||||
{t("add_to_calendar")}
|
<DialogFooter>
|
||||||
</span>
|
|
||||||
<div className="flex flex-grow justify-center text-center">
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
`https://calendar.google.com/calendar/r/eventedit?dates=${date
|
|
||||||
.utc()
|
|
||||||
.format("YYYYMMDDTHHmmss[Z]")}/${date
|
|
||||||
.add(props.eventType.length, "minute")
|
|
||||||
.utc()
|
|
||||||
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
|
|
||||||
props.eventType.description
|
|
||||||
}` +
|
|
||||||
(typeof location === "string" ? "&location=" + encodeURIComponent(location) : "")
|
|
||||||
}>
|
|
||||||
<a className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white">
|
|
||||||
<svg
|
|
||||||
className="-mt-1 inline-block h-4 w-4"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<title>Google</title>
|
|
||||||
<path d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
encodeURI(
|
|
||||||
"https://outlook.live.com/calendar/0/deeplink/compose?body=" +
|
|
||||||
props.eventType.description +
|
|
||||||
"&enddt=" +
|
|
||||||
date.add(props.eventType.length, "minute").utc().format() +
|
|
||||||
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
|
|
||||||
date.utc().format() +
|
|
||||||
"&subject=" +
|
|
||||||
eventName
|
|
||||||
) + (location ? "&location=" + location : "")
|
|
||||||
}>
|
|
||||||
<a
|
|
||||||
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
|
||||||
target="_blank">
|
|
||||||
<svg
|
|
||||||
className="mr-1 -mt-1 inline-block h-4 w-4"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<title>Microsoft Outlook</title>
|
|
||||||
<path d="M7.88 12.04q0 .45-.11.87-.1.41-.33.74-.22.33-.58.52-.37.2-.87.2t-.85-.2q-.35-.21-.57-.55-.22-.33-.33-.75-.1-.42-.1-.86t.1-.87q.1-.43.34-.76.22-.34.59-.54.36-.2.87-.2t.86.2q.35.21.57.55.22.34.31.77.1.43.1.88zM24 12v9.38q0 .46-.33.8-.33.32-.8.32H7.13q-.46 0-.8-.33-.32-.33-.32-.8V18H1q-.41 0-.7-.3-.3-.29-.3-.7V7q0-.41.3-.7Q.58 6 1 6h6.5V2.55q0-.44.3-.75.3-.3.75-.3h12.9q.44 0 .75.3.3.3.3.75V10.85l1.24.72h.01q.1.07.18.18.07.12.07.25zm-6-8.25v3h3v-3zm0 4.5v3h3v-3zm0 4.5v1.83l3.05-1.83zm-5.25-9v3h3.75v-3zm0 4.5v3h3.75v-3zm0 4.5v2.03l2.41 1.5 1.34-.8v-2.73zM9 3.75V6h2l.13.01.12.04v-2.3zM5.98 15.98q.9 0 1.6-.3.7-.32 1.19-.86.48-.55.73-1.28.25-.74.25-1.61 0-.83-.25-1.55-.24-.71-.71-1.24t-1.15-.83q-.68-.3-1.55-.3-.92 0-1.64.3-.71.3-1.2.85-.5.54-.75 1.3-.25.74-.25 1.63 0 .85.26 1.56.26.72.74 1.23.48.52 1.17.81.69.3 1.56.3zM7.5 21h12.39L12 16.08V17q0 .41-.3.7-.29.3-.7.3H7.5zm15-.13v-7.24l-5.9 3.54Z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
encodeURI(
|
|
||||||
"https://outlook.office.com/calendar/0/deeplink/compose?body=" +
|
|
||||||
props.eventType.description +
|
|
||||||
"&enddt=" +
|
|
||||||
date.add(props.eventType.length, "minute").utc().format() +
|
|
||||||
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
|
|
||||||
date.utc().format() +
|
|
||||||
"&subject=" +
|
|
||||||
eventName
|
|
||||||
) + (location ? "&location=" + location : "")
|
|
||||||
}>
|
|
||||||
<a
|
|
||||||
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
|
||||||
target="_blank">
|
|
||||||
<svg
|
|
||||||
className="mr-1 -mt-1 inline-block h-4 w-4"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<title>Microsoft Office</title>
|
|
||||||
<path d="M21.53 4.306v15.363q0 .807-.472 1.433-.472.627-1.253.85l-6.888 1.974q-.136.037-.29.055-.156.019-.293.019-.396 0-.72-.105-.321-.106-.656-.292l-4.505-2.544q-.248-.137-.391-.366-.143-.23-.143-.515 0-.434.304-.738.304-.305.739-.305h5.831V4.964l-4.38 1.563q-.533.187-.856.658-.322.472-.322 1.03v8.078q0 .496-.248.912-.25.416-.683.651l-2.072 1.13q-.286.148-.571.148-.497 0-.844-.347-.348-.347-.348-.844V6.563q0-.62.33-1.19.328-.571.874-.881L11.07.285q.248-.136.534-.21.285-.075.57-.075.211 0 .38.031.166.031.364.093l6.888 1.899q.384.11.7.329.317.217.547.52.23.305.353.67.125.367.125.764zm-1.588 15.363V4.306q0-.273-.16-.478-.163-.204-.423-.28l-3.388-.93q-.397-.111-.794-.23-.397-.117-.794-.216v19.68l4.976-1.427q.26-.074.422-.28.161-.204.161-.477z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
<Link href={"data:text/calendar," + eventLink()}>
|
|
||||||
<a
|
|
||||||
className="mx-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white"
|
|
||||||
download={props.eventType.title + ".ics"}>
|
|
||||||
<svg
|
|
||||||
version="1.1"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 1000 1000"
|
|
||||||
className="mr-1 -mt-1 inline-block h-4 w-4">
|
|
||||||
<title>{t("other")}</title>
|
|
||||||
<path d="M971.3,154.9c0-34.7-28.2-62.9-62.9-62.9H611.7c-1.3,0-2.6,0.1-3.9,0.2V10L28.7,87.3v823.4L607.8,990v-84.6c1.3,0.1,2.6,0.2,3.9,0.2h296.7c34.7,0,62.9-28.2,62.9-62.9V154.9z M607.8,636.1h44.6v-50.6h-44.6v-21.9h44.6v-50.6h-44.6v-92h277.9v230.2c0,3.8-3.1,7-7,7H607.8V636.1z M117.9,644.7l-50.6-2.4V397.5l50.6-2.2V644.7z M288.6,607.3c17.6,0.6,37.3-2.8,49.1-7.2l9.1,48c-11,5.1-35.6,9.9-66.9,8.3c-85.4-4.3-127.5-60.7-127.5-132.6c0-86.2,57.8-136.7,133.2-140.1c30.3-1.3,53.7,4,64.3,9.2l-12.2,48.9c-12.1-4.9-28.8-9.2-49.5-8.6c-45.3,1.2-79.5,30.1-79.5,87.4C208.8,572.2,237.8,605.7,288.6,607.3z M455.5,665.2c-32.4-1.6-63.7-11.3-79.1-20.5l12.6-50.7c16.8,9.1,42.9,18.5,70.4,19.4c30.1,1,46.3-10.7,46.3-29.3c0-17.8-14-28.1-48.8-40.6c-46.9-16.4-76.8-41.7-76.8-81.5c0-46.6,39.3-84.1,106.8-87.1c33.3-1.5,58.3,4.2,76.5,11.2l-15.4,53.3c-12.1-5.3-33.5-12.8-62.3-12c-28.3,0.8-41.9,13.6-41.9,28.1c0,17.8,16.1,25.5,53.6,39c52.9,18.5,78.4,45.3,78.4,86.4C575.6,629.7,536.2,669.2,455.5,665.2z M935.3,842.7c0,14.9-12.1,27-27,27H611.7c-1.3,0-2.6-0.2-3.9-0.4V686.2h270.9c19.2,0,34.9-15.6,34.9-34.9V398.4c0-19.2-15.6-34.9-34.9-34.9h-47.1v-32.3H808v32.3h-44.8v-32.3h-22.7v32.3h-43.3v-32.3h-22.7v32.3H628v-32.3h-20.2v-203c1.31.2,2.6-0.4,3.9-0.4h296.7c14.9,0,27,12.1,27,27L935.3,842.7L935.3,842.7z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!props.hideBranding && (
|
{!props.hideBranding && (
|
||||||
<div className="pt-4 text-center text-xs text-gray-400 dark:border-gray-900 dark:text-white">
|
<div className="pt-4 text-center text-xs text-gray-400 dark:border-gray-900 dark:text-white">
|
||||||
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
|
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
|
||||||
|
@ -281,10 +275,10 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</DialogFooter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DialogContent>
|
||||||
</div>
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
)) ||
|
)) ||
|
||||||
|
|
|
@ -6,7 +6,9 @@ import { getSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
||||||
|
@ -16,6 +18,7 @@ import { HeadSeo } from "@components/seo/head-seo";
|
||||||
|
|
||||||
export default function MeetingUnavailable(props: inferSSRProps<typeof getServerSideProps>) {
|
export default function MeetingUnavailable(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { t } = useLocale();
|
||||||
// if no booking redirectis to the 404 page
|
// if no booking redirectis to the 404 page
|
||||||
const emptyBooking = props.booking === null;
|
const emptyBooking = props.booking === null;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -28,49 +31,38 @@ export default function MeetingUnavailable(props: inferSSRProps<typeof getServer
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo title={`Meeting Unavaialble`} description={`Meeting Unavailable`} />
|
<HeadSeo title={`Meeting Unavaialble`} description={`Meeting Unavailable`} />
|
||||||
<main className="mx-auto my-24 max-w-3xl">
|
<main className="mx-auto my-24 max-w-3xl">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div className="mx-auto mb-5 flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
<div
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
aria-labelledby="modal-headline">
|
|
||||||
<div>
|
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
|
||||||
<XIcon className="h-6 w-6 text-red-600" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
|
||||||
This meeting is in the past.
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 border-t border-b py-4">
|
|
||||||
<h2 className="font-cal mb-2 text-center text-lg font-medium text-gray-600">
|
|
||||||
{props.booking.title}
|
|
||||||
</h2>
|
|
||||||
<p className="text-center text-gray-500">
|
|
||||||
<CalendarIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
|
|
||||||
{dayjs(props.booking.startTime).format(
|
|
||||||
detectBrowserTimeFormat + ", dddd DD MMMM YYYY"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 text-center sm:mt-6">
|
|
||||||
<div className="mt-5">
|
|
||||||
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
|
||||||
Go back home
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-5 flex justify-center">
|
||||||
</div>
|
<DialogHeader title={props.booking.title} />
|
||||||
|
</div>
|
||||||
|
<h3
|
||||||
|
className="flex justify-center text-center text-lg font-medium leading-6 text-gray-900"
|
||||||
|
id="modal-headline">
|
||||||
|
This meeting is in the past.
|
||||||
|
</h3>
|
||||||
|
<p className="-mt-4 flex justify-center text-sm text-gray-500">
|
||||||
|
<CalendarIcon className="mr-1 inline-block h-4 w-4" />
|
||||||
|
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
|
||||||
|
</p>
|
||||||
|
<p className="flex justify-center text-center text-sm text-gray-500">
|
||||||
|
This meeting will be accessible 60 minutes in advance.
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
||||||
|
{t("go_back_home")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,9 @@ import { getSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
||||||
|
@ -16,7 +18,7 @@ import { HeadSeo } from "@components/seo/head-seo";
|
||||||
|
|
||||||
export default function MeetingNotStarted(props: inferSSRProps<typeof getServerSideProps>) {
|
export default function MeetingNotStarted(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { t } = useLocale();
|
||||||
//if no booking redirectis to the 404 page
|
//if no booking redirectis to the 404 page
|
||||||
const emptyBooking = props.booking === null;
|
const emptyBooking = props.booking === null;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -29,54 +31,33 @@ export default function MeetingNotStarted(props: inferSSRProps<typeof getServerS
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo title={`Meeting Unavaialble`} description={`Meeting Unavailable`} />
|
<HeadSeo title={`Meeting Unavaialble`} description={`Meeting Unavailable`} />
|
||||||
<main className="mx-auto my-24 max-w-3xl">
|
<main className="mx-auto my-24 max-w-3xl">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div className="mx-auto mb-5 flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
<div
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
aria-labelledby="modal-headline">
|
|
||||||
<div>
|
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
|
||||||
<XIcon className="h-6 w-6 text-red-600" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
|
||||||
This meeting has not started yet
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 border-t border-b py-4">
|
|
||||||
<h2 className="font-cal mb-2 text-center text-lg font-medium text-gray-600">
|
|
||||||
{props.booking.title}
|
|
||||||
</h2>
|
|
||||||
<p className="text-center text-gray-500">
|
|
||||||
<CalendarIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
|
|
||||||
{dayjs(props.booking.startTime).format(
|
|
||||||
detectBrowserTimeFormat + ", dddd DD MMMM YYYY"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
This meeting will be accessible 60 minutes in advance.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 text-center sm:mt-6">
|
|
||||||
<div className="mt-5">
|
|
||||||
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
|
||||||
Go back home
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-5 flex justify-center">
|
||||||
</div>
|
<DialogHeader title={props.booking.title} />
|
||||||
|
</div>
|
||||||
|
<p className="-mt-4 flex items-center justify-center text-sm text-gray-500">
|
||||||
|
<CalendarIcon className="mr-1 inline-block h-4 w-4" />
|
||||||
|
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
|
||||||
|
</p>
|
||||||
|
<p className="flex justify-center text-center text-sm text-gray-500">
|
||||||
|
This meeting will be accessible 60 minutes in advance.
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
||||||
|
{t("go_back_home")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { XIcon } from "@heroicons/react/outline";
|
import { XIcon } from "@heroicons/react/outline";
|
||||||
import { ArrowRightIcon } from "@heroicons/react/solid";
|
import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
import { HeadSeo } from "@components/seo/head-seo";
|
||||||
|
|
||||||
|
@ -14,41 +14,27 @@ export default function NoMeetingFound() {
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo title={t("no_meeting_found")} description={t("no_meeting_found")} />
|
<HeadSeo title={t("no_meeting_found")} description={t("no_meeting_found")} />
|
||||||
<main className="mx-auto my-24 max-w-3xl">
|
<main className="mx-auto my-24 max-w-3xl">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<Dialog defaultOpen={true}>
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<DialogContent
|
||||||
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
onInteractOutside={(e) => {
|
||||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
|
e.preventDefault();
|
||||||
​
|
}}>
|
||||||
</span>
|
<div className="mx-auto mb-5 flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
<div
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
className="inline-block transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
aria-labelledby="modal-headline">
|
|
||||||
<div>
|
|
||||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
|
||||||
<XIcon className="h-6 w-6 text-red-600" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
|
||||||
{t("no_meeting_found")}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">
|
|
||||||
<p className="text-center text-sm text-gray-500">{t("no_meeting_found_description")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 text-center sm:mt-6">
|
|
||||||
<div className="mt-5">
|
|
||||||
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
|
||||||
{t("go_back_home")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-5 flex justify-center">
|
||||||
</div>
|
<DialogHeader title={t("no_meeting_found")} />
|
||||||
|
</div>
|
||||||
|
<p className="-mt-4 text-center text-sm text-gray-500">{t("no_meeting_found_description")}</p>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<DialogFooter>
|
||||||
|
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
||||||
|
{t("go_back_home")}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -63,7 +63,7 @@ export const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps
|
||||||
<DialogPrimitive.Overlay className="fixed inset-0 z-40 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<DialogPrimitive.Overlay className="fixed inset-0 z-40 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
{...props}
|
{...props}
|
||||||
className="fixed left-1/2 top-1/2 z-[9999999999] min-w-[360px] -translate-x-1/2 -translate-y-1/2 rounded bg-white p-6 text-left shadow-xl sm:w-full sm:max-w-lg sm:align-middle"
|
className="fixed left-1/2 top-1/2 z-[9999999999] min-w-[360px] -translate-x-1/2 -translate-y-1/2 rounded bg-white p-6 text-left shadow-xl focus-visible:outline-none sm:w-full sm:max-w-[35rem] sm:align-middle"
|
||||||
ref={forwardedRef}>
|
ref={forwardedRef}>
|
||||||
{children}
|
{children}
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
|
|
Loading…
Reference in a new issue