tailwind prettier (#1646)

* tailwind prettier

* Minor fixes

* Sorts components and pages

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
Peer Richelsen 2022-02-09 00:05:13 +00:00 committed by GitHub
parent 1e9234ea67
commit 51d553559f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 977 additions and 945 deletions

View file

@ -9,4 +9,5 @@ module.exports = {
arrowParens: "always", arrowParens: "always",
importOrder: ["^@ee/(.*)$", "^@lib/(.*)$", "^@components/(.*)$", "^@(server|trpc)/(.*)$", "^[./]"], importOrder: ["^@ee/(.*)$", "^@lib/(.*)$", "^@components/(.*)$", "^@(server|trpc)/(.*)$", "^[./]"],
importOrderSeparation: true, importOrderSeparation: true,
plugins: [require("prettier-plugin-tailwindcss")],
}; };

View file

@ -5,7 +5,6 @@
"esbenp.prettier-vscode", // prettier plugin "esbenp.prettier-vscode", // prettier plugin
"dbaeumer.vscode-eslint", // eslint plugin "dbaeumer.vscode-eslint", // eslint plugin
"bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind "bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind
"heybourn.headwind", // automatically sort tailwind classes in predictable order, kinda like "prettier for tailwind",
"ban.spellright", // Spell check for docs "ban.spellright", // Spell check for docs
"stripe.vscode-stripe" // stripe VSCode extension "stripe.vscode-stripe" // stripe VSCode extension
] ]

View file

@ -0,0 +1,15 @@
declare module "@wojtekmaj/react-daterange-picker/dist/entry.nostyle" {
import { CalendarProps } from "react-calendar";
export type DateRangePickerCalendarProps = Omit<
CalendarProps,
"calendarClassName" | "onChange" | "value"
> & {
calendarClassName?: string;
onChange: (value: [Date, Date]) => void;
value: [Date, Date];
clearIcon: JSX.Element | null;
calendarIcon: JSX.Element | null;
rangeDivider: JSX.Element | null;
};
export default function DateRangePicker(props: DateRangePickerCalendarProps): JSX.Element;
}

View file

@ -14,13 +14,13 @@ export default function AddToHomescreen() {
} }
return !closeBanner ? ( return !closeBanner ? (
<div className="fixed inset-x-0 bottom-0 pb-2 sm:hidden sm:pb-5"> <div className="fixed inset-x-0 bottom-0 pb-2 sm:hidden sm:pb-5">
<div className="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8"> <div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div className="p-2 rounded-lg shadow-lg sm:p-3" style={{ background: "#2F333D" }}> <div className="rounded-lg p-2 shadow-lg sm:p-3" style={{ background: "#2F333D" }}>
<div className="flex flex-wrap items-center justify-between"> <div className="flex flex-wrap items-center justify-between">
<div className="flex items-center flex-1 w-0"> <div className="flex w-0 flex-1 items-center">
<span className="flex p-2 rounded-lg bg-opacity-30 bg-brand text-brandcontrast"> <span className="flex rounded-lg bg-brand bg-opacity-30 p-2 text-brandcontrast">
<svg <svg
className="text-indigo-500 fill-current h-7 w-7" className="h-7 w-7 fill-current text-indigo-500"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 50 50" viewBox="0 0 50 50"
enableBackground="new 0 0 50 50"> enableBackground="new 0 0 50 50">
@ -34,13 +34,13 @@ export default function AddToHomescreen() {
</p> </p>
</div> </div>
<div className="flex-shrink-0 order-2 sm:order-3"> <div className="order-2 flex-shrink-0 sm:order-3">
<button <button
onClick={() => setCloseBanner(true)} onClick={() => setCloseBanner(true)}
type="button" type="button"
className="flex p-2 -mr-1 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white"> className="-mr-1 flex rounded-md p-2 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">{t("dismiss")}</span> <span className="sr-only">{t("dismiss")}</span>
<XIcon className="w-6 h-6 text-white" aria-hidden="true" /> <XIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button> </button>
</div> </div>
</div> </div>

View file

@ -56,8 +56,8 @@ const DestinationCalendarSelector = ({
<div className="relative"> <div className="relative">
{/* There's no easy way to customize the displayed value for a Select, so we fake it. */} {/* There's no easy way to customize the displayed value for a Select, so we fake it. */}
{!hidePlaceholder && ( {!hidePlaceholder && (
<div className="absolute z-10 pointer-events-none"> <div className="pointer-events-none absolute z-10">
<Button size="sm" color="secondary" className="border-transparent m-[1px] rounded-sm"> <Button size="sm" color="secondary" className="m-[1px] rounded-sm border-transparent">
{t("select_destination_calendar")}: {selectedOption?.label || ""} {t("select_destination_calendar")}: {selectedOption?.label || ""}
</Button> </Button>
</div> </div>
@ -67,7 +67,7 @@ const DestinationCalendarSelector = ({
placeholder={!hidePlaceholder ? `${t("select_destination_calendar")}:` : undefined} placeholder={!hidePlaceholder ? `${t("select_destination_calendar")}:` : undefined}
options={options} options={options}
isSearchable={false} isSearchable={false}
className="flex-1 block w-full min-w-0 mt-1 mb-2 border-gray-300 rounded-none focus:ring-primary-500 focus:border-primary-500 rounded-r-md sm:text-sm" className="mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-md border-gray-300 focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
onChange={(option) => { onChange={(option) => {
setSelectedOption(option); setSelectedOption(option);
if (!option) { if (!option) {

View file

@ -6,7 +6,7 @@ export function Dialog(props: DialogProps) {
const { children, ...other } = props; const { children, ...other } = props;
return ( return (
<DialogPrimitive.Root {...other}> <DialogPrimitive.Root {...other}>
<DialogPrimitive.Overlay className="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75" /> <DialogPrimitive.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
{children} {children}
</DialogPrimitive.Root> </DialogPrimitive.Root>
); );
@ -17,7 +17,7 @@ export const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps
({ children, ...props }, forwardedRef) => ( ({ children, ...props }, forwardedRef) => (
<DialogPrimitive.Content <DialogPrimitive.Content
{...props} {...props}
className="min-w-[360px] fixed left-1/2 top-1/2 p-6 text-left bg-white rounded shadow-xl -translate-x-1/2 -translate-y-1/2 sm:align-middle sm:w-full sm:max-w-lg" className="fixed left-1/2 top-1/2 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"
ref={forwardedRef}> ref={forwardedRef}>
{children} {children}
</DialogPrimitive.Content> </DialogPrimitive.Content>
@ -32,7 +32,7 @@ type DialogHeaderProps = {
export function DialogHeader(props: DialogHeaderProps) { export function DialogHeader(props: DialogHeaderProps) {
return ( return (
<div className="mb-8"> <div className="mb-8">
<h3 className="text-xl text-gray-900 leading-16 font-cal" id="modal-title"> <h3 className="leading-16 font-cal text-xl text-gray-900" id="modal-title">
{props.title} {props.title}
</h3> </h3>
{props.subtitle && <div className="text-sm text-gray-400">{props.subtitle}</div>} {props.subtitle && <div className="text-sm text-gray-400">{props.subtitle}</div>}
@ -43,7 +43,7 @@ export function DialogHeader(props: DialogHeaderProps) {
export function DialogFooter(props: { children: ReactNode }) { export function DialogFooter(props: { children: ReactNode }) {
return ( return (
<div> <div>
<div className="flex justify-end mt-5 rtl:space-x-reverse space-x-2">{props.children}</div> <div className="mt-5 flex justify-end space-x-2 rtl:space-x-reverse">{props.children}</div>
</div> </div>
); );
} }

View file

@ -13,12 +13,12 @@ export default function EmptyScreen({
}) { }) {
return ( return (
<> <>
<div className="min-h-80 border border-dashed rounded-sm flex justify-center items-center flex-col my-6"> <div className="my-6 flex min-h-80 flex-col items-center justify-center rounded-sm border border-dashed">
<div className="bg-white w-[72px] h-[72px] flex justify-center items-center rounded-full"> <div className="flex h-[72px] w-[72px] items-center justify-center rounded-full bg-white">
<Icon className="inline-block w-10 h-10 bg-white" /> <Icon className="inline-block h-10 w-10 bg-white" />
</div> </div>
<div className="max-w-[420px] text-center"> <div className="max-w-[420px] text-center">
<h2 className="text-lg font-medium mt-6 mb-1">{headline}</h2> <h2 className="mt-6 mb-1 text-lg font-medium">{headline}</h2>
<p className="text-sm leading-6 text-gray-600">{description}</p> <p className="text-sm leading-6 text-gray-600">{description}</p>
</div> </div>
</div> </div>

View file

@ -38,8 +38,8 @@ function CropContainer({
}; };
return ( return (
<div className="w-40 h-40 rounded-full crop-container max-h-40"> <div className="crop-container h-40 max-h-40 w-40 rounded-full">
<div className="relative w-40 h-40 rounded-full"> <div className="relative h-40 w-40 rounded-full">
<Cropper <Cropper
image={imageSrc} image={imageSrc}
crop={crop} crop={crop}
@ -119,38 +119,38 @@ export default function ImageUploader({
<DialogContent> <DialogContent>
<div className="mb-4 sm:flex sm:items-start"> <div className="mb-4 sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="text-lg font-bold leading-6 text-gray-900 font-cal" id="modal-title"> <h3 className="font-cal text-lg font-bold leading-6 text-gray-900" id="modal-title">
{t("upload_target", { target })} {t("upload_target", { target })}
</h3> </h3>
</div> </div>
</div> </div>
<div className="mb-4"> <div className="mb-4">
<div className="flex flex-col items-center justify-center p-8 mt-6 cropper"> <div className="cropper mt-6 flex flex-col items-center justify-center p-8">
{!result && ( {!result && (
<div className="flex items-center justify-start w-20 h-20 bg-gray-50 rounded-full max-h-20"> <div className="flex h-20 max-h-20 w-20 items-center justify-start rounded-full bg-gray-50">
{!imageSrc && ( {!imageSrc && (
<p className="w-full text-sm text-center text-white sm:text-xs"> <p className="w-full text-center text-sm text-white sm:text-xs">
{t("no_target", { target })} {t("no_target", { target })}
</p> </p>
)} )}
{imageSrc && <img className="w-20 h-20 rounded-full" src={imageSrc} alt={target} />} {imageSrc && <img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />}
</div> </div>
)} )}
{result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />} {result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />}
<label className="px-3 py-1 mt-8 text-xs font-medium leading-4 text-gray-700 bg-white border border-gray-300 rounded-sm hover:bg-gray-50 hover:text-gray-900 hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-neutral-900 dark:bg-transparent dark:text-white dark:border-gray-800 dark:hover:bg-gray-900"> <label className="mt-8 rounded-sm border border-gray-300 bg-white px-3 py-1 text-xs font-medium leading-4 text-gray-700 hover:bg-gray-50 hover:text-gray-900 hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent dark:text-white dark:hover:bg-gray-900">
<input <input
onInput={onInputFile} onInput={onInputFile}
type="file" type="file"
name={id} name={id}
placeholder={t("upload_image")} placeholder={t("upload_image")}
className="absolute mt-4 opacity-0 pointer-events-none" className="pointer-events-none absolute mt-4 opacity-0"
accept="image/*" accept="image/*"
/> />
{t("choose_a_file")} {t("choose_a_file")}
</label> </label>
</div> </div>
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse gap-x-2"> <div className="mt-5 gap-x-2 sm:mt-4 sm:flex sm:flex-row-reverse">
<DialogClose asChild> <DialogClose asChild>
<Button onClick={() => showCroppedImage(croppedAreaPixels)}>{t("save")}</Button> <Button onClick={() => showCroppedImage(croppedAreaPixels)}>{t("save")}</Button>
</DialogClose> </DialogClose>

View file

@ -5,7 +5,7 @@ import classNames from "@lib/classNames";
export function List(props: JSX.IntrinsicElements["ul"]) { export function List(props: JSX.IntrinsicElements["ul"]) {
return ( return (
<ul {...props} className={classNames("sm:overflow-hidden rounded-sm sm:mx-0 -mx-4", props.className)}> <ul {...props} className={classNames("-mx-4 rounded-sm sm:mx-0 sm:overflow-hidden", props.className)}>
{props.children} {props.children}
</ul> </ul>
); );

View file

@ -3,7 +3,7 @@ export default function Logo({ small, icon }: { small?: boolean; icon?: boolean
<h1 className="inline"> <h1 className="inline">
<strong> <strong>
{icon ? ( {icon ? (
<img className="w-9 mx-auto" alt="Cal" title="Cal" src="/cal-com-icon-white.svg" /> <img className="mx-auto w-9" alt="Cal" title="Cal" src="/cal-com-icon-white.svg" />
) : ( ) : (
<img <img
className={small ? "h-4 w-auto" : "h-5 w-auto"} className={small ? "h-4 w-auto" : "h-5 w-auto"}

View file

@ -18,7 +18,7 @@ const NavTabs: FC<Props> = ({ tabs, linkProps }) => {
return ( return (
<> <>
<nav <nav
className="flex -mb-px space-x-2 space-x-5 rtl:space-x-reverse sm:rtl:space-x-reverse" className="-mb-px flex space-x-2 space-x-5 rtl:space-x-reverse sm:rtl:space-x-reverse"
aria-label="Tabs"> aria-label="Tabs">
{tabs.map((tab) => { {tabs.map((tab) => {
const isCurrent = router.asPath === tab.href; const isCurrent = router.asPath === tab.href;
@ -28,15 +28,15 @@ const NavTabs: FC<Props> = ({ tabs, linkProps }) => {
className={classNames( className={classNames(
isCurrent isCurrent
? "border-neutral-900 text-neutral-900" ? "border-neutral-900 text-neutral-900"
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300", : "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
"group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" "group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium"
)} )}
aria-current={isCurrent ? "page" : undefined}> aria-current={isCurrent ? "page" : undefined}>
{tab.icon && ( {tab.icon && (
<tab.icon <tab.icon
className={classNames( className={classNames(
isCurrent ? "text-neutral-900" : "text-gray-400 group-hover:text-gray-500", isCurrent ? "text-neutral-900" : "text-gray-400 group-hover:text-gray-500",
"-ml-0.5 ltr:mr-2 rtl:ml-2 h-5 w-5 hidden sm:inline-block" "-ml-0.5 hidden h-5 w-5 ltr:mr-2 rtl:ml-2 sm:inline-block"
)} )}
aria-hidden="true" aria-hidden="true"
/> />

View file

@ -104,12 +104,12 @@ export function ShellSubHeading(props: {
className?: string; className?: string;
}) { }) {
return ( return (
<div className={classNames("block sm:flex justify-between mb-3", props.className)}> <div className={classNames("mb-3 block justify-between sm:flex", props.className)}>
<div> <div>
<h2 className="flex items-center content-center space-x-2 text-base font-bold leading-6 text-gray-900 rtl:space-x-reverse"> <h2 className="flex content-center items-center space-x-2 text-base font-bold leading-6 text-gray-900 rtl:space-x-reverse">
{props.title} {props.title}
</h2> </h2>
{props.subtitle && <p className="text-sm ltr:mr-4 text-neutral-500">{props.subtitle}</p>} {props.subtitle && <p className="text-sm text-neutral-500 ltr:mr-4">{props.subtitle}</p>}
</div> </div>
{props.actions && <div className="flex-shrink-0">{props.actions}</div>} {props.actions && <div className="flex-shrink-0">{props.actions}</div>}
</div> </div>
@ -184,7 +184,7 @@ export default function Shell(props: {
if (i18n.status === "loading" || isRedirectingToOnboarding || loading) { if (i18n.status === "loading" || isRedirectingToOnboarding || loading) {
// show spinner whilst i18n is loading to avoid language flicker // show spinner whilst i18n is loading to avoid language flicker
return ( return (
<div className="absolute z-50 flex items-center w-full h-screen bg-gray-50"> <div className="absolute z-50 flex h-screen w-full items-center bg-gray-50">
<Loader /> <Loader />
</div> </div>
); );
@ -206,9 +206,9 @@ export default function Shell(props: {
<div className="flex h-screen overflow-hidden bg-gray-100" data-testid="dashboard-shell"> <div className="flex h-screen overflow-hidden bg-gray-100" data-testid="dashboard-shell">
<div className="hidden md:flex lg:flex-shrink-0"> <div className="hidden md:flex lg:flex-shrink-0">
<div className="flex flex-col w-14 lg:w-56"> <div className="flex w-14 flex-col lg:w-56">
<div className="flex flex-col flex-1 h-0 bg-white border-r border-gray-200"> <div className="flex h-0 flex-1 flex-col border-r border-gray-200 bg-white">
<div className="flex flex-col flex-1 pt-3 pb-4 overflow-y-auto lg:pt-5"> <div className="flex flex-1 flex-col overflow-y-auto pt-3 pb-4 lg:pt-5">
<Link href="/event-types"> <Link href="/event-types">
<a className="px-4 md:hidden lg:inline"> <a className="px-4 md:hidden lg:inline">
<Logo small /> <Logo small />
@ -220,7 +220,7 @@ export default function Shell(props: {
<Logo small icon /> <Logo small icon />
</a> </a>
</Link> </Link>
<nav className="flex-1 px-2 mt-2 space-y-1 bg-white lg:mt-5"> <nav className="mt-2 flex-1 space-y-1 bg-white px-2 lg:mt-5">
{navigation.map((item) => ( {navigation.map((item) => (
<Link key={item.name} href={item.href}> <Link key={item.name} href={item.href}>
<a <a
@ -228,14 +228,14 @@ export default function Shell(props: {
item.current item.current
? "bg-neutral-100 text-neutral-900" ? "bg-neutral-100 text-neutral-900"
: "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900", : "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
"group flex items-center px-2 py-2 text-sm font-medium rounded-sm" "group flex items-center rounded-sm px-2 py-2 text-sm font-medium"
)}> )}>
<item.icon <item.icon
className={classNames( className={classNames(
item.current item.current
? "text-neutral-500" ? "text-neutral-500"
: "text-neutral-400 group-hover:text-neutral-500", : "text-neutral-400 group-hover:text-neutral-500",
"ltr:mr-3 rtl:ml-3 flex-shrink-0 h-5 w-5" "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
)} )}
aria-hidden="true" aria-hidden="true"
/> />
@ -246,7 +246,7 @@ export default function Shell(props: {
</nav> </nav>
</div> </div>
<TrialBanner /> <TrialBanner />
<div className="p-2 pt-2 pr-2 m-2 rounded-sm hover:bg-gray-100"> <div className="m-2 rounded-sm p-2 pt-2 pr-2 hover:bg-gray-100">
<span className="hidden lg:inline"> <span className="hidden lg:inline">
<UserDropdown /> <UserDropdown />
</span> </span>
@ -258,25 +258,25 @@ export default function Shell(props: {
</div> </div>
</div> </div>
<div className="flex flex-col flex-1 w-0 overflow-hidden"> <div className="flex w-0 flex-1 flex-col overflow-hidden">
<main <main
className={classNames( className={classNames(
"flex-1 relative z-0 overflow-y-auto focus:outline-none max-w-[1700px]", "relative z-0 max-w-[1700px] flex-1 overflow-y-auto focus:outline-none",
props.flexChildrenContainer && "flex flex-col" props.flexChildrenContainer && "flex flex-col"
)}> )}>
{/* show top navigation for md and smaller (tablet and phones) */} {/* show top navigation for md and smaller (tablet and phones) */}
<nav className="flex items-center justify-between p-4 bg-white border-b border-gray-200 md:hidden"> <nav className="flex items-center justify-between border-b border-gray-200 bg-white p-4 md:hidden">
<Link href="/event-types"> <Link href="/event-types">
<a> <a>
<Logo /> <Logo />
</a> </a>
</Link> </Link>
<div className="flex items-center self-center gap-3"> <div className="flex items-center gap-3 self-center">
<button className="p-2 text-gray-400 bg-white rounded-full hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"> <button className="rounded-full bg-white p-2 text-gray-400 hover:bg-gray-50 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
<span className="sr-only">{t("view_notifications")}</span> <span className="sr-only">{t("view_notifications")}</span>
<Link href="/settings/profile"> <Link href="/settings/profile">
<a> <a>
<CogIcon className="w-6 h-6" aria-hidden="true" /> <CogIcon className="h-6 w-6" aria-hidden="true" />
</a> </a>
</Link> </Link>
</button> </button>
@ -285,8 +285,8 @@ export default function Shell(props: {
</nav> </nav>
<div <div
className={classNames( className={classNames(
props.centered && "md:max-w-5xl mx-auto", props.centered && "mx-auto md:max-w-5xl",
props.flexChildrenContainer && "flex flex-col flex-1", props.flexChildrenContainer && "flex flex-1 flex-col",
"py-8" "py-8"
)}> )}>
{!!props.backPath && ( {!!props.backPath && (
@ -299,25 +299,25 @@ export default function Shell(props: {
</Button> </Button>
</div> </div>
)} )}
<div className="block sm:flex justify-between px-4 sm:px-6 md:px-8 min-h-[80px]"> <div className="block min-h-[80px] justify-between px-4 sm:flex sm:px-6 md:px-8">
{props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>} {props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>}
<div className="w-full mb-8"> <div className="mb-8 w-full">
<h1 className="mb-1 text-xl font-bold tracking-wide text-gray-900 font-cal"> <h1 className="mb-1 font-cal text-xl font-bold tracking-wide text-gray-900">
{props.heading} {props.heading}
</h1> </h1>
<p className="text-sm ltr:mr-4 rtl:ml-4 text-neutral-500">{props.subtitle}</p> <p className="text-sm text-neutral-500 ltr:mr-4 rtl:ml-4">{props.subtitle}</p>
</div> </div>
<div className="flex-shrink-0 mb-4">{props.CTA}</div> <div className="mb-4 flex-shrink-0">{props.CTA}</div>
</div> </div>
<div <div
className={classNames( className={classNames(
"px-4 sm:px-6 md:px-8", "px-4 sm:px-6 md:px-8",
props.flexChildrenContainer && "flex flex-col flex-1" props.flexChildrenContainer && "flex flex-1 flex-col"
)}> )}>
{props.children} {props.children}
</div> </div>
{/* show bottom navigation for md and smaller (tablet and phones) */} {/* show bottom navigation for md and smaller (tablet and phones) */}
<nav className="fixed bottom-0 z-40 flex w-full bg-white shadow bottom-nav md:hidden"> <nav className="bottom-nav fixed bottom-0 z-40 flex w-full bg-white shadow md:hidden">
{/* note(PeerRich): using flatMap instead of map to remove settings from bottom nav */} {/* note(PeerRich): using flatMap instead of map to remove settings from bottom nav */}
{navigation.flatMap((item, itemIdx) => {navigation.flatMap((item, itemIdx) =>
item.href === "/settings/profile" ? ( item.href === "/settings/profile" ? (
@ -329,13 +329,13 @@ export default function Shell(props: {
item.current ? "text-gray-900" : "text-neutral-400 hover:text-gray-700", item.current ? "text-gray-900" : "text-neutral-400 hover:text-gray-700",
itemIdx === 0 ? "rounded-l-lg" : "", itemIdx === 0 ? "rounded-l-lg" : "",
itemIdx === navigation.length - 1 ? "rounded-r-lg" : "", itemIdx === navigation.length - 1 ? "rounded-r-lg" : "",
"group relative min-w-0 flex-1 overflow-hidden bg-white py-2 px-2 text-xs sm:text-sm font-medium text-center hover:bg-gray-50 focus:z-10" "group relative min-w-0 flex-1 overflow-hidden bg-white py-2 px-2 text-center text-xs font-medium hover:bg-gray-50 focus:z-10 sm:text-sm"
)} )}
aria-current={item.current ? "page" : undefined}> aria-current={item.current ? "page" : undefined}>
<item.icon <item.icon
className={classNames( className={classNames(
item.current ? "text-gray-900" : "text-gray-400 group-hover:text-gray-500", item.current ? "text-gray-900" : "text-gray-400 group-hover:text-gray-500",
"block mx-auto flex-shrink-0 h-5 w-5 mb-1 text-center" "mx-auto mb-1 block h-5 w-5 flex-shrink-0 text-center"
)} )}
aria-hidden="true" aria-hidden="true"
/> />
@ -370,11 +370,11 @@ function UserDropdown({ small }: { small?: boolean }) {
return ( return (
<Dropdown> <Dropdown>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<div className="flex items-center w-full appearance-none cursor-pointer group"> <div className="group flex w-full cursor-pointer appearance-none items-center">
<span <span
className={classNames( className={classNames(
small ? "w-8 h-8" : "w-10 h-10", small ? "h-8 w-8" : "h-10 w-10",
"bg-gray-300 rounded-full flex-shrink-0 relative ltr:mr-3 rtl:ml-3" "relative flex-shrink-0 rounded-full bg-gray-300 ltr:mr-3 rtl:ml-3"
)}> )}>
<img <img
className="rounded-full" className="rounded-full"
@ -387,24 +387,24 @@ function UserDropdown({ small }: { small?: boolean }) {
alt={user?.username || "Nameless User"} alt={user?.username || "Nameless User"}
/> />
{!user?.away && ( {!user?.away && (
<div className="absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white rounded-full"></div> <div className="absolute bottom-0 right-0 h-3 w-3 rounded-full border-2 border-white bg-green-500"></div>
)} )}
{user?.away && ( {user?.away && (
<div className="absolute bottom-0 right-0 w-3 h-3 bg-yellow-500 border-2 border-white rounded-full"></div> <div className="absolute bottom-0 right-0 h-3 w-3 rounded-full border-2 border-white bg-yellow-500"></div>
)} )}
</span> </span>
{!small && ( {!small && (
<span className="flex items-center flex-grow truncate"> <span className="flex flex-grow items-center truncate">
<span className="flex-grow text-sm truncate"> <span className="flex-grow truncate text-sm">
<span className="block font-medium text-gray-900 truncate"> <span className="block truncate font-medium text-gray-900">
{user?.username || "Nameless User"} {user?.username || "Nameless User"}
</span> </span>
<span className="block font-normal truncate text-neutral-500"> <span className="block truncate font-normal text-neutral-500">
{user?.username ? `cal.com/${user.username}` : "No public page"} {user?.username ? `cal.com/${user.username}` : "No public page"}
</span> </span>
</span> </span>
<SelectorIcon <SelectorIcon
className="flex-shrink-0 w-5 h-5 text-gray-400 group-hover:text-gray-500" className="h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500"
aria-hidden="true" aria-hidden="true"
/> />
</span> </span>
@ -418,13 +418,13 @@ function UserDropdown({ small }: { small?: boolean }) {
mutation.mutate({ away: !user?.away }); mutation.mutate({ away: !user?.away });
utils.invalidateQueries("viewer.me"); utils.invalidateQueries("viewer.me");
}} }}
className="flex px-4 py-2 text-sm cursor-pointer hover:bg-gray-100 hover:text-gray-900"> className="flex cursor-pointer px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<MoonIcon <MoonIcon
className={classNames( className={classNames(
user?.away user?.away
? "text-purple-500 group-hover:text-purple-700" ? "text-purple-500 group-hover:text-purple-700"
: "text-gray-500 group-hover:text-gray-700", : "text-gray-500 group-hover:text-gray-700",
"ltr:mr-3 rtl:ml-3 flex-shrink-0 h-5 w-5" "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
)} )}
aria-hidden="true" aria-hidden="true"
/> />
@ -439,7 +439,7 @@ function UserDropdown({ small }: { small?: boolean }) {
rel="noopener noreferrer" rel="noopener noreferrer"
href={`${process.env.NEXT_PUBLIC_APP_URL}/${user.username}`} href={`${process.env.NEXT_PUBLIC_APP_URL}/${user.username}`}
className="flex items-center px-4 py-2 text-sm text-gray-700"> className="flex items-center px-4 py-2 text-sm text-gray-700">
<ExternalLinkIcon className="w-5 h-5 text-gray-500 ltr:mr-3 rtl:ml-3" /> {t("view_public_page")} <ExternalLinkIcon className="h-5 w-5 text-gray-500 ltr:mr-3 rtl:ml-3" /> {t("view_public_page")}
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
)} )}
@ -454,7 +454,7 @@ function UserDropdown({ small }: { small?: boolean }) {
viewBox="0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5"
className={classNames( className={classNames(
"text-gray-500 group-hover:text-gray-700", "text-gray-500 group-hover:text-gray-700",
"mt-0.5 ltr:mr-2 rtl:ml-2 flex-shrink-0 h-4 w-4" "mt-0.5 h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-2"
)} )}
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<g clipRule="evenodd" fillRule="evenodd"> <g clipRule="evenodd" fillRule="evenodd">
@ -481,7 +481,7 @@ function UserDropdown({ small }: { small?: boolean }) {
rel="noopener noreferrer" rel="noopener noreferrer"
href="https://cal.com/roadmap" href="https://cal.com/roadmap"
className="flex items-center px-4 py-2 text-sm text-gray-700"> className="flex items-center px-4 py-2 text-sm text-gray-700">
<MapIcon className="w-5 h-5 text-gray-500 ltr:mr-3 rtl:ml-3" /> {t("visit_roadmap")} <MapIcon className="h-5 w-5 text-gray-500 ltr:mr-3 rtl:ml-3" /> {t("visit_roadmap")}
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
<HelpMenuItemDynamic /> <HelpMenuItemDynamic />
@ -489,11 +489,11 @@ function UserDropdown({ small }: { small?: boolean }) {
<DropdownMenuItem> <DropdownMenuItem>
<a <a
onClick={() => signOut({ callbackUrl: "/auth/logout" })} onClick={() => signOut({ callbackUrl: "/auth/logout" })}
className="flex px-4 py-2 text-sm cursor-pointer hover:bg-gray-100 hover:text-gray-900"> className="flex cursor-pointer px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<LogoutIcon <LogoutIcon
className={classNames( className={classNames(
"text-gray-500 group-hover:text-gray-700", "text-gray-500 group-hover:text-gray-700",
"ltr:mr-3 rtl:ml-3 flex-shrink-0 h-5 w-5" "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
)} )}
aria-hidden="true" aria-hidden="true"
/> />

View file

@ -12,7 +12,7 @@ const Slider = ({
changeHandler: (value: number) => void; changeHandler: (value: number) => void;
}) => ( }) => (
<SliderPrimitive.Root <SliderPrimitive.Root
className="mt-2 slider" className="slider mt-2"
value={[value]} value={[value]}
aria-label={label} aria-label={label}
onValueChange={(value: number[]) => changeHandler(value[0] ?? value)} onValueChange={(value: number[]) => changeHandler(value[0] ?? value)}

View file

@ -9,15 +9,15 @@ export type SwatchProps = {
const Swatch = (props: SwatchProps) => { const Swatch = (props: SwatchProps) => {
const { size, backgroundColor, onClick } = props; const { size, backgroundColor, onClick } = props;
return ( return (
<div className="p-1 border-2 border-gray-200 shadow-sm"> <div className="border-2 border-gray-200 p-1 shadow-sm">
<div <div
onClick={onClick} onClick={onClick}
style={{ backgroundColor }} style={{ backgroundColor }}
className={classNames( className={classNames(
"cursor-pointer", "cursor-pointer",
size === "sm" && "w-6 h-6 rounded-sm", size === "sm" && "h-6 w-6 rounded-sm",
size === "base" && "w-16 h-16 rounded-sm", size === "base" && "h-16 w-16 rounded-sm",
size === "lg" && "w-24 h-24 rounded-sm" size === "lg" && "h-24 w-24 rounded-sm"
)}></div> )}></div>
</div> </div>
); );

View file

@ -23,7 +23,7 @@ export function Tooltip({
onOpenChange={onOpenChange}> onOpenChange={onOpenChange}>
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger> <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content <TooltipPrimitive.Content
className="bg-black text-xs -mt-2 text-white px-1 py-0.5 shadow-lg rounded-sm" className="-mt-2 rounded-sm bg-black px-1 py-0.5 text-xs text-white shadow-lg"
side="top" side="top"
align="center" align="center"
{...props}> {...props}>

View file

@ -35,7 +35,7 @@ export default function SAMLLogin(props: Props) {
<Button <Button
color="secondary" color="secondary"
data-testid={"saml"} data-testid={"saml"}
className="flex justify-center w-full" className="flex w-full justify-center"
onClick={async (event) => { onClick={async (event) => {
event.preventDefault(); event.preventDefault();

View file

@ -26,7 +26,7 @@ export default function TwoFactor() {
const className = "h-12 w-12 !text-xl text-center"; const className = "h-12 w-12 !text-xl text-center";
return ( return (
<div className="max-w-sm mx-auto !mt-0"> <div className="mx-auto !mt-0 max-w-sm">
<p className="mb-4 text-sm text-gray-500">{t("2fa_enabled_instructions")}</p> <p className="mb-4 text-sm text-gray-500">{t("2fa_enabled_instructions")}</p>
<input hidden type="hidden" value={value} {...methods.register("totpCode")} /> <input hidden type="hidden" value={value} {...methods.register("totpCode")} />
<div className="flex flex-row space-x-1"> <div className="flex flex-row space-x-1">

View file

@ -55,8 +55,8 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
}, []); }, []);
return ( return (
<div className="flex flex-col mt-8 text-center sm:pl-4 sm:mt-0 sm:w-1/3 md:-mb-5"> <div className="mt-8 flex flex-col text-center sm:mt-0 sm:w-1/3 sm:pl-4 md:-mb-5">
<div className="mb-4 text-lg font-light text-left text-gray-600"> <div className="mb-4 text-left text-lg font-light text-gray-600">
<span className="w-1/2 text-gray-600 dark:text-white"> <span className="w-1/2 text-gray-600 dark:text-white">
<strong>{date.toDate().toLocaleString(i18n.language, { weekday: "long" })}</strong> <strong>{date.toDate().toLocaleString(i18n.language, { weekday: "long" })}</strong>
<span className="text-gray-500"> <span className="text-gray-500">
@ -65,7 +65,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
</span> </span>
</span> </span>
</div> </div>
<div className="flex-grow md:h-[364px] overflow-y-auto"> <div className="flex-grow overflow-y-auto md:h-[364px]">
{!loading && {!loading &&
slots?.length > 0 && slots?.length > 0 &&
slots.map((slot) => { slots.map((slot) => {
@ -95,7 +95,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
<Link href={bookingUrl}> <Link href={bookingUrl}>
<a <a
className={classNames( className={classNames(
"block py-4 mb-2 font-medium bg-white border rounded-sm dark:bg-gray-600 text-primary-500 dark:text-neutral-200 dark:border-transparent hover:text-white hover:bg-brand hover:text-brandcontrast dark:hover:border-black dark:hover:bg-brand dark:hover:text-brandcontrast", "mb-2 block rounded-sm border bg-white py-4 font-medium text-primary-500 hover:bg-brand hover:text-white hover:text-brandcontrast dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 dark:hover:border-black dark:hover:bg-brand dark:hover:text-brandcontrast",
brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand" brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand"
)} )}
data-testid="time"> data-testid="time">
@ -106,7 +106,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
); );
})} })}
{!loading && !error && !slots.length && ( {!loading && !error && !slots.length && (
<div className="flex flex-col items-center content-center justify-center w-full h-full -mt-4"> <div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1> <h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1>
</div> </div>
)} )}
@ -114,10 +114,10 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
{loading && <Loader />} {loading && <Loader />}
{error && ( {error && (
<div className="p-4 border-l-4 border-yellow-400 bg-yellow-50"> <div className="border-l-4 border-yellow-400 bg-yellow-50 p-4">
<div className="flex"> <div className="flex">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<ExclamationIcon className="w-5 h-5 text-yellow-400" aria-hidden="true" /> <ExclamationIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
</div> </div>
<div className="ltr:ml-3 rtl:mr-3"> <div className="ltr:ml-3 rtl:mr-3">
<p className="text-sm text-yellow-700">{t("slots_load_fail")}</p> <p className="text-sm text-yellow-700">{t("slots_load_fail")}</p>

View file

@ -74,13 +74,13 @@ function BookingListItem(booking: BookingItem) {
return ( return (
<tr className="flex"> <tr className="flex">
<td className="hidden py-4 align-top ltr:pl-6 rtl:pr-6 sm:table-cell whitespace-nowrap"> <td className="hidden whitespace-nowrap py-4 align-top ltr:pl-6 rtl:pr-6 sm:table-cell">
<div className="text-sm leading-6 text-gray-900">{startTime}</div> <div className="text-sm leading-6 text-gray-900">{startTime}</div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")} {dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")}
</div> </div>
</td> </td>
<td className={"ltr:pl-4 rtl:pr-4 py-4 flex-1" + (booking.rejected ? " line-through" : "")}> <td className={"flex-1 py-4 ltr:pl-4 rtl:pr-4" + (booking.rejected ? " line-through" : "")}>
<div className="sm:hidden"> <div className="sm:hidden">
{!booking.confirmed && !booking.rejected && ( {!booking.confirmed && !booking.rejected && (
<Tag className="mb-2 ltr:mr-2 rtl:ml-2">{t("unconfirmed")}</Tag> <Tag className="mb-2 ltr:mr-2 rtl:ml-2">{t("unconfirmed")}</Tag>
@ -97,7 +97,7 @@ function BookingListItem(booking: BookingItem) {
</div> </div>
<div <div
title={booking.title} title={booking.title}
className="text-sm font-medium leading-6 truncate text-neutral-900 max-w-56 md:max-w-max"> className="max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max">
{booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>} {booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>}
{booking.title} {booking.title}
{!!booking?.eventType?.price && !booking.paid && ( {!!booking?.eventType?.price && !booking.paid && (
@ -108,7 +108,7 @@ function BookingListItem(booking: BookingItem) {
)} )}
</div> </div>
{booking.description && ( {booking.description && (
<div className="text-sm text-gray-500 truncate max-w-52 md:max-w-96" title={booking.description}> <div className="max-w-52 truncate text-sm text-gray-500 md:max-w-96" title={booking.description}>
&quot;{booking.description}&quot; &quot;{booking.description}&quot;
</div> </div>
)} )}
@ -119,7 +119,7 @@ function BookingListItem(booking: BookingItem) {
)} )}
</td> </td>
<td className="py-4 text-sm font-medium text-right ltr:pr-4 rtl:pl-4 whitespace-nowrap"> <td className="whitespace-nowrap py-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4">
{isUpcoming && !isCancelled ? ( {isUpcoming && !isCancelled ? (
<> <>
{!booking.confirmed && !booking.rejected && <TableActions actions={pendingActions} />} {!booking.confirmed && !booking.rejected && <TableActions actions={pendingActions} />}
@ -137,7 +137,7 @@ function BookingListItem(booking: BookingItem) {
const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => { const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => {
return ( return (
<span <span
className={`inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800 ${className}`}> className={`inline-flex items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ${className}`}>
{children} {children}
</span> </span>
); );

View file

@ -163,10 +163,10 @@ function DatePicker({
className={ className={
"mt-8 sm:mt-0 sm:min-w-[455px] " + "mt-8 sm:mt-0 sm:min-w-[455px] " +
(date (date
? "w-full sm:w-1/2 md:w-1/3 sm:border-r sm:dark:border-gray-800 sm:pl-4 sm:pr-6 " ? "w-full sm:w-1/2 sm:border-r sm:pl-4 sm:pr-6 sm:dark:border-gray-800 md:w-1/3 "
: "w-full sm:pl-4") : "w-full sm:pl-4")
}> }>
<div className="flex mb-4 text-xl font-light text-gray-600"> <div className="mb-4 flex text-xl font-light text-gray-600">
<span className="w-1/2 text-gray-600 dark:text-white"> <span className="w-1/2 text-gray-600 dark:text-white">
<strong className="text-gray-900 dark:text-white">{month}</strong>{" "} <strong className="text-gray-900 dark:text-white">{month}</strong>{" "}
<span className="text-gray-500">{year}</span> <span className="text-gray-500">{year}</span>
@ -175,21 +175,21 @@ function DatePicker({
<button <button
onClick={decrementMonth} onClick={decrementMonth}
className={classNames( className={classNames(
"group ltr:mr-2 rtl:ml-2 p-1", "group p-1 ltr:mr-2 rtl:ml-2",
isFirstMonth && "text-gray-400 dark:text-gray-600" isFirstMonth && "text-gray-400 dark:text-gray-600"
)} )}
disabled={isFirstMonth} disabled={isFirstMonth}
data-testid="decrementMonth"> data-testid="decrementMonth">
<ChevronLeftIcon className="w-5 h-5 group-hover:text-black dark:group-hover:text-white" /> <ChevronLeftIcon className="h-5 w-5 group-hover:text-black dark:group-hover:text-white" />
</button> </button>
<button className="p-1 group" onClick={incrementMonth} data-testid="incrementMonth"> <button className="group p-1" onClick={incrementMonth} data-testid="incrementMonth">
<ChevronRightIcon className="w-5 h-5 group-hover:text-black dark:group-hover:text-white" /> <ChevronRightIcon className="h-5 w-5 group-hover:text-black dark:group-hover:text-white" />
</button> </button>
</div> </div>
</div> </div>
<div className="grid grid-cols-7 gap-4 text-center border-t border-b dark:border-gray-800 sm:border-0"> <div className="grid grid-cols-7 gap-4 border-t border-b text-center dark:border-gray-800 sm:border-0">
{weekdayNames(i18n.language, weekStart === "Sunday" ? 0 : 1, "short").map((weekDay) => ( {weekdayNames(i18n.language, weekStart === "Sunday" ? 0 : 1, "short").map((weekDay) => (
<div key={weekDay} className="my-4 text-xs tracking-widest text-gray-500 uppercase"> <div key={weekDay} className="my-4 text-xs uppercase tracking-widest text-gray-500">
{weekDay} {weekDay}
</div> </div>
))} ))}
@ -209,9 +209,9 @@ function DatePicker({
onClick={() => onDatePicked(browsingDate.date(day.date))} onClick={() => onDatePicked(browsingDate.date(day.date))}
disabled={day.disabled} disabled={day.disabled}
className={classNames( className={classNames(
"absolute w-full top-0 left-0 right-0 bottom-0 rounded-sm text-center mx-auto", "absolute top-0 left-0 right-0 bottom-0 mx-auto w-full rounded-sm text-center",
"hover:border hover:border-brand dark:hover:border-white", "hover:border hover:border-brand dark:hover:border-white",
day.disabled ? "text-gray-400 font-light hover:border-0 cursor-default" : "font-medium", day.disabled ? "cursor-default font-light text-gray-400 hover:border-0" : "font-medium",
date && date.isSame(browsingDate.date(day.date), "day") date && date.isSame(browsingDate.date(day.date), "day")
? "bg-brand text-brandcontrast" ? "bg-brand text-brandcontrast"
: !day.disabled : !day.disabled

View file

@ -35,8 +35,8 @@ const TimeOptions: FC<Props> = (props) => {
}; };
return selectedTimeZone !== "" ? ( return selectedTimeZone !== "" ? (
<div className="absolute z-10 w-full px-4 py-2 bg-white border border-gray-200 rounded-sm max-w-80 dark:bg-gray-700 dark:border-0"> <div className="absolute z-10 w-full max-w-80 rounded-sm border border-gray-200 bg-white px-4 py-2 dark:border-0 dark:bg-gray-700">
<div className="flex mb-4"> <div className="mb-4 flex">
<div className="w-1/2 font-medium text-gray-600 dark:text-white">{t("time_options")}</div> <div className="w-1/2 font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
<div className="w-1/2"> <div className="w-1/2">
<Switch.Group as="div" className="flex items-center justify-end"> <Switch.Group as="div" className="flex items-center justify-end">
@ -47,15 +47,15 @@ const TimeOptions: FC<Props> = (props) => {
checked={is24hClock} checked={is24hClock}
onChange={handle24hClockToggle} onChange={handle24hClockToggle}
className={classNames( className={classNames(
is24hClock ? "bg-brand text-brandcontrast" : "dark:bg-gray-600 bg-gray-200", is24hClock ? "bg-brand text-brandcontrast" : "bg-gray-200 dark:bg-gray-600",
"relative inline-flex flex-shrink-0 h-5 w-8 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black" "relative inline-flex h-5 w-8 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
)}> )}>
<span className="sr-only">{t("use_setting")}</span> <span className="sr-only">{t("use_setting")}</span>
<span <span
aria-hidden="true" aria-hidden="true"
className={classNames( className={classNames(
is24hClock ? "translate-x-3" : "translate-x-0", is24hClock ? "translate-x-3" : "translate-x-0",
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200" "pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
)} )}
/> />
</Switch> </Switch>
@ -69,7 +69,7 @@ const TimeOptions: FC<Props> = (props) => {
id="timeZone" id="timeZone"
value={selectedTimeZone} value={selectedTimeZone}
onChange={(tz: ITimezoneOption) => setSelectedTimeZone(tz.value)} onChange={(tz: ITimezoneOption) => setSelectedTimeZone(tz.value)}
className="block w-full mt-1 mb-2 border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="mt-1 mb-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
/> />
</div> </div>
) : null; ) : null;

View file

@ -110,11 +110,11 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
<div> <div>
<main <main
className={ className={
"mx-auto my-0 md:my-24 transition-max-width ease-in-out duration-500 " + "transition-max-width mx-auto my-0 duration-500 ease-in-out md:my-24 " +
(selectedDate ? "max-w-5xl" : "max-w-3xl") (selectedDate ? "max-w-5xl" : "max-w-3xl")
}> }>
{isReady && ( {isReady && (
<div className="bg-white border-gray-200 rounded-sm sm:dark:border-gray-600 dark:bg-gray-900 md:border"> <div className="rounded-sm border-gray-200 bg-white dark:bg-gray-900 sm:dark:border-gray-600 md:border">
{/* mobile: details */} {/* mobile: details */}
<div className="block p-4 sm:p-8 md:hidden"> <div className="block p-4 sm:p-8 md:hidden">
<div className="flex items-center"> <div className="flex items-center">
@ -139,12 +139,12 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
<div className="flex gap-2 text-xs font-medium text-gray-600"> <div className="flex gap-2 text-xs font-medium text-gray-600">
{eventType.title} {eventType.title}
<div> <div>
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <ClockIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{eventType.length} {t("minutes")} {eventType.length} {t("minutes")}
</div> </div>
{eventType.price > 0 && ( {eventType.price > 0 && (
<div> <div>
<CreditCardIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <CreditCardIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en"> <IntlProvider locale="en">
<FormattedNumber <FormattedNumber
value={eventType.price / 100.0} value={eventType.price / 100.0}
@ -160,10 +160,10 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
<p className="mt-3 text-gray-600 dark:text-gray-200">{eventType.description}</p> <p className="mt-3 text-gray-600 dark:text-gray-200">{eventType.description}</p>
</div> </div>
<div className="px-4 sm:flex sm:py-5 sm:p-4"> <div className="px-4 sm:flex sm:p-4 sm:py-5">
<div <div
className={ className={
"hidden md:block pr-8 sm:border-r sm:dark:border-gray-800 " + "hidden pr-8 sm:border-r sm:dark:border-gray-800 md:block " +
(selectedDate ? "sm:w-1/3" : "sm:w-1/2") (selectedDate ? "sm:w-1/3" : "sm:w-1/2")
}> }>
<AvatarGroup <AvatarGroup
@ -183,16 +183,16 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
truncateAfter={3} truncateAfter={3}
/> />
<h2 className="mt-3 font-medium text-gray-500 dark:text-gray-300">{profile.name}</h2> <h2 className="mt-3 font-medium text-gray-500 dark:text-gray-300">{profile.name}</h2>
<h1 className="mb-4 text-3xl font-semibold text-gray-800 font-cal dark:text-white"> <h1 className="mb-4 font-cal text-3xl font-semibold text-gray-800 dark:text-white">
{eventType.title} {eventType.title}
</h1> </h1>
<p className="px-2 py-1 mb-1 -ml-2 text-gray-500"> <p className="mb-1 -ml-2 px-2 py-1 text-gray-500">
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <ClockIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{eventType.length} {t("minutes")} {eventType.length} {t("minutes")}
</p> </p>
{eventType.price > 0 && ( {eventType.price > 0 && (
<p className="px-2 py-1 mb-1 -ml-2 text-gray-500"> <p className="mb-1 -ml-2 px-2 py-1 text-gray-500">
<CreditCardIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <CreditCardIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en"> <IntlProvider locale="en">
<FormattedNumber <FormattedNumber
value={eventType.price / 100.0} value={eventType.price / 100.0}
@ -221,7 +221,7 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
minimumBookingNotice={eventType.minimumBookingNotice} minimumBookingNotice={eventType.minimumBookingNotice}
/> />
<div className="block mt-4 ml-1 sm:hidden"> <div className="mt-4 ml-1 block sm:hidden">
<TimezoneDropdown /> <TimezoneDropdown />
</div> </div>
@ -249,13 +249,13 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
function TimezoneDropdown() { function TimezoneDropdown() {
return ( return (
<Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}> <Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
<Collapsible.Trigger className="px-2 py-1 mb-1 -ml-2 text-left text-gray-500 min-w-32"> <Collapsible.Trigger className="mb-1 -ml-2 min-w-32 px-2 py-1 text-left text-gray-500">
<GlobeIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <GlobeIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{timeZone()} {timeZone()}
{isTimeOptionsOpen ? ( {isTimeOptionsOpen ? (
<ChevronUpIcon className="inline-block w-4 h-4 ml-1 -mt-1" /> <ChevronUpIcon className="ml-1 -mt-1 inline-block h-4 w-4" />
) : ( ) : (
<ChevronDownIcon className="inline-block w-4 h-4 ml-1 -mt-1" /> <ChevronDownIcon className="ml-1 -mt-1 inline-block h-4 w-4" />
)} )}
</Collapsible.Trigger> </Collapsible.Trigger>
<Collapsible.Content> <Collapsible.Content>

View file

@ -291,9 +291,9 @@ const BookingPage = (props: BookingPageProps) => {
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<CustomBranding val={props.profile.brandColor} /> <CustomBranding val={props.profile.brandColor} />
<main className="max-w-3xl mx-auto my-0 rounded-sm sm:my-24 sm:border sm:dark:border-gray-600"> <main className=" mx-auto my-0 max-w-3xl rounded-sm sm:my-24 sm:border sm:dark:border-gray-600">
{isReady && ( {isReady && (
<div className="overflow-hidden bg-white border border-gray-200 dark:bg-neutral-900 dark:border-0 sm:rounded-sm"> <div className="overflow-hidden border border-gray-200 bg-white dark:border-0 dark:bg-neutral-900 sm:rounded-sm">
<div className="px-4 py-5 sm:flex sm:p-4"> <div className="px-4 py-5 sm:flex sm:p-4">
<div className="sm:w-1/2 sm:border-r sm:dark:border-gray-800"> <div className="sm:w-1/2 sm:border-r sm:dark:border-gray-800">
<AvatarGroup <AvatarGroup
@ -307,19 +307,19 @@ const BookingPage = (props: BookingPageProps) => {
})) }))
)} )}
/> />
<h2 className="mt-2 font-medium text-gray-500 font-cal dark:text-gray-300"> <h2 className="mt-2 font-cal font-medium text-gray-500 dark:text-gray-300">
{props.profile.name} {props.profile.name}
</h2> </h2>
<h1 className="mb-4 text-3xl font-semibold text-gray-800 dark:text-white"> <h1 className="mb-4 text-3xl font-semibold text-gray-800 dark:text-white">
{props.eventType.title} {props.eventType.title}
</h1> </h1>
<p className="mb-2 text-gray-500"> <p className="mb-2 text-gray-500">
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <ClockIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{props.eventType.length} {t("minutes")} {props.eventType.length} {t("minutes")}
</p> </p>
{props.eventType.price > 0 && ( {props.eventType.price > 0 && (
<p className="px-2 py-1 mb-1 -ml-2 text-gray-500"> <p className="mb-1 -ml-2 px-2 py-1 text-gray-500">
<CreditCardIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <CreditCardIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en"> <IntlProvider locale="en">
<FormattedNumber <FormattedNumber
value={props.eventType.price / 100.0} value={props.eventType.price / 100.0}
@ -331,22 +331,22 @@ const BookingPage = (props: BookingPageProps) => {
)} )}
{selectedLocation === LocationType.InPerson && ( {selectedLocation === LocationType.InPerson && (
<p className="mb-2 text-gray-500"> <p className="mb-2 text-gray-500">
<LocationMarkerIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <LocationMarkerIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{getLocationValue({ locationType: selectedLocation })} {getLocationValue({ locationType: selectedLocation })}
</p> </p>
)} )}
{selectedLocation === LocationType.Jitsi && ( {selectedLocation === LocationType.Jitsi && (
<p className="mb-2 text-gray-500"> <p className="mb-2 text-gray-500">
<LocationMarkerIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <LocationMarkerIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
Jitsi Meet Jitsi Meet
</p> </p>
)} )}
<p className="mb-4 text-green-500"> <p className="mb-4 text-green-500">
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" /> <CalendarIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{parseDate(date)} {parseDate(date)}
</p> </p>
{eventTypeDetail.isWeb3Active && eventType.metadata.smartContractAddress && ( {eventTypeDetail.isWeb3Active && eventType.metadata.smartContractAddress && (
<p className="px-2 py-1 mb-1 -ml-2 text-gray-500"> <p className="mb-1 -ml-2 px-2 py-1 text-gray-500">
Requires ownership of a token belonging to the following address:{" "} Requires ownership of a token belonging to the following address:{" "}
{eventType.metadata.smartContractAddress} {eventType.metadata.smartContractAddress}
</p> </p>
@ -366,7 +366,7 @@ const BookingPage = (props: BookingPageProps) => {
name="name" name="name"
id="name" id="name"
required required
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder="John Doe" placeholder="John Doe"
/> />
</div> </div>
@ -381,7 +381,7 @@ const BookingPage = (props: BookingPageProps) => {
<EmailInput <EmailInput
{...bookingForm.register("email")} {...bookingForm.register("email")}
required required
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder="you@example.com" placeholder="you@example.com"
/> />
</div> </div>
@ -395,12 +395,12 @@ const BookingPage = (props: BookingPageProps) => {
<label key={i} className="block"> <label key={i} className="block">
<input <input
type="radio" type="radio"
className="w-4 h-4 text-black border-gray-300 ltr:mr-2 rtl:ml-2 location focus:ring-black" className="location h-4 w-4 border-gray-300 text-black focus:ring-black ltr:mr-2 rtl:ml-2"
{...bookingForm.register("locationType", { required: true })} {...bookingForm.register("locationType", { required: true })}
value={location.type} value={location.type}
defaultChecked={selectedLocation === location.type} defaultChecked={selectedLocation === location.type}
/> />
<span className="ltr:ml-2 rtl:mr-2text-sm dark:text-gray-500"> <span className="rtl:mr-2text-sm ltr:ml-2 dark:text-gray-500">
{locationLabels[location.type]} {locationLabels[location.type]}
</span> </span>
</label> </label>
@ -428,7 +428,7 @@ const BookingPage = (props: BookingPageProps) => {
{input.type !== EventTypeCustomInputType.BOOL && ( {input.type !== EventTypeCustomInputType.BOOL && (
<label <label
htmlFor={"custom_" + input.id} htmlFor={"custom_" + input.id}
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white"> className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{input.label} {input.label}
</label> </label>
)} )}
@ -439,7 +439,7 @@ const BookingPage = (props: BookingPageProps) => {
})} })}
id={"custom_" + input.id} id={"custom_" + input.id}
rows={3} rows={3}
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder={input.placeholder} placeholder={input.placeholder}
/> />
)} )}
@ -450,7 +450,7 @@ const BookingPage = (props: BookingPageProps) => {
required: input.required, required: input.required,
})} })}
id={"custom_" + input.id} id={"custom_" + input.id}
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder={input.placeholder} placeholder={input.placeholder}
/> />
)} )}
@ -461,24 +461,24 @@ const BookingPage = (props: BookingPageProps) => {
required: input.required, required: input.required,
})} })}
id={"custom_" + input.id} id={"custom_" + input.id}
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder="" placeholder=""
/> />
)} )}
{input.type === EventTypeCustomInputType.BOOL && ( {input.type === EventTypeCustomInputType.BOOL && (
<div className="flex items-center h-5"> <div className="flex h-5 items-center">
<input <input
type="checkbox" type="checkbox"
{...bookingForm.register(`customInputs.${input.id}`, { {...bookingForm.register(`customInputs.${input.id}`, {
required: input.required, required: input.required,
})} })}
id={"custom_" + input.id} id={"custom_" + input.id}
className="w-4 h-4 text-black border-gray-300 rounded ltr:mr-2 rtl:ml-2 focus:ring-black" className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black ltr:mr-2 rtl:ml-2"
placeholder="" placeholder=""
/> />
<label <label
htmlFor={"custom_" + input.id} htmlFor={"custom_" + input.id}
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white"> className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{input.label} {input.label}
</label> </label>
</div> </div>
@ -491,7 +491,7 @@ const BookingPage = (props: BookingPageProps) => {
<label <label
onClick={() => setGuestToggle(!guestToggle)} onClick={() => setGuestToggle(!guestToggle)}
htmlFor="guests" htmlFor="guests"
className="block mb-1 text-sm font-medium dark:text-white hover:cursor-pointer"> className="mb-1 block text-sm font-medium hover:cursor-pointer dark:text-white">
{/*<UserAddIcon className="inline-block w-5 h-5 mr-1 -mt-1" />*/} {/*<UserAddIcon className="inline-block w-5 h-5 mr-1 -mt-1" />*/}
{t("additional_guests")} {t("additional_guests")}
</label> </label>
@ -500,7 +500,7 @@ const BookingPage = (props: BookingPageProps) => {
<div> <div>
<label <label
htmlFor="guests" htmlFor="guests"
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white"> className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{t("guests")} {t("guests")}
</label> </label>
<Controller <Controller
@ -536,14 +536,14 @@ const BookingPage = (props: BookingPageProps) => {
<div className="mb-4"> <div className="mb-4">
<label <label
htmlFor="notes" htmlFor="notes"
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white"> className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{t("additional_notes")} {t("additional_notes")}
</label> </label>
<textarea <textarea
{...bookingForm.register("notes")} {...bookingForm.register("notes")}
id="notes" id="notes"
rows={3} rows={3}
className="block w-full border-gray-300 rounded-sm shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black dark:border-gray-900 dark:bg-black dark:text-white sm:text-sm"
placeholder={t("share_additional_notes")} placeholder={t("share_additional_notes")}
/> />
</div> </div>
@ -557,10 +557,10 @@ const BookingPage = (props: BookingPageProps) => {
</div> </div>
</Form> </Form>
{mutation.isError && ( {mutation.isError && (
<div className="p-4 mt-2 border-l-4 border-yellow-400 bg-yellow-50"> <div className="mt-2 border-l-4 border-yellow-400 bg-yellow-50 p-4">
<div className="flex"> <div className="flex">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<ExclamationIcon className="w-5 h-5 text-yellow-400" aria-hidden="true" /> <ExclamationIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
</div> </div>
<div className="ltr:ml-3 rtl:mr-3"> <div className="ltr:ml-3 rtl:mr-3">
<p className="text-sm text-yellow-700"> <p className="text-sm text-yellow-700">

View file

@ -33,26 +33,26 @@ export default function ConfirmationDialogContent(props: PropsWithChildren<Confi
<DialogContent> <DialogContent>
<div className="flex"> <div className="flex">
{variety && ( {variety && (
<div className="ltr:mr-3 mt-0.5"> <div className="mt-0.5 ltr:mr-3">
{variety === "danger" && ( {variety === "danger" && (
<div className="p-2 mx-auto text-center bg-red-100 rounded-full"> <div className="mx-auto rounded-full bg-red-100 p-2 text-center">
<ExclamationIcon className="w-5 h-5 text-red-600" /> <ExclamationIcon className="h-5 w-5 text-red-600" />
</div> </div>
)} )}
{variety === "warning" && ( {variety === "warning" && (
<div className="p-2 mx-auto text-center bg-orange-100 rounded-full"> <div className="mx-auto rounded-full bg-orange-100 p-2 text-center">
<ExclamationIcon className="w-5 h-5 text-orange-600" /> <ExclamationIcon className="h-5 w-5 text-orange-600" />
</div> </div>
)} )}
{variety === "success" && ( {variety === "success" && (
<div className="p-2 mx-auto text-center bg-green-100 rounded-full"> <div className="mx-auto rounded-full bg-green-100 p-2 text-center">
<CheckIcon className="w-5 h-5 text-green-600" /> <CheckIcon className="h-5 w-5 text-green-600" />
</div> </div>
)} )}
</div> </div>
)} )}
<div> <div>
<DialogPrimitive.Title className="text-xl font-bold text-gray-900 font-cal"> <DialogPrimitive.Title className="font-cal text-xl font-bold text-gray-900">
{title} {title}
</DialogPrimitive.Title> </DialogPrimitive.Title>
<DialogPrimitive.Description className="text-sm text-neutral-500"> <DialogPrimitive.Description className="text-sm text-neutral-500">
@ -60,7 +60,7 @@ export default function ConfirmationDialogContent(props: PropsWithChildren<Confi
</DialogPrimitive.Description> </DialogPrimitive.Description>
</div> </div>
</div> </div>
<div className="mt-5 sm:mt-8 sm:flex sm:flex-row-reverse gap-x-2"> <div className="mt-5 gap-x-2 sm:mt-8 sm:flex sm:flex-row-reverse">
<DialogClose onClick={onConfirm} asChild> <DialogClose onClick={onConfirm} asChild>
{confirmBtn || <Button color="primary">{confirmBtnText}</Button>} {confirmBtn || <Button color="primary">{confirmBtnText}</Button>}
</DialogClose> </DialogClose>

View file

@ -29,15 +29,15 @@ const ErrorDebugPanel: React.FC<{ error: Props["error"]; children?: never }> = (
]; ];
return ( return (
<div className="bg-white shadow overflow-hidden sm:rounded-lg"> <div className="overflow-hidden bg-white shadow sm:rounded-lg">
<div className="border-t border-gray-200 px-4 py-5 sm:p-0"> <div className="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl className="sm:divide-y sm:divide-gray-200"> <dl className="sm:divide-y sm:divide-gray-200">
{debugMap.map(([key, value]) => { {debugMap.map(([key, value]) => {
if (value !== undefined) { if (value !== undefined) {
return ( return (
<div key={key} className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"> <div key={key} className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-bold text-black">{key}</dt> <dt className="text-sm font-bold text-black">{key}</dt>
<dd className="mt-1 text-sm text-black sm:mt-0 sm:col-span-2">{value}</dd> <dd className="mt-1 text-sm text-black sm:col-span-2 sm:mt-0">{value}</dd>
</div> </div>
); );
} }
@ -53,11 +53,11 @@ export const ErrorPage: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="bg-white min-h-screen px-4"> <div className="min-h-screen bg-white px-4">
<main className="max-w-xl mx-auto pb-6 pt-16 sm:pt-24"> <main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24">
<div className="text-center"> <div className="text-center">
<p className="text-sm font-semibold text-black uppercase tracking-wide">{statusCode}</p> <p className="text-sm font-semibold uppercase tracking-wide text-black">{statusCode}</p>
<h1 className="mt-2 text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl"> <h1 className="mt-2 text-4xl font-extrabold tracking-tight text-gray-900 sm:text-5xl">
{message} {message}
</h1> </h1>
</div> </div>

View file

@ -139,7 +139,7 @@ export default function CreateEventTypeButton(props: Props) {
{props.options.map((option) => ( {props.options.map((option) => (
<DropdownMenuItem <DropdownMenuItem
key={option.slug} key={option.slug}
className="px-3 py-2 cursor-pointer hover:bg-neutral-100 focus:outline-none" className="cursor-pointer px-3 py-2 hover:bg-neutral-100 focus:outline-none"
onSelect={() => openModal(option)}> onSelect={() => openModal(option)}>
<Avatar <Avatar
alt={option.name || ""} alt={option.name || ""}
@ -207,7 +207,7 @@ export default function CreateEventTypeButton(props: Props) {
className="pr-20" className="pr-20"
{...register("length", { valueAsNumber: true })} {...register("length", { valueAsNumber: true })}
/> />
<div className="absolute inset-y-0 right-0 flex items-center pt-4 mt-1.5 pr-3 text-sm text-gray-400"> <div className="absolute inset-y-0 right-0 mt-1.5 flex items-center pt-4 pr-3 text-sm text-gray-400">
{t("minutes")} {t("minutes")}
</div> </div>
</div> </div>
@ -227,20 +227,20 @@ export default function CreateEventTypeButton(props: Props) {
<RadioArea.Group <RadioArea.Group
{...register("schedulingType")} {...register("schedulingType")}
onChange={(val) => form.setValue("schedulingType", val as SchedulingType)} onChange={(val) => form.setValue("schedulingType", val as SchedulingType)}
className="relative flex mt-1 rtl:space-x-reverse space-x-6 rounded-sm shadow-sm"> className="relative mt-1 flex space-x-6 rounded-sm shadow-sm rtl:space-x-reverse">
<RadioArea.Item value={SchedulingType.COLLECTIVE} className="w-1/2 text-sm"> <RadioArea.Item value={SchedulingType.COLLECTIVE} className="w-1/2 text-sm">
<strong className="block mb-1">{t("collective")}</strong> <strong className="mb-1 block">{t("collective")}</strong>
<p>{t("collective_description")}</p> <p>{t("collective_description")}</p>
</RadioArea.Item> </RadioArea.Item>
<RadioArea.Item value={SchedulingType.ROUND_ROBIN} className="w-1/2 text-sm"> <RadioArea.Item value={SchedulingType.ROUND_ROBIN} className="w-1/2 text-sm">
<strong className="block mb-1">{t("round_robin")}</strong> <strong className="mb-1 block">{t("round_robin")}</strong>
<p>{t("round_robin_description")}</p> <p>{t("round_robin_description")}</p>
</RadioArea.Item> </RadioArea.Item>
</RadioArea.Group> </RadioArea.Group>
</div> </div>
)} )}
</div> </div>
<div className="flex flex-row-reverse mt-8 gap-x-2"> <div className="mt-8 flex flex-row-reverse gap-x-2">
<Button type="submit" loading={createMutation.isLoading}> <Button type="submit" loading={createMutation.isLoading}>
{t("continue")} {t("continue")}
</Button> </Button>

View file

@ -32,31 +32,31 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript
<> <>
<div className={classNames("text-neutral-500 dark:text-white", className)}> <div className={classNames("text-neutral-500 dark:text-white", className)}>
{eventType.description && ( {eventType.description && (
<h2 className="opacity-60 text-ellipsis overflow-hidden max-w-[280px] sm:max-w-[500px]"> <h2 className="max-w-[280px] overflow-hidden text-ellipsis opacity-60 sm:max-w-[500px]">
{eventType.description.substring(0, 100)} {eventType.description.substring(0, 100)}
{eventType.description.length > 100 && "..."} {eventType.description.length > 100 && "..."}
</h2> </h2>
)} )}
<ul className="flex mt-2 space-x-4 rtl:space-x-reverse "> <ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
<li className="flex whitespace-nowrap"> <li className="flex whitespace-nowrap">
<ClockIcon className="inline mt-0.5 mr-1.5 h-4 w-4 text-neutral-400" aria-hidden="true" /> <ClockIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
{eventType.length}m {eventType.length}m
</li> </li>
{eventType.schedulingType ? ( {eventType.schedulingType ? (
<li className="flex whitespace-nowrap"> <li className="flex whitespace-nowrap">
<UsersIcon className="inline mt-0.5 mr-1.5 h-4 w-4 text-neutral-400" aria-hidden="true" /> <UsersIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
{eventType.schedulingType === SchedulingType.ROUND_ROBIN && t("round_robin")} {eventType.schedulingType === SchedulingType.ROUND_ROBIN && t("round_robin")}
{eventType.schedulingType === SchedulingType.COLLECTIVE && t("collective")} {eventType.schedulingType === SchedulingType.COLLECTIVE && t("collective")}
</li> </li>
) : ( ) : (
<li className="flex whitespace-nowrap"> <li className="flex whitespace-nowrap">
<UserIcon className="inline mt-0.5 mr-1.5 h-4 w-4 text-neutral-400" aria-hidden="true" /> <UserIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
{t("1_on_1")} {t("1_on_1")}
</li> </li>
)} )}
{eventType.price > 0 && ( {eventType.price > 0 && (
<li className="flex whitespace-nowrap"> <li className="flex whitespace-nowrap">
<CreditCardIcon className="inline mt-0.5 mr-1.5 h-4 w-4 text-neutral-400" aria-hidden="true" /> <CreditCardIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<IntlProvider locale="en"> <IntlProvider locale="en">
<FormattedNumber <FormattedNumber
value={eventType.price / 100.0} value={eventType.price / 100.0}

View file

@ -17,7 +17,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(pro
{...props} {...props}
ref={ref} ref={ref}
className={classNames( className={classNames(
"mt-1 block w-full border border-gray-300 rounded-sm shadow-sm py-2 px-3 focus:outline-none focus:ring-1 focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm", "mt-1 block w-full rounded-sm border border-gray-300 py-2 px-3 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-1 focus:ring-neutral-800 sm:text-sm",
props.className props.className
)} )}
/> />
@ -34,7 +34,7 @@ export function Label(props: JSX.IntrinsicElements["label"]) {
export function InputLeading(props: JSX.IntrinsicElements["div"]) { export function InputLeading(props: JSX.IntrinsicElements["div"]) {
return ( return (
<span className="inline-flex items-center flex-shrink-0 px-3 text-gray-500 border border-r-0 border-gray-300 rounded-l-sm bg-gray-50 sm:text-sm"> <span className="inline-flex flex-shrink-0 items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm">
{props.children} {props.children}
</span> </span>
); );
@ -69,7 +69,7 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
</Label> </Label>
)} )}
{addOnLeading ? ( {addOnLeading ? (
<div className="flex mt-1 rounded-md shadow-sm"> <div className="mt-1 flex rounded-md shadow-sm">
{addOnLeading} {addOnLeading}
<Input <Input
id={id} id={id}
@ -136,7 +136,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function
ref={ref} ref={ref}
{...props} {...props}
className={classNames( className={classNames(
"block w-full font-mono border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm", "font-mono block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm",
props.className props.className
)} )}
/> />
@ -221,7 +221,7 @@ export function InputGroupBox(props: JSX.IntrinsicElements["div"]) {
return ( return (
<div <div
{...props} {...props}
className={classNames("p-2 bg-white border border-gray-300 rounded-sm space-y-2", props.className)}> className={classNames("space-y-2 rounded-sm border border-gray-300 bg-white p-2", props.className)}>
{props.children} {props.children}
</div> </div>
); );

View file

@ -122,7 +122,7 @@ function ConnectedCalendarsList(props: Props) {
onOpenChange={props.onChanged} onOpenChange={props.onChanged}
/> />
}> }>
<ul className="p-4 space-y-2"> <ul className="space-y-2 p-4">
{item.calendars.map((cal) => ( {item.calendars.map((cal) => (
<CalendarSwitch <CalendarSwitch
key={cal.externalId} key={cal.externalId}

View file

@ -14,9 +14,9 @@ function IntegrationListItem(props: {
}): JSX.Element { }): JSX.Element {
return ( return (
<ListItem expanded={!!props.children} className={classNames("flex-col")}> <ListItem expanded={!!props.children} className={classNames("flex-col")}>
<div className={classNames("flex flex-1 rtl:space-x-reverse space-x-2 w-full p-3 items-center")}> <div className={classNames("flex w-full flex-1 items-center space-x-2 p-3 rtl:space-x-reverse")}>
<Image width={40} height={40} src={`/${props.imageSrc}`} alt={props.title} /> <Image width={40} height={40} src={`/${props.imageSrc}`} alt={props.title} />
<div className="flex-grow pl-2 truncate"> <div className="flex-grow truncate pl-2">
<ListItemTitle component="h3">{props.title}</ListItemTitle> <ListItemTitle component="h3">{props.title}</ListItemTitle>
<ListItemText component="p">{props.description}</ListItemText> <ListItemText component="p">{props.description}</ListItemText>
</div> </div>

View file

@ -1,12 +1,17 @@
import { EventTypeCustomInput, EventTypeCustomInputType } from "@prisma/client"; import { EventTypeCustomInput, EventTypeCustomInputType } from "@prisma/client";
import React, { FC } from "react"; import React, { FC } from "react";
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form"; import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
import Select, { OptionTypeBase } from "react-select"; import Select from "react-select";
import { useLocale } from "@lib/hooks/useLocale"; import { useLocale } from "@lib/hooks/useLocale";
import Button from "@components/ui/Button"; import Button from "@components/ui/Button";
interface OptionTypeBase {
label: string;
value: EventTypeCustomInputType;
}
interface Props { interface Props {
onSubmit: SubmitHandler<IFormInput>; onSubmit: SubmitHandler<IFormInput>;
onCancel: () => void; onCancel: () => void;
@ -50,8 +55,8 @@ const CustomInputTypeForm: FC<Props> = (props) => {
defaultValue={selectedInputOption} defaultValue={selectedInputOption}
options={inputOptions} options={inputOptions}
isSearchable={false} isSearchable={false}
className="flex-1 block w-full min-w-0 mt-1 mb-2 border-gray-300 rounded-none focus:ring-primary-500 focus:border-primary-500 rounded-r-md sm:text-sm" className="mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-md border-gray-300 focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
onChange={(option) => field.onChange(option.value)} onChange={(option) => option && field.onChange(option.value)}
value={selectedInputOption} value={selectedInputOption}
onBlur={field.onBlur} onBlur={field.onBlur}
name={field.name} name={field.name}
@ -68,7 +73,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
type="text" type="text"
id="label" id="label"
required required
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
defaultValue={selectedCustomInput?.label} defaultValue={selectedCustomInput?.label}
{...register("label", { required: true })} {...register("label", { required: true })}
/> />
@ -84,18 +89,18 @@ const CustomInputTypeForm: FC<Props> = (props) => {
<input <input
type="text" type="text"
id="placeholder" id="placeholder"
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
defaultValue={selectedCustomInput?.placeholder} defaultValue={selectedCustomInput?.placeholder}
{...register("placeholder")} {...register("placeholder")}
/> />
</div> </div>
</div> </div>
)} )}
<div className="flex items-center h-5"> <div className="flex h-5 items-center">
<input <input
id="required" id="required"
type="checkbox" type="checkbox"
className="w-4 h-4 ltr:mr-2 rtl:ml-2 border-gray-300 rounded focus:ring-primary-500 text-primary-600" className="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500 ltr:mr-2 rtl:ml-2"
defaultChecked={selectedCustomInput?.required ?? true} defaultChecked={selectedCustomInput?.required ?? true}
{...register("required")} {...register("required")}
/> />

View file

@ -57,7 +57,7 @@ const ChangePasswordSection = () => {
return ( return (
<> <>
<div className="mt-6"> <div className="mt-6">
<h2 className="text-lg font-medium leading-6 text-gray-900 font-cal">{t("change_password")}</h2> <h2 className="font-cal text-lg font-medium leading-6 text-gray-900">{t("change_password")}</h2>
</div> </div>
<form className="divide-y divide-gray-200 lg:col-span-9" onSubmit={changePasswordHandler}> <form className="divide-y divide-gray-200 lg:col-span-9" onSubmit={changePasswordHandler}>
<div className="py-6 lg:pb-8"> <div className="py-6 lg:pb-8">
@ -74,12 +74,12 @@ const ChangePasswordSection = () => {
name="current_password" name="current_password"
id="current_password" id="current_password"
required required
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder={t("your_old_password")} placeholder={t("your_old_password")}
/> />
</div> </div>
</div> </div>
<div className="w-1/2 ml-2"> <div className="ml-2 w-1/2">
<label htmlFor="new_password" className="block text-sm font-medium text-gray-700"> <label htmlFor="new_password" className="block text-sm font-medium text-gray-700">
{t("new_password")} {t("new_password")}
</label> </label>
@ -91,7 +91,7 @@ const ChangePasswordSection = () => {
value={newPassword} value={newPassword}
required required
onInput={(e) => setNewPassword(e.currentTarget.value)} onInput={(e) => setNewPassword(e.currentTarget.value)}
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder={t("super_secure_new_password")} placeholder={t("super_secure_new_password")}
/> />
</div> </div>

View file

@ -70,7 +70,7 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
required required
value={password} value={password}
onInput={(e) => setPassword(e.currentTarget.value)} onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
/> />
</div> </div>

View file

@ -139,7 +139,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
required required
value={password} value={password}
onInput={(e) => setPassword(e.currentTarget.value)} onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
/> />
</div> </div>
@ -152,7 +152,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
<div className="flex justify-center"> <div className="flex justify-center">
<img src={dataUri} /> <img src={dataUri} />
</div> </div>
<p className="text-center text-xs font-mono">{secret}</p> <p className="font-mono text-center text-xs">{secret}</p>
</> </>
</WithStep> </WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}> <WithStep step={SetupStep.EnterTotpCode} current={step}>
@ -172,7 +172,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
minLength={6} minLength={6}
inputMode="numeric" inputMode="numeric"
onInput={(e) => setTotpCode(e.currentTarget.value)} onInput={(e) => setTotpCode(e.currentTarget.value)}
className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
/> />
</div> </div>

View file

@ -17,8 +17,8 @@ const TwoFactorAuthSection = ({ twoFactorEnabled }: { twoFactorEnabled: boolean
return ( return (
<> <>
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<h2 className="font-cal text-lg leading-6 font-medium text-gray-900">{t("2fa")}</h2> <h2 className="font-cal text-lg font-medium leading-6 text-gray-900">{t("2fa")}</h2>
<Badge className="text-xs ml-2" variant={enabled ? "success" : "gray"}> <Badge className="ml-2 text-xs" variant={enabled ? "success" : "gray"}>
{enabled ? t("enabled") : t("disabled")} {enabled ? t("enabled") : t("disabled")}
</Badge> </Badge>
</div> </div>

View file

@ -4,11 +4,11 @@ import React from "react";
const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => { const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => {
return ( return (
<div className="mb-4 sm:flex sm:items-start"> <div className="mb-4 sm:flex sm:items-start">
<div className="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto rounded-full bg-brand text-brandcontrast bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10"> <div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-brand bg-opacity-5 text-brandcontrast sm:mx-0 sm:h-10 sm:w-10">
<ShieldCheckIcon className="w-6 h-6 text-black" /> <ShieldCheckIcon className="h-6 w-6 text-black" />
</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 font-cal" id="modal-title"> <h3 className="font-cal text-lg font-medium leading-6 text-gray-900" id="modal-title">
{title} {title}
</h3> </h3>
<p className="text-sm text-gray-400">{description}</p> <p className="text-sm text-gray-400">{description}</p>

View file

@ -51,14 +51,14 @@ export default function MemberChangeRoleModal(props: {
</div> </div>
<form onSubmit={changeRole}> <form onSubmit={changeRole}>
<div className="mb-4"> <div className="mb-4">
<label className="block mb-2 text-sm font-medium tracking-wide text-gray-700" htmlFor="role"> <label className="mb-2 block text-sm font-medium tracking-wide text-gray-700" htmlFor="role">
{t("role")} {t("role")}
</label> </label>
<select <select
value={role} value={role}
onChange={(e) => setRole(e.target.value as MembershipRole)} onChange={(e) => setRole(e.target.value as MembershipRole)}
id="role" id="role"
className="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm"> className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm">
<option value="MEMBER">{t("member")}</option> <option value="MEMBER">{t("member")}</option>
<option value="ADMIN">{t("admin")}</option> <option value="ADMIN">{t("admin")}</option>
{/*<option value="OWNER">{t("owner")}</option> - needs dialog to confirm change of ownership */} {/*<option value="OWNER">{t("owner")}</option> - needs dialog to confirm change of ownership */}

View file

@ -53,19 +53,19 @@ export default function MemberInvitationModal(props: { team: TeamWithMembers | n
aria-labelledby="modal-title" aria-labelledby="modal-title"
role="dialog" role="dialog"
aria-modal="true"> aria-modal="true">
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"> <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
className="fixed inset-0 z-0 transition-opacity bg-gray-500 bg-opacity-75" className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden="true"></div> aria-hidden="true"></div>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203; &#8203;
</span> </span>
<div className="inline-block px-4 pt-5 pb-4 text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6"> <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="mb-4 sm:flex sm:items-start">
<div className="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto rounded-full bg-brand text-brandcontrast bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10"> <div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-brand bg-opacity-5 text-brandcontrast sm:mx-0 sm:h-10 sm:w-10">
<UserIcon className="w-6 h-6 text-brandcontrast" /> <UserIcon className="h-6 w-6 text-brandcontrast" />
</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">
@ -86,34 +86,34 @@ export default function MemberInvitationModal(props: { team: TeamWithMembers | n
required required
/> />
<div> <div>
<label className="block mb-1 text-sm font-medium tracking-wide text-gray-700" htmlFor="role"> <label className="mb-1 block text-sm font-medium tracking-wide text-gray-700" htmlFor="role">
{t("role")} {t("role")}
</label> </label>
<select <select
id="role" id="role"
className="block w-full mt-1 border-gray-300 rounded-sm shadow-sm focus:ring-black focus:border-brand sm:text-sm"> className="mt-1 block w-full rounded-sm border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm">
<option value="MEMBER">{t("member")}</option> <option value="MEMBER">{t("member")}</option>
<option value="ADMIN">{t("admin")}</option> <option value="ADMIN">{t("admin")}</option>
</select> </select>
</div> </div>
<div className="relative flex items-start"> <div className="relative flex items-start">
<div className="flex items-center h-5"> <div className="flex h-5 items-center">
<input <input
type="checkbox" type="checkbox"
name="sendInviteEmail" name="sendInviteEmail"
defaultChecked defaultChecked
id="sendInviteEmail" id="sendInviteEmail"
className="text-black border-gray-300 rounded-sm shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="rounded-sm border-gray-300 text-black shadow-sm focus:border-brand focus:ring-black sm:text-sm"
/> />
</div> </div>
<div className="ltr:ml-2 rtl:mr-2text-sm"> <div className="rtl:mr-2text-sm ltr:ml-2">
<label htmlFor="sendInviteEmail" className="font-medium text-gray-700"> <label htmlFor="sendInviteEmail" className="font-medium text-gray-700">
{t("send_invite_email")} {t("send_invite_email")}
</label> </label>
</div> </div>
</div> </div>
<div className="flex flex-row px-3 py-2 rounded-sm bg-gray-50"> <div className="flex flex-row rounded-sm bg-gray-50 px-3 py-2">
<InformationCircleIcon className="flex-shrink-0 w-5 h-5 fill-gray-400" aria-hidden="true" /> <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"> <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 Note: This will cost an extra seat ($12/m) on your subscription if this invitee does not
have a pro account.{" "} have a pro account.{" "}

View file

@ -12,7 +12,7 @@ export default function MemberList(props: Props) {
return ( return (
<div> <div>
<ul className="px-4 mb-2 -mx-4 bg-white border divide-y divide-gray-200 rounded sm:px-4 sm:mx-0"> <ul className="-mx-4 mb-2 divide-y divide-gray-200 rounded border bg-white px-4 sm:mx-0 sm:px-4">
{props.members?.map((member) => ( {props.members?.map((member) => (
<MemberListItem key={member.id} member={member} team={props.team} /> <MemberListItem key={member.id} member={member} team={props.team} />
))} ))}

View file

@ -61,25 +61,25 @@ export default function MemberListItem(props: Props) {
return ( return (
<li className="divide-y"> <li className="divide-y">
<div className="flex justify-between my-4"> <div className="my-4 flex justify-between">
<div className="flex flex-col justify-between w-full sm:flex-row"> <div className="flex w-full flex-col justify-between sm:flex-row">
<div className="flex"> <div className="flex">
<Avatar <Avatar
imageSrc={getPlaceholderAvatar(props.member?.avatar, name)} imageSrc={getPlaceholderAvatar(props.member?.avatar, name)}
alt={name || ""} alt={name || ""}
className="rounded-full w-9 h-9" className="h-9 w-9 rounded-full"
/> />
<div className="inline-block ml-3"> <div className="ml-3 inline-block">
<span className="text-sm font-bold text-neutral-700">{name}</span> <span className="text-sm font-bold text-neutral-700">{name}</span>
<span <span
className="block -mt-1 text-xs text-gray-400" className="-mt-1 block text-xs text-gray-400"
data-testid="member-email" data-testid="member-email"
data-email={props.member.email}> data-email={props.member.email}>
{props.member.email} {props.member.email}
</span> </span>
</div> </div>
</div> </div>
<div className="flex mt-2 ltr:mr-2 rtl:ml-2 sm:mt-0 sm:justify-center"> <div className="mt-2 flex ltr:mr-2 rtl:ml-2 sm:mt-0 sm:justify-center">
{/* Tooltip doesn't show... WHY????? */} {/* Tooltip doesn't show... WHY????? */}
{props.member.isMissingSeat && ( {props.member.isMissingSeat && (
<Tooltip content={t("hidden_team_member_message")}> <Tooltip content={t("hidden_team_member_message")}>
@ -102,13 +102,13 @@ export default function MemberListItem(props: Props) {
disabled={!props.member.accepted} disabled={!props.member.accepted}
onClick={() => (props.member.accepted ? setShowTeamAvailabilityModal(true) : null)} onClick={() => (props.member.accepted ? setShowTeamAvailabilityModal(true) : null)}
color="minimal" color="minimal"
className="items-center justify-center hidden w-10 h-10 px-0 py-0 border border-transparent group text-neutral-400 hover:border-gray-200 hover:bg-white sm:flex"> className="group hidden h-10 w-10 items-center justify-center border border-transparent px-0 py-0 text-neutral-400 hover:border-gray-200 hover:bg-white sm:flex">
<ClockIcon className="w-5 h-5 group-hover:text-gray-800" /> <ClockIcon className="h-5 w-5 group-hover:text-gray-800" />
</Button> </Button>
</Tooltip> </Tooltip>
<Dropdown> <Dropdown>
<DropdownMenuTrigger className="w-10 h-10 p-0 border border-transparent group text-neutral-400 hover:border-gray-200 hover:bg-white"> <DropdownMenuTrigger className="group h-10 w-10 border border-transparent p-0 text-neutral-400 hover:border-gray-200 hover:bg-white">
<DotsHorizontalIcon className="w-5 h-5 group-hover:text-gray-800" /> <DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem> <DropdownMenuItem>
@ -129,7 +129,7 @@ export default function MemberListItem(props: Props) {
onClick={() => setShowChangeMemberRoleModal(true)} onClick={() => setShowChangeMemberRoleModal(true)}
color="minimal" color="minimal"
StartIcon={PencilIcon} StartIcon={PencilIcon}
className="flex-shrink-0 w-full font-normal"> className="w-full flex-shrink-0 font-normal">
{t("edit_role")} {t("edit_role")}
</Button> </Button>
</DropdownMenuItem> </DropdownMenuItem>
@ -173,7 +173,7 @@ export default function MemberListItem(props: Props) {
{showTeamAvailabilityModal && ( {showTeamAvailabilityModal && (
<ModalContainer wide noPadding> <ModalContainer wide noPadding>
<TeamAvailabilityModal team={props.team} member={props.member} /> <TeamAvailabilityModal team={props.team} member={props.member} />
<div className="p-5 space-x-2 border-t rtl:space-x-reverse"> <div className="space-x-2 border-t p-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`}>

View file

@ -37,19 +37,19 @@ export default function TeamCreate(props: Props) {
aria-labelledby="modal-title" aria-labelledby="modal-title"
role="dialog" role="dialog"
aria-modal="true"> aria-modal="true">
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"> <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
className="fixed inset-0 z-0 transition-opacity bg-gray-500 bg-opacity-75" className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden="true"></div> aria-hidden="true"></div>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203; &#8203;
</span> </span>
<div className="inline-block px-4 pt-5 pb-4 text-left align-bottom transition-all transform bg-white rounded-sm shadow-xl sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6"> <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="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto rounded-full bg-neutral-100 sm:mx-0 sm:h-10 sm:w-10"> <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">
<UsersIcon className="w-6 h-6 text-neutral-900" /> <UsersIcon className="h-6 w-6 text-neutral-900" />
</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">
@ -72,7 +72,7 @@ export default function TeamCreate(props: Props) {
id="name" id="name"
placeholder="Acme Inc." placeholder="Acme Inc."
required required
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm" className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/> />
</div> </div>
{errorMessage && <Alert severity="error" title={errorMessage} />} {errorMessage && <Alert severity="error" title={errorMessage} />}
@ -80,7 +80,7 @@ export default function TeamCreate(props: Props) {
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
{t("create_team")} {t("create_team")}
</button> </button>
<button onClick={props.onClose} type="button" className="ltr:mr-2 btn btn-white"> <button onClick={props.onClose} type="button" className="btn btn-white ltr:mr-2">
{t("cancel")} {t("cancel")}
</button> </button>
</div> </div>

View file

@ -33,7 +33,7 @@ export default function TeamList(props: Props) {
return ( return (
<div> <div>
<ul className="mb-2 bg-white border divide-y rounded divide-neutral-200"> <ul className="mb-2 divide-y divide-neutral-200 rounded border bg-white">
{props.teams.map((team) => ( {props.teams.map((team) => (
<TeamListItem <TeamListItem
key={team?.id as number} key={team?.id as number}

View file

@ -60,9 +60,9 @@ export default function TeamListItem(props: Props) {
size={9} size={9}
imageSrc={getPlaceholderAvatar(team?.logo, team?.name as string)} imageSrc={getPlaceholderAvatar(team?.logo, team?.name as string)}
alt="Team Logo" alt="Team Logo"
className="rounded-full w-9 h-9 min-w-9 min-h-9" className="h-9 min-h-9 w-9 min-w-9 rounded-full"
/> />
<div className="inline-block ml-3"> <div className="ml-3 inline-block">
<span className="text-sm font-bold text-neutral-700">{team.name}</span> <span className="text-sm font-bold text-neutral-700">{team.name}</span>
<span className="block text-xs text-gray-400"> <span className="block text-xs text-gray-400">
{process.env.NEXT_PUBLIC_APP_URL}/team/{team.slug} {process.env.NEXT_PUBLIC_APP_URL}/team/{team.slug}
@ -75,12 +75,12 @@ export default function TeamListItem(props: Props) {
<li className="divide-y"> <li className="divide-y">
<div <div
className={classNames( className={classNames(
"flex justify-between items-center", "flex items-center justify-between",
!isInvitee && "group hover:bg-neutral-50" !isInvitee && "group hover:bg-neutral-50"
)}> )}>
{!isInvitee ? ( {!isInvitee ? (
<Link href={"/settings/teams/" + team.id}> <Link href={"/settings/teams/" + team.id}>
<a className="flex-grow text-sm truncate cursor-pointer" title={`${team.name}`}> <a className="flex-grow cursor-pointer truncate text-sm" title={`${team.name}`}>
{teamInfo} {teamInfo}
</a> </a>
</Link> </Link>
@ -108,16 +108,16 @@ export default function TeamListItem(props: Props) {
navigator.clipboard.writeText(process.env.NEXT_PUBLIC_APP_URL + "/team/" + team.slug); navigator.clipboard.writeText(process.env.NEXT_PUBLIC_APP_URL + "/team/" + team.slug);
showToast(t("link_copied"), "success"); showToast(t("link_copied"), "success");
}} }}
className="w-10 h-10 transition-none" className="h-10 w-10 transition-none"
size="icon" size="icon"
color="minimal" color="minimal"
type="button"> type="button">
<LinkIcon className="w-5 h-5 group-hover:text-gray-600" /> <LinkIcon className="h-5 w-5 group-hover:text-gray-600" />
</Button> </Button>
</Tooltip> </Tooltip>
<Dropdown> <Dropdown>
<DropdownMenuTrigger className="w-10 h-10 p-0 border border-transparent group text-neutral-400 hover:border-gray-200 "> <DropdownMenuTrigger className="group h-10 w-10 border border-transparent p-0 text-neutral-400 hover:border-gray-200 ">
<DotsHorizontalIcon className="w-5 h-5 group-hover:text-gray-800" /> <DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
{isAdmin && ( {isAdmin && (

View file

@ -13,12 +13,12 @@ interface Props {
export default function TeamPill(props: Props) { export default function TeamPill(props: Props) {
return ( return (
<div <div
className={classNames("self-center px-3 py-1 ltr:mr-2 rtl:ml-2 text-xs capitalize border rounded-md", { className={classNames("self-center rounded-md border px-3 py-1 text-xs capitalize ltr:mr-2 rtl:ml-2", {
"bg-gray-50 border-gray-200 text-gray-700": !props.color, "border-gray-200 bg-gray-50 text-gray-700": !props.color,
"bg-blue-50 border-blue-200 text-blue-700": props.color === "blue", "border-blue-200 bg-blue-50 text-blue-700": props.color === "blue",
"bg-red-50 border-red-200 text-red-700": props.color === "red", "border-red-200 bg-red-50 text-red-700": props.color === "red",
"bg-yellow-50 border-yellow-200 text-yellow-700": props.color === "yellow", "border-yellow-200 bg-yellow-50 text-yellow-700": props.color === "yellow",
"bg-green-50 border-green-200 text-green-600": props.color === "green", "border-green-200 bg-green-50 text-green-600": props.color === "green",
})}> })}>
{props.text} {props.text}
</div> </div>

View file

@ -90,7 +90,7 @@ export default function TeamSettings(props: Props) {
name="" // typescript requires name but we don't want component to render name label name="" // typescript requires name but we don't want component to render name label
id="team-url" id="team-url"
addOnLeading={ addOnLeading={
<span className="inline-flex items-center px-3 text-gray-500 border border-r-0 border-gray-300 rounded-l-sm bg-gray-50 sm:text-sm"> <span className="inline-flex items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm">
{process.env.NEXT_PUBLIC_APP_URL}/{"team/"} {process.env.NEXT_PUBLIC_APP_URL}/{"team/"}
</span> </span>
} }
@ -111,7 +111,7 @@ export default function TeamSettings(props: Props) {
id="name" id="name"
placeholder={t("your_team_name")} placeholder={t("your_team_name")}
required required
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm" className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-neutral-800 sm:text-sm"
defaultValue={team?.name as string} defaultValue={team?.name as string}
/> />
} }
@ -130,7 +130,7 @@ export default function TeamSettings(props: Props) {
name="about" name="about"
rows={3} rows={3}
defaultValue={team?.bio as string} defaultValue={team?.bio as string}
className="block w-full mt-1 border-gray-300 rounded-sm shadow-sm focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm"></textarea> className="mt-1 block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-800 focus:ring-neutral-800 sm:text-sm"></textarea>
<p className="mt-2 text-sm text-gray-500">{t("team_description")}</p> <p className="mt-2 text-sm text-gray-500">{t("team_description")}</p>
</> </>
} }
@ -143,14 +143,14 @@ export default function TeamSettings(props: Props) {
htmlFor="avatar" htmlFor="avatar"
Input={ Input={
<> <>
<div className="flex mt-1"> <div className="mt-1 flex">
<input <input
ref={logoRef} ref={logoRef}
type="hidden" type="hidden"
name="avatar" name="avatar"
id="avatar" id="avatar"
placeholder="URL" placeholder="URL"
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm" className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-neutral-800 sm:text-sm"
defaultValue={team?.logo ?? undefined} defaultValue={team?.logo ?? undefined}
/> />
<ImageUploader <ImageUploader
@ -165,7 +165,7 @@ export default function TeamSettings(props: Props) {
onClick={removeLogo} onClick={removeLogo}
color="secondary" color="secondary"
type="button" type="button"
className="py-1 ml-1 text-xs"> className="ml-1 py-1 text-xs">
{t("remove_logo")} {t("remove_logo")}
</Button> </Button>
)} )}
@ -178,14 +178,14 @@ export default function TeamSettings(props: Props) {
</div> </div>
<div className="relative flex items-start"> <div className="relative flex items-start">
<div className="flex items-center h-5"> <div className="flex h-5 items-center">
<input <input
id="hide-branding" id="hide-branding"
name="hide-branding" name="hide-branding"
type="checkbox" type="checkbox"
ref={hideBrandingRef} ref={hideBrandingRef}
defaultChecked={team?.hideBranding} defaultChecked={team?.hideBranding}
className="w-4 h-4 border-gray-300 rounded-sm focus:ring-neutral-500 text-neutral-900" className="h-4 w-4 rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500"
/> />
</div> </div>
<div className="text-sm ltr:ml-3 rtl:mr-3"> <div className="text-sm ltr:ml-3 rtl:mr-3">

View file

@ -48,7 +48,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
} }
return ( return (
<div className="px-2 space-y-6"> <div className="space-y-6 px-2">
{(props.role === MembershipRole.OWNER || props.role === MembershipRole.ADMIN) && ( {(props.role === MembershipRole.OWNER || props.role === MembershipRole.ADMIN) && (
<CreateEventTypeButton <CreateEventTypeButton
isIndividualTeam isIndividualTeam
@ -119,7 +119,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
</div> </div>
{props.team?.id && props.role !== MembershipRole.MEMBER && ( {props.team?.id && props.role !== MembershipRole.MEMBER && (
<Link href={`/settings/teams/${props.team.id}/availability`}> <Link href={`/settings/teams/${props.team.id}/availability`}>
<div className="hidden mt-5 space-y-1 sm:block"> <div className="mt-5 hidden space-y-1 sm:block">
<LinkIconButton Icon={ClockIcon}>{"View Availability"}</LinkIconButton> <LinkIconButton Icon={ClockIcon}>{"View Availability"}</LinkIconButton>
<p className="mt-2 text-sm text-gray-500">See your team members availability at a glance.</p> <p className="mt-2 text-sm text-gray-500">See your team members availability at a glance.</p>
</div> </div>

View file

@ -50,7 +50,7 @@ export function UpgradeToFlexibleProModal(props: Props) {
setErrorMessage(null); setErrorMessage(null);
}}> }}>
<DialogTrigger asChild> <DialogTrigger asChild>
<a className="underline cursor-pointer">{"Upgrade Now"}</a> <a className="cursor-pointer underline">{"Upgrade Now"}</a>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogHeader title={t("Purchase missing seats")} /> <DialogHeader title={t("Purchase missing seats")} />

View file

@ -44,7 +44,7 @@ const Team = ({ team }: TeamPageProps) => {
"absolute top-4 right-4", "absolute top-4 right-4",
"h-4 w-4", "h-4 w-4",
"transition-opacity", "transition-opacity",
"opacity-0 group-hover:opacity-100 group-hover:block" "opacity-0 group-hover:block group-hover:opacity-100"
)} )}
/> />
@ -52,9 +52,9 @@ const Team = ({ team }: TeamPageProps) => {
<Avatar <Avatar
alt={member.name || ""} alt={member.name || ""}
imageSrc={getPlaceholderAvatar(member.avatar, member.username)} imageSrc={getPlaceholderAvatar(member.avatar, member.username)}
className="w-12 h-12 -mt-4" className="-mt-4 h-12 w-12"
/> />
<section className="w-full mt-2 space-y-1"> <section className="mt-2 w-full space-y-1">
<Text variant="title">{member.name}</Text> <Text variant="title">{member.name}</Text>
<Text variant="subtitle" className=""> <Text variant="subtitle" className="">
{member.bio || t("user_from_team", { user: member.name, team: team.name })} {member.bio || t("user_from_team", { user: member.name, team: team.name })}
@ -72,7 +72,7 @@ const Team = ({ team }: TeamPageProps) => {
} }
return ( return (
<section className="flex flex-wrap justify-center max-w-5xl min-w-full mx-auto lg:min-w-lg gap-x-6 gap-y-6"> <section className="lg:min-w-lg mx-auto flex min-w-full max-w-5xl flex-wrap justify-center gap-x-6 gap-y-6">
{members.map((member) => { {members.map((member) => {
return member.username !== null && <Member key={member.id} member={member} />; return member.username !== null && <Member key={member.id} member={member} />;
})} })}

View file

@ -15,10 +15,10 @@ export function Alert(props: AlertProps) {
return ( return (
<div <div
className={classNames( className={classNames(
"rounded-sm p-3 border border-opacity-20", "rounded-sm border border-opacity-20 p-3",
props.className, props.className,
severity === "error" && "bg-red-50 text-red-800 border-red-900", severity === "error" && "border-red-900 bg-red-50 text-red-800",
severity === "warning" && "bg-yellow-50 text-yellow-700 border-yellow-700", severity === "warning" && "border-yellow-700 bg-yellow-50 text-yellow-700",
severity === "success" && "bg-gray-900 text-white" severity === "success" && "bg-gray-900 text-white"
)}> )}>
<div className="flex"> <div className="flex">
@ -33,7 +33,7 @@ export function Alert(props: AlertProps) {
<CheckCircleIcon className={classNames("h-5 w-5 text-gray-400")} aria-hidden="true" /> <CheckCircleIcon className={classNames("h-5 w-5 text-gray-400")} aria-hidden="true" />
)} )}
</div> </div>
<div className="flex-grow ml-3"> <div className="ml-3 flex-grow">
<h3 className="text-sm font-medium">{props.title}</h3> <h3 className="text-sm font-medium">{props.title}</h3>
<div className="text-sm">{props.message}</div> <div className="text-sm">{props.message}</div>
</div> </div>

View file

@ -14,26 +14,26 @@ interface Props {
export default function AuthContainer(props: React.PropsWithChildren<Props>) { export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return ( return (
<div className="flex flex-col justify-center min-h-screen py-12 bg-neutral-50 sm:px-6 lg:px-8"> <div className="flex min-h-screen flex-col justify-center bg-neutral-50 py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} /> <HeadSeo title={props.title} description={props.description} />
<div className="sm:mx-auto sm:w-full sm:max-w-md"> <div className="sm:mx-auto sm:w-full sm:max-w-md">
{props.showLogo && ( {props.showLogo && (
<img className="h-6 mx-auto" src="/calendso-logo-white-word.svg" alt="Cal.com Logo" /> <img className="mx-auto h-6" src="/calendso-logo-white-word.svg" alt="Cal.com Logo" />
)} )}
{props.heading && ( {props.heading && (
<h2 className="mt-6 text-3xl font-bold text-center font-cal text-neutral-900">{props.heading}</h2> <h2 className="mt-6 text-center font-cal text-3xl font-bold text-neutral-900">{props.heading}</h2>
)} )}
</div> </div>
{props.loading && ( {props.loading && (
<div className="absolute z-50 flex items-center w-full h-screen bg-gray-50"> <div className="absolute z-50 flex h-screen w-full items-center bg-gray-50">
<Loader /> <Loader />
</div> </div>
)} )}
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="px-4 py-8 mx-2 bg-white border rounded-sm sm:px-10 border-neutral-200"> <div className="mx-2 rounded-sm border border-neutral-200 bg-white px-4 py-8 sm:px-10">
{props.children} {props.children}
</div> </div>
<div className="mt-4 text-sm text-center text-neutral-600">{props.footerText}</div> <div className="mt-4 text-center text-sm text-neutral-600">{props.footerText}</div>
</div> </div>
</div> </div>
); );

View file

@ -36,7 +36,7 @@ export default function Avatar(props: AvatarProps) {
return title ? ( return title ? (
<Tooltip.Tooltip delayDuration={300}> <Tooltip.Tooltip delayDuration={300}>
<Tooltip.TooltipTrigger className="cursor-default">{avatar}</Tooltip.TooltipTrigger> <Tooltip.TooltipTrigger className="cursor-default">{avatar}</Tooltip.TooltipTrigger>
<Tooltip.Content className="p-2 text-sm rounded-sm shadow-sm bg-brand text-brandcontrast"> <Tooltip.Content className="rounded-sm bg-brand p-2 text-sm text-brandcontrast shadow-sm">
<Tooltip.Arrow /> <Tooltip.Arrow />
{title} {title}
</Tooltip.Content> </Tooltip.Content>

View file

@ -27,7 +27,7 @@ export const AvatarGroup = function AvatarGroup(props: AvatarGroupProps) {
: [];*/ : [];*/
return ( return (
<ul className={classNames("flex -rtl:space-x-reverse space-x-2 overflow-hidden", props.className)}> <ul className={classNames("-rtl:space-x-reverse flex space-x-2 overflow-hidden", props.className)}>
{props.items.slice(0, props.truncateAfter).map((item, idx) => { {props.items.slice(0, props.truncateAfter).map((item, idx) => {
if (item.image != null) { if (item.image != null) {
return ( return (

View file

@ -13,7 +13,7 @@ export const Badge = function Badge(props: BadgeProps) {
<span <span
{...passThroughProps} {...passThroughProps}
className={classNames( className={classNames(
"font-bold px-2 py-0.5 inline-block rounded-sm text-xs", "inline-block rounded-sm px-2 py-0.5 text-xs font-bold",
variant === "default" && "bg-yellow-100 text-yellow-800", variant === "default" && "bg-yellow-100 text-yellow-800",
variant === "success" && "bg-green-100 text-green-800", variant === "success" && "bg-green-100 text-green-800",
variant === "gray" && "bg-gray-200 text-gray-800", variant === "gray" && "bg-gray-200 text-gray-800",

View file

@ -93,17 +93,17 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
<StartIcon <StartIcon
className={classNames( className={classNames(
"inline", "inline",
size === "icon" ? "w-5 h-5 " : "w-5 h-5 ltr:mr-2 rtl:ml-2 rtl:ml-2 rtl:-mr-1 -ml-1" size === "icon" ? "h-5 w-5 " : "-ml-1 h-5 w-5 ltr:mr-2 rtl:ml-2 rtl:ml-2 rtl:-mr-1"
)} )}
/> />
)} )}
{props.children} {props.children}
{loading && ( {loading && (
<div className="absolute transform -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2"> <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform">
<svg <svg
className={classNames( className={classNames(
"w-5 h-5 mx-4 animate-spin", "mx-4 h-5 w-5 animate-spin",
color === "primary" ? "dark:text-black text-white" : "text-black" color === "primary" ? "text-white dark:text-black" : "text-black"
)} )}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
@ -122,7 +122,7 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
</svg> </svg>
</div> </div>
)} )}
{EndIcon && <EndIcon className="inline w-5 h-5 ltr:ml-2 rtl:mr-2 -mr-1" />} {EndIcon && <EndIcon className="-mr-1 inline h-5 w-5 ltr:ml-2 rtl:mr-2" />}
</> </>
); );
return props.href ? ( return props.href ? (

View file

@ -12,7 +12,7 @@ export const DropdownMenuTrigger = forwardRef<HTMLButtonElement, DropdownMenuTri
className={ className={
props.asChild props.asChild
? className ? className
: `relative inline-flex items-center px-3 py-2 ltr:ml-2 rtl:mr-2 text-sm font-medium text-gray-700 bg-transparent rounded-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-gray-100 focus:ring-neutral-500 group-hover:text-black ${className}` : `relative inline-flex items-center rounded-sm bg-transparent px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-neutral-500 focus:ring-offset-1 group-hover:text-black ltr:ml-2 rtl:mr-2 ${className}`
} }
ref={forwardedRef} ref={forwardedRef}
/> />
@ -26,7 +26,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
return ( return (
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
{...props} {...props}
className="z-10 w-48 mt-1 text-sm origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" className="z-10 mt-1 w-48 origin-top-right rounded-sm bg-white text-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
ref={forwardedRef}> ref={forwardedRef}>
{children} {children}
</DropdownMenuPrimitive.Content> </DropdownMenuPrimitive.Content>

View file

@ -7,7 +7,7 @@ export default function InfoBadge({ content }: { content: string }) {
<> <>
<Tooltip content={content}> <Tooltip content={content}>
<span title={content}> <span title={content}>
<InformationCircleIcon className="relative w-4 h-4 mt-px text-gray-500 top-px left-1 right-1" /> <InformationCircleIcon className="relative top-px left-1 right-1 mt-px h-4 w-4 text-gray-500" />
</span> </span>
</Tooltip> </Tooltip>
</> </>

View file

@ -12,8 +12,8 @@ export default function LinkIconButton(props: LinkIconButtonProps) {
<button <button
type="button" type="button"
{...props} {...props}
className="flex items-center px-2 py-1 text-sm font-medium text-gray-700 rounded-sm text-md hover:text-gray-900 hover:bg-gray-200"> className="text-md flex items-center rounded-sm px-2 py-1 text-sm font-medium text-gray-700 hover:bg-gray-200 hover:text-gray-900">
<props.Icon className="w-4 h-4 ltr:mr-2 rtl:ml-2 text-neutral-500" /> <props.Icon className="h-4 w-4 text-neutral-500 ltr:mr-2 rtl:ml-2" />
{props.children} {props.children}
</button> </button>
</div> </div>

View file

@ -14,19 +14,19 @@ export default function ModalContainer(props: Props) {
aria-labelledby="modal-title" aria-labelledby="modal-title"
role="dialog" role="dialog"
aria-modal="true"> aria-modal="true">
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"> <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
className="fixed inset-0 z-0 transition-opacity bg-gray-500 bg-opacity-75" className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden="true"></div> aria-hidden="true"></div>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203; &#8203;
</span> </span>
<div <div
className={classNames( className={classNames(
"inline-block min-w-96 px-4 pt-5 pb-4 text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:p-6", "inline-block min-w-96 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",
{ {
"sm:max-w-lg sm:w-full ": !props.wide, "sm:w-full sm:max-w-lg ": !props.wide,
"sm:max-w-4xl sm:w-4xl": props.wide, "sm:w-4xl sm:max-w-4xl": props.wide,
"overflow-scroll": props.scroll, "overflow-scroll": props.scroll,
"!p-0": props.noPadding, "!p-0": props.noPadding,
} }

View file

@ -5,17 +5,17 @@ import { useLocale } from "@lib/hooks/useLocale";
const PoweredByCal = () => { const PoweredByCal = () => {
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<div className="text-xs text-center sm:text-right p-1"> <div className="p-1 text-center text-xs sm:text-right">
<Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}> <Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}>
<a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100"> <a target="_blank" className="text-gray-500 opacity-50 hover:opacity-100 dark:text-white">
{t("powered_by")}{" "} {t("powered_by")}{" "}
<img <img
className="dark:hidden w-auto inline h-[10px] relative -mt-px" className="relative -mt-px inline h-[10px] w-auto dark:hidden"
src="https://cal.com/logo.svg" src="https://cal.com/logo.svg"
alt="Cal.com Logo" alt="Cal.com Logo"
/> />
<img <img
className="hidden dark:inline w-auto h-[10px] relativ -mt-px" className="relativ -mt-px hidden h-[10px] w-auto dark:inline"
src="https://cal.com/logo-white.svg" src="https://cal.com/logo-white.svg"
alt="Cal.com Logo" alt="Cal.com Logo"
/> />

View file

@ -66,11 +66,11 @@ export const Scheduler = ({ availability, setAvailability, timeZone, setTimeZone
}; };
const OpeningHours = ({ idx, item }: { idx: number; item: Availability }) => ( const OpeningHours = ({ idx, item }: { idx: number; item: Availability }) => (
<li className="flex justify-between py-2 border-b"> <li className="flex justify-between border-b py-2">
<div className="flex flex-col space-y-4 lg:inline-flex"> <div className="flex flex-col space-y-4 lg:inline-flex">
<WeekdaySelect defaultValue={item.days} onSelect={(selected: number[]) => (item.days = selected)} /> <WeekdaySelect defaultValue={item.days} onSelect={(selected: number[]) => (item.days = selected)} />
<button <button
className="px-3 py-2 text-sm rounded-sm bg-neutral-100" className="rounded-sm bg-neutral-100 px-3 py-2 text-sm"
type="button" type="button"
onClick={() => setEditSchedule(idx)}> onClick={() => setEditSchedule(idx)}>
{item.startTime.toLocaleTimeString(i18n.language, { {item.startTime.toLocaleTimeString(i18n.language, {
@ -89,8 +89,8 @@ export const Scheduler = ({ availability, setAvailability, timeZone, setTimeZone
<button <button
type="button" type="button"
onClick={() => removeScheduleAt(idx)} onClick={() => removeScheduleAt(idx)}
className="px-2 py-1 ml-1 bg-transparent btn-sm"> className="btn-sm ml-1 bg-transparent px-2 py-1">
<TrashIcon className="inline w-5 h-5 -mt-1 text-gray-400" /> <TrashIcon className="-mt-1 inline h-5 w-5 text-gray-400" />
</button> </button>
</li> </li>
); );
@ -108,7 +108,7 @@ export const Scheduler = ({ availability, setAvailability, timeZone, setTimeZone
id="timeZone" id="timeZone"
value={timeZone} value={timeZone}
onChange={(tz: ITimezoneOption) => setTimeZone(tz.value)} onChange={(tz: ITimezoneOption) => setTimeZone(tz.value)}
className="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
/> />
</div> </div>
</div> </div>

View file

@ -13,12 +13,12 @@ export default function SettingInputContainer({
<div className="space-y-3"> <div className="space-y-3">
<div className="block sm:flex"> <div className="block sm:flex">
<div className="mb-4 min-w-48 sm:mb-0"> <div className="mb-4 min-w-48 sm:mb-0">
<label htmlFor={htmlFor} className="flex mt-1 text-sm font-medium text-neutral-700"> <label htmlFor={htmlFor} className="mt-1 flex text-sm font-medium text-neutral-700">
<Icon className="w-4 h-4 ltr:mr-2 rtl:ml-2 mt-0.5 text-neutral-500" /> <Icon className="mt-0.5 h-4 w-4 text-neutral-500 ltr:mr-2 rtl:ml-2" />
{label} {label}
</label> </label>
</div> </div>
<div className="flex-grow w-full">{Input}</div> <div className="w-full flex-grow">{Input}</div>
</div> </div>
</div> </div>
); );

View file

@ -20,16 +20,16 @@ export default function Switch(props: SwitchProps) {
}; };
const id = useId(); const id = useId();
return ( return (
<div className="flex items-center h-[20px]"> <div className="flex h-[20px] items-center">
<PrimitiveSwitch.Root <PrimitiveSwitch.Root
className={classNames(checked ? "bg-gray-900" : "bg-gray-400", "rounded-sm w-[36px] p-0.5 h-[20px]")} className={classNames(checked ? "bg-gray-900" : "bg-gray-400", "h-[20px] w-[36px] rounded-sm p-0.5")}
checked={checked} checked={checked}
onCheckedChange={onPrimitiveCheckedChange} onCheckedChange={onPrimitiveCheckedChange}
{...primitiveProps}> {...primitiveProps}>
<PrimitiveSwitch.Thumb <PrimitiveSwitch.Thumb
id={id} id={id}
className={classNames( className={classNames(
"bg-white w-[16px] h-[16px] block transition-transform", "block h-[16px] w-[16px] bg-white transition-transform",
checked ? "translate-x-[16px]" : "translate-x-0" checked ? "translate-x-[16px]" : "translate-x-0"
)} )}
/> />
@ -37,7 +37,7 @@ export default function Switch(props: SwitchProps) {
{label && ( {label && (
<Label.Root <Label.Root
htmlFor={id} htmlFor={id}
className="text-sm font-medium align-text-top cursor-pointer text-neutral-700 ltr:ml-3 rtl:mr-3"> className="cursor-pointer align-text-top text-sm font-medium text-neutral-700 ltr:ml-3 rtl:mr-3">
{label} {label}
</Label.Root> </Label.Root>
)} )}

View file

@ -24,7 +24,7 @@ const TableActions: FC<Props> = ({ actions }) => {
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<> <>
<div className="rtl:space-x-reverse space-x-2 hidden lg:block"> <div className="hidden space-x-2 rtl:space-x-reverse lg:block">
{actions.map((action) => ( {actions.map((action) => (
<Button <Button
key={action.id} key={action.id}
@ -38,11 +38,11 @@ const TableActions: FC<Props> = ({ actions }) => {
</Button> </Button>
))} ))}
</div> </div>
<Menu as="div" className="inline-block lg:hidden text-left "> <Menu as="div" className="inline-block text-left lg:hidden ">
{({ open }) => ( {({ open }) => (
<> <>
<div> <div>
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200"> <Menu.Button className="mt-1 border border-transparent p-2 text-neutral-400 hover:border-gray-200">
<span className="sr-only">{t("open_options")}</span> <span className="sr-only">{t("open_options")}</span>
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" /> <DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button> </Menu.Button>
@ -59,7 +59,7 @@ const TableActions: FC<Props> = ({ actions }) => {
leaveTo="transform opacity-0 scale-95"> leaveTo="transform opacity-0 scale-95">
<Menu.Items <Menu.Items
static static
className="origin-top-right absolute right-0 mt-2 w-56 rounded-sm shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none divide-y divide-neutral-100"> className="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-neutral-100 rounded-sm bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1"> <div className="py-1">
{actions.map((action) => { {actions.map((action) => {
const Element = typeof action.onClick === "function" ? "span" : "a"; const Element = typeof action.onClick === "function" ? "span" : "a";
@ -74,7 +74,7 @@ const TableActions: FC<Props> = ({ actions }) => {
"group flex items-center px-4 py-2 text-sm font-medium" "group flex items-center px-4 py-2 text-sm font-medium"
)}> )}>
<action.icon <action.icon
className="ltr:mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500" className="h-5 w-5 text-neutral-400 group-hover:text-neutral-500 ltr:mr-3"
aria-hidden="true" aria-hidden="true"
/> />
{action.label} {action.label}

View file

@ -33,8 +33,8 @@ export const WeekdaySelect = (props: WeekdaySelectProps) => {
toggleDay(idx); toggleDay(idx);
}} }}
className={` className={`
w-10 h-10 h-10 w-10
bg-brand text-brandcontrast focus:outline-none px-3 py-1 rounded rounded bg-brand px-3 py-1 text-brandcontrast focus:outline-none
${activeDays[idx + 1] ? "rounded-r-none" : ""} ${activeDays[idx + 1] ? "rounded-r-none" : ""}
${activeDays[idx - 1] ? "rounded-l-none" : ""} ${activeDays[idx - 1] ? "rounded-l-none" : ""}
${idx === 0 ? "rounded-l" : ""} ${idx === 0 ? "rounded-l" : ""}
@ -50,7 +50,7 @@ export const WeekdaySelect = (props: WeekdaySelectProps) => {
toggleDay(idx); toggleDay(idx);
}} }}
style={{ marginTop: "1px", marginBottom: "1px" }} style={{ marginTop: "1px", marginBottom: "1px" }}
className={`w-10 h-10 bg-gray-50 focus:outline-none px-3 py-1 rounded-none ${ className={`h-10 w-10 rounded-none bg-gray-50 px-3 py-1 focus:outline-none ${
idx === 0 ? "rounded-l" : "border-l-0" idx === 0 ? "rounded-l" : "border-l-0"
} ${idx === days.length - 1 ? "rounded-r" : ""}`}> } ${idx === days.length - 1 ? "rounded-r" : ""}`}>
{day} {day}

View file

@ -69,13 +69,13 @@ const ColorPicker = (props: ColorPickerProps) => {
const close = useCallback(() => toggle(false), []); const close = useCallback(() => toggle(false), []);
useOnClickOutside(popover, close); useOnClickOutside(popover, close);
return ( return (
<div className="relative flex items-center justify-center mt-1"> <div className="relative mt-1 flex items-center justify-center">
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} /> <Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
{isOpen && ( {isOpen && (
<div className="popover" ref={popover}> <div className="popover" ref={popover}>
<HexColorPicker <HexColorPicker
className="!w-32 !h-32 !absolute !top-10 !left-0 !z-10" className="!absolute !top-10 !left-0 !z-10 !h-32 !w-32"
color={color} color={color}
onChange={(val) => { onChange={(val) => {
setColor(val); setColor(val);
@ -85,7 +85,7 @@ const ColorPicker = (props: ColorPickerProps) => {
</div> </div>
)} )}
<HexColorInput <HexColorInput
className="block w-full px-3 py-2 ml-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm" className="ml-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-neutral-800 sm:text-sm"
color={color} color={color}
onChange={(val) => { onChange={(val) => {
setColor(val); setColor(val);

View file

@ -7,7 +7,7 @@ type Props = InputHTMLAttributes<HTMLInputElement> & {
const CheckboxField = forwardRef<HTMLInputElement, Props>(({ label, description, ...rest }, ref) => { const CheckboxField = forwardRef<HTMLInputElement, Props>(({ label, description, ...rest }, ref) => {
return ( return (
<div className="items-center block sm:flex"> <div className="block items-center sm:flex">
<div className="mb-4 min-w-48 sm:mb-0"> <div className="mb-4 min-w-48 sm:mb-0">
<label htmlFor={rest.id} className="flex text-sm font-medium text-neutral-700"> <label htmlFor={rest.id} className="flex text-sm font-medium text-neutral-700">
{label} {label}
@ -15,15 +15,15 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(({ label, description,
</div> </div>
<div className="w-full"> <div className="w-full">
<div className="relative flex items-start"> <div className="relative flex items-start">
<div className="flex items-center h-5"> <div className="flex h-5 items-center">
<input <input
{...rest} {...rest}
ref={ref} ref={ref}
type="checkbox" type="checkbox"
className="w-4 h-4 border-gray-300 rounded focus:ring-primary-500 text-primary-600" className="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500"
/> />
</div> </div>
<div className="ltr:ml-3 rtl:mr-3 text-sm"> <div className="text-sm ltr:ml-3 rtl:mr-3">
<p className="text-neutral-900">{description}</p> <p className="text-neutral-900">{description}</p>
</div> </div>
</div> </div>

View file

@ -1,15 +1,15 @@
import { CheckIcon, XIcon } from "@heroicons/react/outline";
import React, { ForwardedRef, useEffect, useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import Avatar from "@components/ui/Avatar"; import Avatar from "@components/ui/Avatar";
import Select from "@components/ui/form/Select"; import Select from "@components/ui/form/Select";
import { CheckIcon, XIcon } from "@heroicons/react/outline";
import { useLocale } from "@lib/hooks/useLocale";
import React, { useEffect, useState } from "react";
import { MultiValue } from "react-select";
type CheckedSelectValue = { type CheckedSelectValue = {
avatar: string; avatar: string;
label: string; label: string;
value: string; value: string;
disabled?: boolean;
}[]; }[];
export type CheckedSelectProps = { export type CheckedSelectProps = {
@ -21,7 +21,7 @@ export type CheckedSelectProps = {
disabled: boolean; disabled: boolean;
}; };
export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: ForwardedRef<unknown>) => { export const CheckedSelect = (props: CheckedSelectProps) => {
const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []); const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []);
const { t } = useLocale(); const { t } = useLocale();
@ -29,18 +29,6 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
props.onChange(selectedOptions); props.onChange(selectedOptions);
}, [selectedOptions]); }, [selectedOptions]);
const formatOptionLabel = ({ label, avatar, disabled }) => (
<div className="flex">
<Avatar className="h-6 w-6 rounded-full mr-3" displayName={label} imageSrc={avatar} />
{label}
{disabled && (
<div className="flex-grow">
<CheckIcon className="text-neutral-500 w-6 h-6 float-right" />
</div>
)}
</div>
);
const options = props.options.map((option) => ({ const options = props.options.map((option) => ({
...option, ...option,
disabled: !!selectedOptions.find((selectedOption) => selectedOption.value === option.value), disabled: !!selectedOptions.find((selectedOption) => selectedOption.value === option.value),
@ -49,7 +37,7 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
const removeOption = (value: string) => const removeOption = (value: string) =>
setSelectedOptions(selectedOptions.filter((option) => option.value !== value)); setSelectedOptions(selectedOptions.filter((option) => option.value !== value));
const changeHandler = (selections) => const changeHandler = (selections: MultiValue<CheckedSelectValue[number]>) =>
selections.forEach((selected) => { selections.forEach((selected) => {
if (selectedOptions.find((option) => option.value === selected.value)) { if (selectedOptions.find((option) => option.value === selected.value)) {
removeOption(selected.value); removeOption(selected.value);
@ -60,8 +48,7 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
return ( return (
<> <>
<Select <Select<CheckedSelectValue[number], true>
ref={ref}
styles={{ styles={{
option: (styles, { isDisabled }) => ({ option: (styles, { isDisabled }) => ({
...styles, ...styles,
@ -71,29 +58,39 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
name={props.name} name={props.name}
placeholder={props.placeholder || t("select")} placeholder={props.placeholder || t("select")}
isSearchable={false} isSearchable={false}
formatOptionLabel={formatOptionLabel} formatOptionLabel={({ label, avatar, disabled }) => (
<div className="flex">
<Avatar className="mr-3 h-6 w-6 rounded-full" alt={label} imageSrc={avatar} />
{label}
{disabled && (
<div className="flex-grow">
<CheckIcon className="float-right h-6 w-6 text-neutral-500" />
</div>
)}
</div>
)}
options={options} options={options}
isMulti isMulti
value={props.placeholder || t("select")} // value={props.placeholder || t("select")}
onChange={changeHandler} onChange={changeHandler}
/> />
{selectedOptions.map((option) => ( {selectedOptions.map((option) => (
<div key={option.value} className="border border-1 p-2 font-medium"> <div key={option.value} className="border-1 border p-2 font-medium">
<Avatar <Avatar
className="w-6 h-6 rounded-full inline ltr:mr-2 rtl:ml-2" className="inline h-6 w-6 rounded-full ltr:mr-2 rtl:ml-2"
imageSrc={option.avatar} imageSrc={option.avatar}
displayName={option.label} alt={option.label}
/> />
{option.label} {option.label}
<XIcon <XIcon
onClick={() => changeHandler([option])} onClick={() => changeHandler([option])}
className="cursor-pointer h-5 w-5 mt-0.5 text-neutral-500 float-right" className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500"
/> />
</div> </div>
))} ))}
</> </>
); );
}); };
CheckedSelect.displayName = "CheckedSelect"; CheckedSelect.displayName = "CheckedSelect";

View file

@ -16,11 +16,11 @@ export const DatePicker = ({ date, onDatesChange, className }: Props) => {
return ( return (
<PrimitiveDatePicker <PrimitiveDatePicker
className={classNames( className={classNames(
"p-1 pl-2 border border-gray-300 rounded-sm shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm", "rounded-sm border border-gray-300 p-1 pl-2 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm",
className className
)} )}
clearIcon={null} clearIcon={null}
calendarIcon={<CalendarIcon className="w-5 h-5 text-gray-500" />} calendarIcon={<CalendarIcon className="h-5 w-5 text-gray-500" />}
value={date} value={date}
onChange={onDatesChange} onChange={onDatesChange}
/> />

View file

@ -14,13 +14,13 @@ type Props = {
export const DateRangePicker = ({ startDate, endDate, onDatesChange }: Props) => { export const DateRangePicker = ({ startDate, endDate, onDatesChange }: Props) => {
return ( return (
<PrimitiveDateRangePicker <PrimitiveDateRangePicker
className="border-gray-300 rounded-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm" className="rounded-sm border-gray-300 focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
clearIcon={null} clearIcon={null}
calendarIcon={<CalendarIcon className="h-5 w-5 text-gray-500" />} calendarIcon={<CalendarIcon className="h-5 w-5 text-gray-500" />}
rangeDivider={<ArrowRightIcon className="h-4 w-4 text-gray-400 ltr:mr-2 rtl:ml-2" />} rangeDivider={<ArrowRightIcon className="h-4 w-4 text-gray-400 ltr:mr-2 rtl:ml-2" />}
value={[startDate, endDate]} value={[startDate, endDate]}
onChange={([startDate, endDate]) => { onChange={([startDate, endDate]) => {
onDatesChange({ startDate, endDate }); if (typeof onDatesChange === "function") onDatesChange({ startDate, endDate });
}} }}
/> />
); );

View file

@ -10,7 +10,7 @@ const MinutesField = forwardRef<HTMLInputElement, Props>(({ label, ...rest }, re
<div className="block sm:flex"> <div className="block sm:flex">
{!!label && ( {!!label && (
<div className="mb-4 min-w-48 sm:mb-0"> <div className="mb-4 min-w-48 sm:mb-0">
<label htmlFor={rest.id} className="flex items-center h-full text-sm font-medium text-neutral-700"> <label htmlFor={rest.id} className="flex h-full items-center text-sm font-medium text-neutral-700">
{label} {label}
</label> </label>
</div> </div>
@ -22,11 +22,11 @@ const MinutesField = forwardRef<HTMLInputElement, Props>(({ label, ...rest }, re
ref={ref} ref={ref}
type="number" type="number"
className={classNames( className={classNames(
"block w-full pl-2 pr-12 border-gray-300 rounded-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm", "block w-full rounded-sm border-gray-300 pl-2 pr-12 focus:border-primary-500 focus:ring-primary-500 sm:text-sm",
rest.className rest.className
)} )}
/> />
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none"> <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span className="text-gray-500 sm:text-sm" id="duration"> <span className="text-gray-500 sm:text-sm" id="duration">
mins mins
</span> </span>

View file

@ -9,7 +9,7 @@ export const PhoneInput = (props: Optional<PhoneInputProps, "onChange">) => (
<BasePhoneInput <BasePhoneInput
{...props} {...props}
className={classNames( className={classNames(
"shadow-sm rounded-sm block w-full py-px px-3 border border-1 border-gray-300 ring-black focus-within:ring-1 focus-within:border-brand dark:border-black dark:text-white dark:bg-black", "border-1 block w-full rounded-sm border border-gray-300 py-px px-3 shadow-sm ring-black focus-within:border-brand focus-within:ring-1 dark:border-black dark:bg-black dark:text-white",
props.className props.className
)} )}
onChange={() => { onChange={() => {

View file

@ -139,21 +139,21 @@ const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => {
}; };
return ( return (
<fieldset className="flex flex-col space-y-2 sm:space-y-0 sm:flex-row justify-between py-5 min-h-[86px]"> <fieldset className="flex min-h-[86px] flex-col justify-between space-y-2 py-5 sm:flex-row sm:space-y-0">
<div className="w-1/3"> <div className="w-1/3">
<label className="flex items-center space-x-2 rtl:space-x-reverse"> <label className="flex items-center space-x-2 rtl:space-x-reverse">
<input <input
type="checkbox" type="checkbox"
checked={fields.length > 0} checked={fields.length > 0}
onChange={(e) => (e.target.checked ? replace([defaultDayRange]) : replace([]))} onChange={(e) => (e.target.checked ? replace([defaultDayRange]) : replace([]))}
className="inline-block border-gray-300 rounded-sm focus:ring-neutral-500 text-neutral-900" className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500"
/> />
<span className="inline-block text-sm capitalize">{weekday}</span> <span className="inline-block text-sm capitalize">{weekday}</span>
</label> </label>
</div> </div>
<div className="flex-grow"> <div className="flex-grow">
{fields.map((field, index) => ( {fields.map((field, index) => (
<div key={field.id} className="flex justify-between mb-1"> <div key={field.id} className="mb-1 flex justify-between">
<div className="flex items-center space-x-2 rtl:space-x-reverse"> <div className="flex items-center space-x-2 rtl:space-x-reverse">
<TimeRangeField name={`${name}.${day}.${index}`} /> <TimeRangeField name={`${name}.${day}.${index}`} />
</div> </div>

View file

@ -11,7 +11,7 @@ const RadioArea = (props: RadioAreaProps) => {
return ( return (
<label <label
className={classNames( className={classNames(
"block border border-1 p-4 focus:outline-none focus:ring focus:ring-neutral-500", "border-1 block border p-4 focus:outline-none focus:ring focus:ring-neutral-500",
props.checked && "border-brand", props.checked && "border-brand",
props.className props.className
)}> )}>

View file

@ -7,13 +7,15 @@ import { useLocale } from "@lib/hooks/useLocale";
import { RadioArea, RadioAreaGroup } from "@components/ui/form/radio-area/RadioAreaGroup"; import { RadioArea, RadioAreaGroup } from "@components/ui/form/radio-area/RadioAreaGroup";
type OptionProps = React.OptionHTMLAttributes<HTMLOptionElement> & { interface OptionProps
extends Pick<React.OptionHTMLAttributes<HTMLOptionElement>, "value" | "label" | "className"> {
description?: string; description?: string;
}; }
type RadioAreaSelectProps = React.SelectHTMLAttributes<HTMLSelectElement> & { interface RadioAreaSelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "onChange"> {
options: OptionProps[]; // allow options to be passed programmatically, like options={} options: OptionProps[]; // allow options to be passed programmatically, like options={}
}; onChange?: (value: string) => void;
}
export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) { export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
const { t } = useLocale(); const { t } = useLocale();
@ -23,7 +25,7 @@ export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
placeholder = t("select"), placeholder = t("select"),
} = props; } = props;
const getLabel = (value: string | ReadonlyArray<string> | number) => const getLabel = (value: string | ReadonlyArray<string> | number | undefined) =>
options.find((option: OptionProps) => option.value === value)?.label; options.find((option: OptionProps) => option.value === value)?.label;
return ( return (
@ -32,8 +34,8 @@ export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
type="button" type="button"
disabled={disabled} disabled={disabled}
className={classNames( className={classNames(
"mb-1 cursor-pointer focus:ring-primary-500 text-left border border-1 bg-white p-2 shadow-sm block w-full sm:text-sm border-gray-300 rounded-sm", "border-1 mb-1 block w-full cursor-pointer rounded-sm border border-gray-300 bg-white p-2 text-left shadow-sm focus:ring-primary-500 sm:text-sm",
disabled && "focus:ring-0 cursor-default bg-gray-200 " disabled && "cursor-default bg-gray-200 focus:ring-0 "
)}> )}>
{getLabel(props.value) ?? placeholder} {getLabel(props.value) ?? placeholder}
<ChevronDownIcon className="float-right h-5 w-5 text-neutral-500" /> <ChevronDownIcon className="float-right h-5 w-5 text-neutral-500" />
@ -41,8 +43,11 @@ export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
<CollapsibleContent> <CollapsibleContent>
<RadioAreaGroup className="space-y-2 text-sm" name={props.name} onChange={props.onChange}> <RadioAreaGroup className="space-y-2 text-sm" name={props.name} onChange={props.onChange}>
{options.map((option) => ( {options.map((option) => (
<RadioArea {...option} key={option.value} defaultChecked={props.value === option.value}> <RadioArea
<strong className="block mb-1">{option.label}</strong> {...option}
key={Array.isArray(option.value) ? option.value.join(",") : `${option.value}`}
defaultChecked={props.value === option.value}>
<strong className="mb-1 block">{option.label}</strong>
<p>{option.description}</p> <p>{option.description}</p>
</RadioArea> </RadioArea>
))} ))}

View file

@ -5,31 +5,21 @@ import { useLocale } from "@lib/hooks/useLocale";
import Button from "@components/ui/Button"; import Button from "@components/ui/Button";
export default function SetTimesModal(props) { interface SetTimesModalProps {
startTime: number;
endTime: number;
onChange: (times: { startTime: number; endTime: number }) => void;
onExit: (...p: unknown[]) => void;
}
export default function SetTimesModal(props: SetTimesModalProps) {
const { t } = useLocale(); const { t } = useLocale();
const [startHours, startMinutes] = [Math.floor(props.startTime / 60), props.startTime % 60]; const [startHours, startMinutes] = [Math.floor(props.startTime / 60), props.startTime % 60];
const [endHours, endMinutes] = [Math.floor(props.endTime / 60), props.endTime % 60]; const [endHours, endMinutes] = [Math.floor(props.endTime / 60), props.endTime % 60];
const startHoursRef = useRef<HTMLInputElement>(null!);
const startHoursRef = useRef<HTMLInputElement>(); const startMinsRef = useRef<HTMLInputElement>(null!);
const startMinsRef = useRef<HTMLInputElement>(); const endHoursRef = useRef<HTMLInputElement>(null!);
const endHoursRef = useRef<HTMLInputElement>(); const endMinsRef = useRef<HTMLInputElement>(null!);
const endMinsRef = useRef<HTMLInputElement>();
function updateStartEndTimesHandler(event) {
event.preventDefault();
const enteredStartHours = parseInt(startHoursRef.current.value);
const enteredStartMins = parseInt(startMinsRef.current.value);
const enteredEndHours = parseInt(endHoursRef.current.value);
const enteredEndMins = parseInt(endMinsRef.current.value);
props.onChange({
startTime: enteredStartHours * 60 + enteredStartMins,
endTime: enteredEndHours * 60 + enteredEndMins,
});
props.onExit(0);
}
return ( return (
<div <div
@ -37,19 +27,19 @@ export default function SetTimesModal(props) {
aria-labelledby="modal-title" aria-labelledby="modal-title"
role="dialog" role="dialog"
aria-modal="true"> aria-modal="true">
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"> <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
className="fixed inset-0 z-0 transition-opacity bg-gray-500 bg-opacity-75" className="fixed inset-0 z-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden="true"></div> aria-hidden="true"></div>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203; &#8203;
</span> </span>
<div className="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6"> <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="mb-4 sm:flex sm:items-start">
<div className="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto bg-blue-100 rounded-full sm:mx-0 sm:h-10 sm:w-10"> <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="w-6 h-6 text-black" /> <ClockIcon className="h-6 w-6 text-black" />
</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">
@ -60,7 +50,7 @@ export default function SetTimesModal(props) {
</div> </div>
</div> </div>
</div> </div>
<div className="flex mb-4"> <div className="mb-4 flex">
<label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label> <label className="block w-1/4 pt-2 text-sm font-medium text-gray-700">{t("start_time")}</label>
<div> <div>
<label htmlFor="startHours" className="sr-only"> <label htmlFor="startHours" className="sr-only">
@ -71,15 +61,15 @@ export default function SetTimesModal(props) {
type="number" type="number"
min="0" min="0"
max="23" max="23"
maxLength="2" maxLength={2}
name="hours" name="hours"
id="startHours" id="startHours"
className="block w-full border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder="9" placeholder="9"
defaultValue={startHours} defaultValue={startHours}
/> />
</div> </div>
<span className="pt-1 mx-2">:</span> <span className="mx-2 pt-1">:</span>
<div> <div>
<label htmlFor="startMinutes" className="sr-only"> <label htmlFor="startMinutes" className="sr-only">
{t("minutes")} {t("minutes")}
@ -90,10 +80,10 @@ export default function SetTimesModal(props) {
min="0" min="0"
max="59" max="59"
step="15" step="15"
maxLength="2" maxLength={2}
name="minutes" name="minutes"
id="startMinutes" id="startMinutes"
className="block w-full border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder="30" placeholder="30"
defaultValue={startMinutes} defaultValue={startMinutes}
/> />
@ -110,15 +100,15 @@ export default function SetTimesModal(props) {
type="number" type="number"
min="0" min="0"
max="24" max="24"
maxLength="2" maxLength={2}
name="hours" name="hours"
id="endHours" id="endHours"
className="block w-full border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder="17" placeholder="17"
defaultValue={endHours} defaultValue={endHours}
/> />
</div> </div>
<span className="pt-1 mx-2">:</span> <span className="mx-2 pt-1">:</span>
<div> <div>
<label htmlFor="endMinutes" className="sr-only"> <label htmlFor="endMinutes" className="sr-only">
{t("minutes")} {t("minutes")}
@ -128,18 +118,34 @@ export default function SetTimesModal(props) {
type="number" type="number"
min="0" min="0"
max="59" max="59"
maxLength="2" maxLength={2}
step="15" step="15"
name="minutes" name="minutes"
id="endMinutes" id="endMinutes"
className="block w-full border-gray-300 rounded-md shadow-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full rounded-md border-gray-300 shadow-sm focus:border-brand focus:ring-black sm:text-sm"
placeholder="30" placeholder="30"
defaultValue={endMinutes} defaultValue={endMinutes}
/> />
</div> </div>
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button onClick={updateStartEndTimesHandler} type="submit"> <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);
props.onChange({
startTime: enteredStartHours * 60 + enteredStartMins,
endTime: enteredEndHours * 60 + enteredEndMins,
});
props.onExit(0);
}}
type="submit">
{t("save")} {t("save")}
</Button> </Button>
<Button onClick={props.onExit} type="button" color="secondary" className="ltr:mr-2"> <Button onClick={props.onExit} type="button" color="secondary" className="ltr:mr-2">

View file

@ -3,7 +3,7 @@ import React from "react";
function UserCalendarIllustration() { function UserCalendarIllustration() {
return ( return (
<svg <svg
className="w-1/2 md:w-32 mx-auto block mb-4" className="mx-auto mb-4 block w-1/2 md:w-32"
viewBox="0 0 132 132" viewBox="0 0 132 132"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">

View file

@ -24,40 +24,40 @@ export default function LicenseBanner() {
} }
return ( return (
<div className="fixed inset-x-0 bottom-0 left-0 pb-2 md:left-56 sm:pb-5"> <div className="fixed inset-x-0 bottom-0 left-0 pb-2 sm:pb-5 md:left-56">
<div className="px-2 mx-auto max-w-7xl sm:px-8"> <div className="mx-auto max-w-7xl px-2 sm:px-8">
<div className="p-2 bg-green-600 rounded-sm shadow-lg sm:p-3"> <div className="rounded-sm bg-green-600 p-2 shadow-lg sm:p-3">
<div className="flex flex-wrap items-center justify-between"> <div className="flex flex-wrap items-center justify-between">
<div className="flex items-center flex-1 w-0"> <div className="flex w-0 flex-1 items-center">
<span className="flex p-2 bg-green-800 rounded-sm"> <span className="flex rounded-sm bg-green-800 p-2">
<BadgeCheckIcon className="w-6 h-6 text-white" aria-hidden="true" /> <BadgeCheckIcon className="h-6 w-6 text-white" aria-hidden="true" />
</span> </span>
<p className="ml-3 font-medium text-white truncate"> <p className="ml-3 truncate font-medium text-white">
<span className="inline"> <span className="inline">
<Trans i18nKey="accept_our_license" values={{ agree: "agree" }}> <Trans i18nKey="accept_our_license" values={{ agree: "agree" }}>
Accept our license by changing the .env variable Accept our license by changing the .env variable
<span className="px-1 bg-gray-50 bg-opacity-20">NEXT_PUBLIC_LICENSE_CONSENT</span> to <span className="bg-gray-50 bg-opacity-20 px-1">NEXT_PUBLIC_LICENSE_CONSENT</span> to
&apos;agree&apos;. &apos;agree&apos;.
</Trans> </Trans>
</span> </span>
</p> </p>
</div> </div>
<div className="flex-shrink-0 order-3 w-full mt-2 sm:order-2 sm:mt-0 sm:w-auto"> <div className="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<button className="flex items-center justify-center w-full px-4 py-2 text-sm font-medium text-green-600 bg-white border border-transparent rounded-sm shadow-sm hover:bg-green-50"> <button className="flex w-full items-center justify-center rounded-sm border border-transparent bg-white px-4 py-2 text-sm font-medium text-green-600 shadow-sm hover:bg-green-50">
{t("accept_license")} {t("accept_license")}
</button> </button>
</DialogTrigger> </DialogTrigger>
<DialogContent /> <DialogContent />
</Dialog> </Dialog>
</div> </div>
<div className="flex-shrink-0 order-2 sm:order-3 sm:ml-2"> <div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<button className="flex p-2 -mr-1 rounded-sm hover:bg-green-500 focus:outline-none focus:ring-2 focus:ring-white"> <button className="-mr-1 flex rounded-sm p-2 hover:bg-green-500 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">{t("dismiss")}</span> <span className="sr-only">{t("dismiss")}</span>
<XIcon className="w-6 h-6 text-white" aria-hidden="true" /> <XIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button> </button>
</DialogTrigger> </DialogTrigger>
<DialogContent /> <DialogContent />
@ -78,12 +78,12 @@ export default function LicenseBanner() {
cancelBtnText={t("cancel")}> cancelBtnText={t("cancel")}>
<Trans i18nKey="remove_banner_instructions" values={{ agree: "agree" }}> <Trans i18nKey="remove_banner_instructions" values={{ agree: "agree" }}>
To remove this banner, please open your .env file and change the To remove this banner, please open your .env file and change the
<span className="bg-green-400 text-green-500 bg-opacity-20 p-[2px]"> <span className="bg-green-400 bg-opacity-20 p-[2px] text-green-500">
NEXT_PUBLIC_LICENSE_CONSENT NEXT_PUBLIC_LICENSE_CONSENT
</span>{" "} </span>
variable to &apos;agreeapos;. variable to &apos;agreeapos;.
</Trans> </Trans>
<h2 className="mt-8 mb-2 text-black font-cal">{t("terms_summary")}:</h2> <h2 className="mt-8 mb-2 font-cal text-black">{t("terms_summary")}:</h2>
<ul className="ml-5 list-disc"> <ul className="ml-5 list-disc">
<li>{t("codebase_has_to_stay_opensource")}</li> <li>{t("codebase_has_to_stay_opensource")}</li>
<li>{t("cannot_repackage_codebase")}</li> <li>{t("cannot_repackage_codebase")}</li>

View file

@ -19,13 +19,13 @@ const TrialBanner = () => {
return ( return (
<div <div
className="hidden p-4 m-4 text-sm font-medium text-center text-gray-600 bg-yellow-200 rounded-md sm:block" className="m-4 hidden rounded-md bg-yellow-200 p-4 text-center text-sm font-medium text-gray-600 sm:block"
data-testid="trial-banner"> data-testid="trial-banner">
<div className="mb-2 text-left">{t("trial_days_left", { days: trialDaysLeft })}</div> <div className="mb-2 text-left">{t("trial_days_left", { days: trialDaysLeft })}</div>
<Button <Button
href="/api/upgrade" href="/api/upgrade"
color="minimal" color="minimal"
className="justify-center w-full border-2 border-gray-600 hover:bg-yellow-100"> className="w-full justify-center border-2 border-gray-600 hover:bg-yellow-100">
{t("upgrade_now")} {t("upgrade_now")}
</Button> </Button>
</div> </div>

View file

@ -94,7 +94,7 @@ export default function SAMLConfiguration({
<> <>
<hr className="mt-8" /> <hr className="mt-8" />
<div className="mt-6"> <div className="mt-6">
<h2 className="text-lg font-medium leading-6 text-gray-900 font-cal"> <h2 className="font-cal text-lg font-medium leading-6 text-gray-900">
{t("saml_configuration")} {t("saml_configuration")}
<Badge className="ml-2 text-xs" variant={samlConfig ? "success" : "gray"}> <Badge className="ml-2 text-xs" variant={samlConfig ? "success" : "gray"}>
{samlConfig ? t("enabled") : t("disabled")} {samlConfig ? t("enabled") : t("disabled")}
@ -110,7 +110,7 @@ export default function SAMLConfiguration({
</div> </div>
{samlConfig ? ( {samlConfig ? (
<div className="flex mt-2"> <div className="mt-2 flex">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button

View file

@ -106,7 +106,7 @@ export default function PaymentComponent(props: Props) {
return ( return (
<form id="payment-form" className="mt-4" onSubmit={handleSubmit}> <form id="payment-form" className="mt-4" onSubmit={handleSubmit}>
<CardElement id="card-element" options={CARD_OPTIONS} onChange={handleChange} /> <CardElement id="card-element" options={CARD_OPTIONS} onChange={handleChange} />
<div className="flex justify-center mt-2"> <div className="mt-2 flex justify-center">
<Button <Button
type="submit" type="submit"
disabled={["processing", "error"].includes(state.status)} disabled={["processing", "error"].includes(state.status)}

View file

@ -40,25 +40,25 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
</title> </title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<main className="max-w-3xl py-24 mx-auto"> <main className="mx-auto max-w-3xl py-24">
<div className="fixed inset-0 z-50 overflow-y-auto"> <div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"> <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 my-4 transition-opacity sm:my-0" aria-hidden="true"> <div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true"> <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203; &#8203;
</span> </span>
<div <div
className="inline-block px-8 pt-5 pb-4 overflow-hidden text-left align-bottom transition-all transform bg-white border rounded-sm dark:bg-gray-800 border-neutral-200 dark:border-neutral-700 sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:py-6" 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"
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-labelledby="modal-headline"> aria-labelledby="modal-headline">
<div> <div>
<div className="flex items-center justify-center w-12 h-12 mx-auto bg-green-100 rounded-full"> <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<CreditCardIcon className="w-8 h-8 text-green-600" /> <CreditCardIcon className="h-8 w-8 text-green-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<h3 <h3
className="text-2xl font-semibold leading-6 dark:text-white text-neutral-900" className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
id="modal-headline"> id="modal-headline">
{t("payment")} {t("payment")}
</h3> </h3>
@ -67,7 +67,7 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
{t("pay_later_instructions")} {t("pay_later_instructions")}
</p> </p>
</div> </div>
<div className="grid grid-cols-3 py-4 mt-4 text-left text-gray-700 border-t border-b dark:text-gray-300 dark:border-gray-900"> <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="font-medium">{t("what")}</div>
<div className="col-span-2 mb-6">{eventName}</div> <div className="col-span-2 mb-6">{eventName}</div>
<div className="font-medium">{t("when")}</div> <div className="font-medium">{t("when")}</div>
@ -117,7 +117,7 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
)} )}
</div> </div>
{!props.profile.hideBranding && ( {!props.profile.hideBranding && (
<div className="pt-4 mt-4 text-xs text-center text-gray-400 border-t dark:border-gray-900 dark:text-white"> <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> <a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
</div> </div>
)} )}

View file

@ -32,17 +32,17 @@ export default function TeamAvailabilityModal(props: Props) {
}, [utils, selectedTimeZone, selectedDate]); }, [utils, selectedTimeZone, selectedDate]);
return ( return (
<div className="flex flex-row max-h-[500px] min-h-[500px] rtl:space-x-reverse space-x-8"> <div className="flex max-h-[500px] min-h-[500px] flex-row space-x-8 rtl:space-x-reverse">
<div className="w-64 p-5 pr-0 space-y-5 min-w-64"> <div className="w-64 min-w-64 space-y-5 p-5 pr-0">
<div className="flex"> <div className="flex">
<Avatar <Avatar
imageSrc={getPlaceholderAvatar(props.member?.avatar, props.member?.name as string)} imageSrc={getPlaceholderAvatar(props.member?.avatar, props.member?.name as string)}
alt={props.member?.name || ""} alt={props.member?.name || ""}
className="rounded-full w-14 h-14" className="h-14 w-14 rounded-full"
/> />
<div className="inline-block pt-1 ml-3"> <div className="ml-3 inline-block pt-1">
<span className="text-lg font-bold text-neutral-700">{props.member?.name}</span> <span className="text-lg font-bold text-neutral-700">{props.member?.name}</span>
<span className="block -mt-1 text-sm text-gray-400">{props.member?.email}</span> <span className="-mt-1 block text-sm text-gray-400">{props.member?.email}</span>
</div> </div>
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
@ -61,7 +61,7 @@ export default function TeamAvailabilityModal(props: Props) {
value={selectedTimeZone} value={selectedTimeZone}
onChange={(timezone) => setSelectedTimeZone(timezone.value)} onChange={(timezone) => setSelectedTimeZone(timezone.value)}
classNamePrefix="react-select" classNamePrefix="react-select"
className="block w-full mt-1 border border-gray-300 rounded-sm shadow-sm react-select-container focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm" className="react-select-container mt-1 block w-full rounded-sm border border-gray-300 shadow-sm focus:border-neutral-800 focus:ring-neutral-800 sm:text-sm"
/> />
</div> </div>
<div> <div>
@ -74,7 +74,7 @@ export default function TeamAvailabilityModal(props: Props) {
]} ]}
isSearchable={false} isSearchable={false}
classNamePrefix="react-select" classNamePrefix="react-select"
className="flex-1 block w-full min-w-0 border border-gray-300 rounded-sm react-select-container focus:ring-primary-500 focus:border-primary-500 sm:text-sm" className="react-select-container block w-full min-w-0 flex-1 rounded-sm border border-gray-300 focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
value={{ value: frequency, label: `${frequency} minutes` }} value={{ value: frequency, label: `${frequency} minutes` }}
onChange={(newFrequency) => setFrequency(newFrequency?.value ?? 30)} onChange={(newFrequency) => setFrequency(newFrequency?.value ?? 30)}
/> />

View file

@ -35,7 +35,7 @@ export default function TeamAvailabilityScreen(props: Props) {
if (!member) return <></>; if (!member) return <></>;
return ( return (
<div key={member.id} style={style} className="flex pl-4 border-r border-gray-200 "> <div key={member.id} style={style} className="flex border-r border-gray-200 pl-4 ">
<TeamAvailabilityTimes <TeamAvailabilityTimes
teamId={props.team?.id as number} teamId={props.team?.id as number}
memberId={member.id} memberId={member.id}
@ -43,15 +43,15 @@ export default function TeamAvailabilityScreen(props: Props) {
selectedDate={selectedDate} selectedDate={selectedDate}
selectedTimeZone={selectedTimeZone} selectedTimeZone={selectedTimeZone}
HeaderComponent={ HeaderComponent={
<div className="flex items-center mb-6"> <div className="mb-6 flex items-center">
<Avatar <Avatar
imageSrc={getPlaceholderAvatar(member?.avatar, member?.name as string)} imageSrc={getPlaceholderAvatar(member?.avatar, member?.name as string)}
alt={member?.name || ""} alt={member?.name || ""}
className="w-10 h-10 mt-1 rounded-full min-w-10 min-h-10" className="mt-1 h-10 min-h-10 w-10 min-w-10 rounded-full"
/> />
<div className="inline-block pt-1 ml-3 overflow-hidden"> <div className="ml-3 inline-block overflow-hidden pt-1">
<span className="text-lg font-bold truncate text-neutral-700">{member?.name}</span> <span className="truncate text-lg font-bold text-neutral-700">{member?.name}</span>
<span className="block -mt-1 text-sm text-gray-400 truncate">{member?.email}</span> <span className="-mt-1 block truncate text-sm text-gray-400">{member?.email}</span>
</div> </div>
</div> </div>
} }
@ -61,8 +61,8 @@ export default function TeamAvailabilityScreen(props: Props) {
}; };
return ( return (
<div className="flex flex-col flex-1 bg-white border rounded-sm border-neutral-200"> <div className="flex flex-1 flex-col rounded-sm border border-neutral-200 bg-white">
<div className="flex w-full p-4 rtl:space-x-reverse space-x-5 border-b border-gray-200"> <div className="flex w-full space-x-5 border-b border-gray-200 p-4 rtl:space-x-reverse">
<div className="flex flex-col"> <div className="flex flex-col">
<span className="text-sm font-medium text-neutral-700">Date</span> <span className="text-sm font-medium text-neutral-700">Date</span>
<DatePicker <DatePicker
@ -80,7 +80,7 @@ export default function TeamAvailabilityScreen(props: Props) {
value={selectedTimeZone} value={selectedTimeZone}
onChange={(timezone) => setSelectedTimeZone(timezone.value)} onChange={(timezone) => setSelectedTimeZone(timezone.value)}
classNamePrefix="react-select" classNamePrefix="react-select"
className="w-full border border-gray-300 rounded-sm shadow-sm react-select-container focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm" className="react-select-container w-full rounded-sm border border-gray-300 shadow-sm focus:border-neutral-800 focus:ring-neutral-800 sm:text-sm"
/> />
</div> </div>
<div className="hidden sm:block"> <div className="hidden sm:block">
@ -93,13 +93,13 @@ export default function TeamAvailabilityScreen(props: Props) {
]} ]}
isSearchable={false} isSearchable={false}
classNamePrefix="react-select" classNamePrefix="react-select"
className="flex-1 block w-full min-w-0 border border-gray-300 rounded-sm react-select-container focus:ring-primary-500 focus:border-primary-500 sm:text-sm" className="react-select-container block w-full min-w-0 flex-1 rounded-sm border border-gray-300 focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
value={{ value: frequency, label: `${frequency} minutes` }} value={{ value: frequency, label: `${frequency} minutes` }}
onChange={(newFrequency) => setFrequency(newFrequency?.value ?? 30)} onChange={(newFrequency) => setFrequency(newFrequency?.value ?? 30)}
/> />
</div> </div>
</div> </div>
<div className="flex flex-1 h-full"> <div className="flex h-full flex-1">
<AutoSizer> <AutoSizer>
{({ height, width }) => ( {({ height, width }) => (
<List <List

View file

@ -48,7 +48,7 @@ export default function TeamAvailabilityTimes(props: Props) {
: []; : [];
return ( return (
<div className={classNames("flex-grow p-5 pl-0 min-w-60", props.className)}> <div className={classNames("min-w-60 flex-grow p-5 pl-0", props.className)}>
{props.HeaderComponent} {props.HeaderComponent}
{isLoading && times.length === 0 && <Loader />} {isLoading && times.length === 0 && <Loader />}
{!isLoading && times.length === 0 && ( {!isLoading && times.length === 0 && (
@ -59,7 +59,7 @@ export default function TeamAvailabilityTimes(props: Props) {
{times.map((time) => ( {times.map((time) => (
<div key={time.format()} className="flex flex-row items-center"> <div key={time.format()} className="flex flex-row items-center">
<a <a
className="flex-grow block py-2 mb-2 mr-3 font-medium text-center bg-white border rounded-sm min-w-48 dark:bg-gray-600 text-primary-500 dark:text-neutral-200 border-brand dark:border-transparent hover:bg-brand hover:text-brandcontrast dark:hover:border-black dark:hover:text-white dark:hover:bg-black" className="mb-2 mr-3 block min-w-48 flex-grow rounded-sm border border-brand bg-white py-2 text-center font-medium text-primary-500 hover:bg-brand hover:text-brandcontrast dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 dark:hover:border-black dark:hover:bg-black dark:hover:text-white"
data-testid="time"> data-testid="time">
{time.format("HH:mm")} {time.format("HH:mm")}
</a> </a>

View file

@ -96,7 +96,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
const verifyButton = useMemo(() => { const verifyButton = useMemo(() => {
return ( return (
<Button color="secondary" onClick={verifyWallet} type="button" id="hasToken" name="hasToken"> <Button color="secondary" onClick={verifyWallet} type="button" id="hasToken" name="hasToken">
<img className="h-5 mr-1" src="/integrations/metamask.svg" /> <img className="mr-1 h-5" src="/integrations/metamask.svg" />
{t("verify_wallet")} {t("verify_wallet")}
</Button> </Button>
); );
@ -105,7 +105,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
const connectButton = useMemo(() => { const connectButton = useMemo(() => {
return ( return (
<Button color="secondary" onClick={connectMetamask} type="button"> <Button color="secondary" onClick={connectMetamask} type="button">
<img className="h-5 mr-1" src="/integrations/metamask.svg" /> <img className="mr-1 h-5" src="/integrations/metamask.svg" />
{t("connect_metamask")} {t("connect_metamask")}
</Button> </Button>
); );
@ -120,7 +120,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
await connectMetamask(); await connectMetamask();
await verifyWallet(); await verifyWallet();
}}> }}>
<img className="h-5 mr-1" src="/integrations/metamask.svg" /> <img className="mr-1 h-5" src="/integrations/metamask.svg" />
{t("verify_wallet")} {t("verify_wallet")}
</Button> </Button>
); );
@ -141,7 +141,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
return ( return (
<div <div
className="absolute transition-opacity transform -translate-x-1/2 -translate-y-1/2 opacity-0 dark:bg-gray-900 top-1/2 left-1/2 group-hover:opacity-100" className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform opacity-0 transition-opacity group-hover:opacity-100 dark:bg-gray-900"
id={`crypto-${props.id}`}> id={`crypto-${props.id}`}>
{determineButton()} {determineButton()}
</div> </div>

View file

@ -20,7 +20,7 @@ const HelpMenuItem = () => {
<ChatAltIcon <ChatAltIcon
className={classNames( className={classNames(
"text-neutral-400 group-hover:text-neutral-500", "text-neutral-400 group-hover:text-neutral-500",
"ltr:mr-2 flex-shrink-0 h-5 w-5" "h-5 w-5 flex-shrink-0 ltr:mr-2"
)} )}
aria-hidden="true" aria-hidden="true"
/> />

View file

@ -1,11 +1,11 @@
export class HttpError<TCode extends number = number> extends Error { export class HttpError<TCode extends number = number> extends Error {
public readonly cause: unknown; public readonly cause?: Error;
public readonly statusCode: TCode; public readonly statusCode: TCode;
public readonly message: string; public readonly message: string;
public readonly url: string | undefined; public readonly url: string | undefined;
public readonly method: string | undefined; public readonly method: string | undefined;
constructor(opts: { url?: string; method?: string; message?: string; statusCode: TCode; cause?: unknown }) { constructor(opts: { url?: string; method?: string; message?: string; statusCode: TCode; cause?: Error }) {
super(opts.message ?? `HTTP Error ${opts.statusCode} `); super(opts.message ?? `HTTP Error ${opts.statusCode} `);
Object.setPrototypeOf(this, HttpError.prototype); Object.setPrototypeOf(this, HttpError.prototype);

View file

@ -123,14 +123,14 @@ const AddCalDavIntegration = React.forwardRef<HTMLFormElement, Props>((props, re
<label htmlFor="url" className="block text-sm font-medium text-gray-700"> <label htmlFor="url" className="block text-sm font-medium text-gray-700">
Calendar URL Calendar URL
</label> </label>
<div className="flex mt-1 rounded-md shadow-sm"> <div className="mt-1 flex rounded-md shadow-sm">
<input <input
required required
type="text" type="text"
name="url" name="url"
id="url" id="url"
placeholder="https://example.com/calendar" placeholder="https://example.com/calendar"
className="flex-grow block w-full min-w-0 lowercase border-gray-300 rounded-none rounded-r-sm focus:ring-black focus:border-brand sm:text-sm" className="block w-full min-w-0 flex-grow rounded-none rounded-r-sm border-gray-300 lowercase focus:border-brand focus:ring-black sm:text-sm"
/> />
</div> </div>
</div> </div>
@ -144,7 +144,7 @@ const AddCalDavIntegration = React.forwardRef<HTMLFormElement, Props>((props, re
name="username" name="username"
id="username" id="username"
placeholder="rickroll" placeholder="rickroll"
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm" className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/> />
</div> </div>
<div className="mb-2"> <div className="mb-2">
@ -157,7 +157,7 @@ const AddCalDavIntegration = React.forwardRef<HTMLFormElement, Props>((props, re
name="password" name="password"
id="password" id="password"
placeholder="•••••••••••••" placeholder="•••••••••••••"
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm" className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/> />
</div> </div>
</form> </form>

View file

@ -152,6 +152,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.4.4", "postcss": "^8.4.4",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"prettier-plugin-tailwindcss": "^0.1.6",
"prisma": "3.0.2", "prisma": "3.0.2",
"tailwindcss": "^3.0.0", "tailwindcss": "^3.0.0",
"ts-jest": "^26.0.0", "ts-jest": "^26.0.0",

View file

@ -43,36 +43,36 @@ export default function Custom404() {
noindex: true, noindex: true,
}} }}
/> />
<div className="min-h-screen px-4 bg-white"> <div className="min-h-screen bg-white px-4">
<main className="max-w-xl pt-16 pb-6 mx-auto sm:pt-24"> <main className="mx-auto max-w-xl pt-16 pb-6 sm:pt-24">
{isSignup && process.env.NEXT_PUBLIC_BASE_URL !== "https://app.cal.com" ? ( {isSignup && process.env.NEXT_PUBLIC_BASE_URL !== "https://app.cal.com" ? (
<div> <div>
<div> <div>
<p className="text-sm font-semibold tracking-wide text-black uppercase"> <p className="text-sm font-semibold uppercase tracking-wide text-black">
{t("missing_license")} {t("missing_license")}
</p> </p>
<h1 className="mt-2 text-3xl font-extrabold text-gray-900 font-cal"> <h1 className="mt-2 font-cal text-3xl font-extrabold text-gray-900">
{t("signup_requires")} {t("signup_requires")}
</h1> </h1>
<p className="mt-4">{t("signup_requires_description")}</p> <p className="mt-4">{t("signup_requires_description")}</p>
</div> </div>
<div className="mt-12"> <div className="mt-12">
<h2 className="text-sm font-semibold tracking-wide text-gray-500 uppercase"> <h2 className="text-sm font-semibold uppercase tracking-wide text-gray-500">
{t("next_steps")} {t("next_steps")}
</h2> </h2>
<ul role="list" className="mt-4"> <ul role="list" className="mt-4">
<li className="px-4 py-2 border-2 border-green-500"> <li className="border-2 border-green-500 px-4 py-2">
<a <a
href="https://cal.com/pricing?infra" href="https://cal.com/pricing?infra"
className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-green-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
<CheckIcon className="w-6 h-6 text-green-500" aria-hidden="true" /> <CheckIcon className="h-6 w-6 text-green-500" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="focus:outline-none"> <span className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("acquire_commercial_license")} {t("acquire_commercial_license")}
@ -81,33 +81,33 @@ export default function Custom404() {
</h3> </h3>
<p className="text-base text-gray-500">{t("the_infrastructure_plan")}</p> <p className="text-base text-gray-500">{t("the_infrastructure_plan")}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
<ul role="list" className="border-gray-200 divide-y divide-gray-200"> <ul role="list" className="divide-y divide-gray-200 border-gray-200">
<li className="px-4 py-2"> <li className="px-4 py-2">
<Link href="https://docs.cal.com/self-hosting/install#setting-up-your-first-user"> <Link href="https://docs.cal.com/self-hosting/install#setting-up-your-first-user">
<a className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> <a className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-gray-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50">
<DocumentTextIcon className="w-6 h-6 text-gray-700" aria-hidden="true" /> <DocumentTextIcon className="h-6 w-6 text-gray-700" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("prisma_studio_tip")} {t("prisma_studio_tip")}
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("prisma_studio_tip_description")}</p> <p className="text-base text-gray-500">{t("prisma_studio_tip_description")}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</Link> </Link>
@ -115,12 +115,12 @@ export default function Custom404() {
<li className="px-4 py-2"> <li className="px-4 py-2">
<a <a
href="https://cal.com/slack" href="https://cal.com/slack"
className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-gray-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50">
<svg <svg
viewBox="0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5"
className="w-6 h-6" className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<g clipRule="evenodd" fillRule="evenodd"> <g clipRule="evenodd" fillRule="evenodd">
<path <path
@ -139,17 +139,17 @@ export default function Custom404() {
</svg> </svg>
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
Slack Slack
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("join_our_community")}</p> <p className="text-base text-gray-500">{t("join_our_community")}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
@ -167,18 +167,18 @@ export default function Custom404() {
) : ( ) : (
<> <>
<div className="text-center"> <div className="text-center">
<p className="text-sm font-semibold tracking-wide text-black uppercase">{t("error_404")}</p> <p className="text-sm font-semibold uppercase tracking-wide text-black">{t("error_404")}</p>
<h1 className="mt-2 text-4xl font-extrabold text-gray-900 font-cal sm:text-5xl"> <h1 className="mt-2 font-cal text-4xl font-extrabold text-gray-900 sm:text-5xl">
{t("page_doesnt_exist")} {t("page_doesnt_exist")}
</h1> </h1>
{isSubpage ? ( {isSubpage ? (
<span className="inline-block mt-2 text-lg "> <span className="mt-2 inline-block text-lg ">
{t("check_spelling_mistakes_or_go_back")} {t("check_spelling_mistakes_or_go_back")}
</span> </span>
) : process.env.NEXT_PUBLIC_BASE_URL === "https://app.cal.com" ? ( ) : process.env.NEXT_PUBLIC_BASE_URL === "https://app.cal.com" ? (
<a <a
href={"https://cal.com/signup?username=" + username.replace("/", "")} href={"https://cal.com/signup?username=" + username.replace("/", "")}
className="inline-block mt-2 text-lg "> className="mt-2 inline-block text-lg ">
{t("the_username")} <strong className="text-blue-500">cal.com{username}</strong>{" "} {t("the_username")} <strong className="text-blue-500">cal.com{username}</strong>{" "}
{t("is_still_available")} <span className="text-blue-500">{t("register_now")}</span>. {t("is_still_available")} <span className="text-blue-500">{t("register_now")}</span>.
</a> </a>
@ -194,23 +194,23 @@ export default function Custom404() {
)} )}
</div> </div>
<div className="mt-12"> <div className="mt-12">
<h2 className="text-sm font-semibold tracking-wide text-gray-500 uppercase"> <h2 className="text-sm font-semibold uppercase tracking-wide text-gray-500">
{t("popular_pages")} {t("popular_pages")}
</h2> </h2>
{!isSubpage && process.env.NEXT_PUBLIC_BASE_URL === "https://app.cal.com" && ( {!isSubpage && process.env.NEXT_PUBLIC_BASE_URL === "https://app.cal.com" && (
<ul role="list" className="mt-4"> <ul role="list" className="mt-4">
<li className="px-4 py-2 border-2 border-green-500"> <li className="border-2 border-green-500 px-4 py-2">
<a <a
href={"https://cal.com/signup?username=" + username.replace("/", "")} href={"https://cal.com/signup?username=" + username.replace("/", "")}
className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-green-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
<CheckIcon className="w-6 h-6 text-green-500" aria-hidden="true" /> <CheckIcon className="h-6 w-6 text-green-500" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="focus:outline-none"> <span className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("register")} <strong className="text-green-500">{username}</strong> {t("register")} <strong className="text-green-500">{username}</strong>
@ -219,35 +219,35 @@ export default function Custom404() {
</h3> </h3>
<p className="text-base text-gray-500">{t("claim_username_and_schedule_events")}</p> <p className="text-base text-gray-500">{t("claim_username_and_schedule_events")}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
)} )}
<ul role="list" className="mt-4 border-gray-200 divide-y divide-gray-200"> <ul role="list" className="mt-4 divide-y divide-gray-200 border-gray-200">
{links.map((link, linkIdx) => ( {links.map((link, linkIdx) => (
<li key={linkIdx} className="px-4 py-2"> <li key={linkIdx} className="px-4 py-2">
<Link href={link.href}> <Link href={link.href}>
<a className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> <a className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-gray-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50">
<link.icon className="w-6 h-6 text-gray-700" aria-hidden="true" /> <link.icon className="h-6 w-6 text-gray-700" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{link.title} {link.title}
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{link.description}</p> <p className="text-base text-gray-500">{link.description}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</Link> </Link>
@ -256,12 +256,12 @@ export default function Custom404() {
<li className="px-4 py-2"> <li className="px-4 py-2">
<a <a
href="https://cal.com/slack" href="https://cal.com/slack"
className="relative flex items-start py-6 space-x-4 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg bg-gray-50"> <span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50">
<svg <svg
viewBox="0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5"
className="w-6 h-6" className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<g clipRule="evenodd" fillRule="evenodd"> <g clipRule="evenodd" fillRule="evenodd">
<path <path
@ -280,17 +280,17 @@ export default function Custom404() {
</svg> </svg>
</span> </span>
</div> </div>
<div className="flex-1 min-w-0"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500"> <span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
Slack Slack
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("join_our_community")}</p> <p className="text-base text-gray-500">{t("join_our_community")}</p>
</div> </div>
<div className="self-center flex-shrink-0"> <div className="flex-shrink-0 self-center">
<ChevronRightIcon className="w-5 h-5 text-gray-400" aria-hidden="true" /> <ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>

View file

@ -47,17 +47,17 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
/> />
{isReady && ( {isReady && (
<div className="h-screen dark:bg-black"> <div className="h-screen dark:bg-black">
<main className="max-w-3xl px-4 py-24 mx-auto"> <main className="mx-auto max-w-3xl px-4 py-24">
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<Avatar <Avatar
imageSrc={user.avatar} imageSrc={user.avatar}
className="w-24 h-24 mx-auto mb-4 rounded-full" className="mx-auto mb-4 h-24 w-24 rounded-full"
alt={nameOrUsername} alt={nameOrUsername}
/> />
<h1 className="mb-1 text-3xl font-bold font-cal text-neutral-900 dark:text-white"> <h1 className="mb-1 font-cal text-3xl font-bold text-neutral-900 dark:text-white">
{nameOrUsername} {nameOrUsername}
{user.verified && ( {user.verified && (
<BadgeCheckIcon className="inline w-6 h-6 -mt-1 text-blue-500 dark:text-white" /> <BadgeCheckIcon className="-mt-1 inline h-6 w-6 text-blue-500 dark:text-white" />
)} )}
</h1> </h1>
<p className="text-neutral-500 dark:text-white">{user.bio}</p> <p className="text-neutral-500 dark:text-white">{user.bio}</p>
@ -68,8 +68,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
<div <div
key={type.id} key={type.id}
style={{ display: "flex" }} style={{ display: "flex" }}
className="relative bg-white border rounded-sm group dark:bg-neutral-900 dark:border-0 dark:hover:border-neutral-600 hover:bg-gray-50 border-neutral-200 hover:border-brand"> className="group relative rounded-sm border border-neutral-200 bg-white hover:border-brand hover:bg-gray-50 dark:border-0 dark:bg-neutral-900 dark:hover:border-neutral-600">
<ArrowRightIcon className="absolute w-4 h-4 text-black transition-opacity opacity-0 right-3 top-3 dark:text-white group-hover:opacity-100" /> <ArrowRightIcon className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
<Link <Link
href={{ href={{
pathname: `/${user.username}/${type.slug}`, pathname: `/${user.username}/${type.slug}`,
@ -88,7 +88,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
}} }}
className="block w-full px-6 py-4" className="block w-full px-6 py-4"
data-testid="event-type-link"> data-testid="event-type-link">
<h2 className="font-semibold grow text-neutral-900 dark:text-white">{type.title}</h2> <h2 className="grow font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
<EventTypeDescription eventType={type} /> <EventTypeDescription eventType={type} />
</a> </a>
</Link> </Link>
@ -108,10 +108,10 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
{eventTypes.length === 0 && ( {eventTypes.length === 0 && (
<div className="overflow-hidden rounded-sm shadow"> <div className="overflow-hidden rounded-sm shadow">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="p-8 text-center text-gray-400 dark:text-white">
<h2 className="text-3xl font-semibold text-gray-600 font-cal dark:text-white"> <h2 className="font-cal text-3xl font-semibold text-gray-600 dark:text-white">
{t("uh_oh")} {t("uh_oh")}
</h2> </h2>
<p className="max-w-md mx-auto">{t("no_event_types_have_been_setup")}</p> <p className="mx-auto max-w-md">{t("no_event_types_have_been_setup")}</p>
</div> </div>
</div> </div>
)} )}

View file

@ -18,11 +18,11 @@ export default function Error() {
return ( return (
<AuthContainer title="" description=""> <AuthContainer title="" description="">
<div> <div>
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100"> <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" /> <XIcon className="h-6 w-6 text-red-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title"> <h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
{error} {error}
</h3> </h3>
<div className="mt-2"> <div className="mt-2">
@ -32,7 +32,7 @@ export default function Error() {
</div> </div>
<div className="mt-5 sm:mt-6"> <div className="mt-5 sm:mt-6">
<Link href="/auth/login"> <Link href="/auth/login">
<Button className="w-full flex justify-center">{t("go_back_login")}</Button> <Button className="flex w-full justify-center">{t("go_back_login")}</Button>
</Link> </Link>
</div> </div>
</AuthContainer> </AuthContainer>

View file

@ -58,7 +58,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<> <>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h2 className="mt-6 text-3xl font-extrabold text-center text-gray-900 font-cal"> <h2 className="mt-6 text-center font-cal text-3xl font-extrabold text-gray-900">
{t("success")} {t("success")}
</h2> </h2>
</div> </div>
@ -66,7 +66,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<Link href="/auth/login"> <Link href="/auth/login">
<button <button
type="button" type="button"
className="flex justify-center w-full px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"> className="flex w-full justify-center px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
{t("login")} {t("login")}
</button> </button>
</Link> </Link>
@ -80,14 +80,14 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<> <>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h2 className="mt-6 text-3xl font-extrabold text-center text-gray-900 font-cal">{t("whoops")}</h2> <h2 className="mt-6 text-center font-cal text-3xl font-extrabold text-gray-900">{t("whoops")}</h2>
<h2 className="text-3xl font-extrabold text-center text-gray-900">{t("request_is_expired")}</h2> <h2 className="text-center text-3xl font-extrabold text-gray-900">{t("request_is_expired")}</h2>
</div> </div>
<p>{t("request_is_expired_instructions")}</p> <p>{t("request_is_expired_instructions")}</p>
<Link href="/auth/forgot-password"> <Link href="/auth/forgot-password">
<button <button
type="button" type="button"
className="flex justify-center w-full px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"> className="flex w-full justify-center px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
{t("try_again")} {t("try_again")}
</button> </button>
</Link> </Link>
@ -102,15 +102,15 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
}, [resetPasswordRequest]); }, [resetPasswordRequest]);
return ( return (
<div className="flex flex-col justify-center min-h-screen py-12 bg-gray-50 sm:px-6 lg:px-8"> <div className="flex min-h-screen flex-col justify-center bg-gray-50 py-12 sm:px-6 lg:px-8">
<HeadSeo title={t("reset_password")} description={t("change_your_password")} /> <HeadSeo title={t("reset_password")} description={t("change_your_password")} />
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="px-4 py-8 mx-2 space-y-6 bg-white rounded-lg shadow sm:px-10"> <div className="mx-2 space-y-6 rounded-lg bg-white px-4 py-8 shadow sm:px-10">
{isRequestExpired && <Expired />} {isRequestExpired && <Expired />}
{!isRequestExpired && !success && ( {!isRequestExpired && !success && (
<> <>
<div className="space-y-6"> <div className="space-y-6">
<h2 className="mt-6 text-3xl font-extrabold text-center text-gray-900 font-cal"> <h2 className="mt-6 text-center font-cal text-3xl font-extrabold text-gray-900">
{t("reset_password")} {t("reset_password")}
</h2> </h2>
<p>{t("enter_new_password")}</p> <p>{t("enter_new_password")}</p>
@ -151,7 +151,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
type="password" type="password"
autoComplete="password" autoComplete="password"
required required
className="block w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm appearance-none focus:outline-none focus:ring-black focus:border-brand sm:text-sm" className="block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-brand focus:outline-none focus:ring-black sm:text-sm"
/> />
</div> </div>
</div> </div>
@ -160,12 +160,12 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<button <button
type="submit" type="submit"
disabled={loading} disabled={loading}
className={`w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ${ className={`flex w-full justify-center rounded-md border border-transparent bg-blue-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 ${
loading ? "cursor-not-allowed" : "" loading ? "cursor-not-allowed" : ""
}`}> }`}>
{loading && ( {loading && (
<svg <svg
className="w-5 h-5 mr-3 -ml-1 text-white animate-spin" className="mr-3 -ml-1 h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24"> viewBox="0 0 24 24">

View file

@ -111,7 +111,7 @@ export default function ForgotPassword({ csrfToken }: { csrfToken: string }) {
/> />
<div className="space-y-2"> <div className="space-y-2">
<Button <Button
className="justify-center w-full" className="w-full justify-center"
type="submit" type="submit"
disabled={loading} disabled={loading}
aria-label={t("request_password_reset")} aria-label={t("request_password_reset")}

View file

@ -139,7 +139,7 @@ export default function Login({
{errorMessage && <Alert severity="error" title={errorMessage} />} {errorMessage && <Alert severity="error" title={errorMessage} />}
<div className="flex space-y-2"> <div className="flex space-y-2">
<Button <Button
className="flex justify-center w-full" className="flex w-full justify-center"
type="submit" type="submit"
disabled={form.formState.isSubmitting}> disabled={form.formState.isSubmitting}>
{twoFactorRequired ? t("submit") : t("sign_in")} {twoFactorRequired ? t("submit") : t("sign_in")}
@ -153,7 +153,7 @@ export default function Login({
<div className="mt-5"> <div className="mt-5">
<Button <Button
color="secondary" color="secondary"
className="flex justify-center w-full" className="flex w-full justify-center"
data-testid={"google"} data-testid={"google"}
onClick={async (e) => { onClick={async (e) => {
e.preventDefault(); e.preventDefault();

View file

@ -26,8 +26,8 @@ export default function Logout(props: Props) {
return ( return (
<AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")}> <AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")}>
<div className="mb-4"> <div className="mb-4">
<div className="flex items-center justify-center w-12 h-12 mx-auto bg-green-100 rounded-full"> <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<CheckIcon className="w-6 h-6 text-green-600" /> <CheckIcon className="h-6 w-6 text-green-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<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">
@ -39,7 +39,7 @@ export default function Logout(props: Props) {
</div> </div>
</div> </div>
<Link href="/auth/login"> <Link href="/auth/login">
<Button className="flex justify-center w-full"> {t("go_back_login")}</Button> <Button className="flex w-full justify-center"> {t("go_back_login")}</Button>
</Link> </Link>
</AuthContainer> </AuthContainer>
); );

View file

@ -64,18 +64,18 @@ export default function Signup({ email }: Props) {
return ( return (
<div <div
className="flex flex-col justify-center min-h-screen py-12 bg-gray-50 sm:px-6 lg:px-8" className="flex min-h-screen flex-col justify-center bg-gray-50 py-12 sm:px-6 lg:px-8"
aria-labelledby="modal-title" aria-labelledby="modal-title"
role="dialog" role="dialog"
aria-modal="true"> aria-modal="true">
<HeadSeo title={t("sign_up")} description={t("sign_up")} /> <HeadSeo title={t("sign_up")} description={t("sign_up")} />
<div className="sm:mx-auto sm:w-full sm:max-w-md"> <div className="sm:mx-auto sm:w-full sm:max-w-md">
<h2 className="text-3xl font-extrabold text-center text-gray-900 font-cal"> <h2 className="text-center font-cal text-3xl font-extrabold text-gray-900">
{t("create_your_account")} {t("create_your_account")}
</h2> </h2>
</div> </div>
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="px-4 py-8 mx-2 bg-white shadow sm:rounded-lg sm:px-10"> <div className="mx-2 bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
{/* TODO: Refactor as soon as /availability is live */} {/* TODO: Refactor as soon as /availability is live */}
<FormProvider {...methods}> <FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(signUp)} className="space-y-6 bg-white"> <form onSubmit={methods.handleSubmit(signUp)} className="space-y-6 bg-white">
@ -83,25 +83,25 @@ export default function Signup({ email }: Props) {
<div className="space-y-2"> <div className="space-y-2">
<TextField <TextField
addOnLeading={ addOnLeading={
<span className="inline-flex items-center px-3 text-gray-500 border border-r-0 border-gray-300 rounded-l-sm bg-gray-50 sm:text-sm"> <span className="inline-flex items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm">
{process.env.NEXT_PUBLIC_APP_URL}/ {process.env.NEXT_PUBLIC_APP_URL}/
</span> </span>
} }
labelProps={{ className: "block text-sm font-medium text-gray-700" }} labelProps={{ className: "block text-sm font-medium text-gray-700" }}
className="flex-grow block w-full min-w-0 lowercase border-gray-300 rounded-none rounded-r-sm focus:ring-black focus:border-black sm:text-sm" className="block w-full min-w-0 flex-grow rounded-none rounded-r-sm border-gray-300 lowercase focus:border-black focus:ring-black sm:text-sm"
{...register("username")} {...register("username")}
required required
/> />
<EmailField <EmailField
{...register("email")} {...register("email")}
className="block w-full px-3 py-2 mt-1 bg-gray-100 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-black focus:border-black sm:text-sm" className="mt-1 block w-full rounded-md border border-gray-300 bg-gray-100 px-3 py-2 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
/> />
<PasswordField <PasswordField
labelProps={{ labelProps={{
className: "block text-sm font-medium text-gray-700", className: "block text-sm font-medium text-gray-700",
}} }}
{...register("password")} {...register("password")}
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-black focus:border-black sm:text-sm" className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
/> />
<PasswordField <PasswordField
label={t("confirm_password")} label={t("confirm_password")}
@ -112,16 +112,16 @@ export default function Signup({ email }: Props) {
validate: (value) => validate: (value) =>
value === methods.watch("password") || (t("error_password_mismatch") as string), value === methods.watch("password") || (t("error_password_mismatch") as string),
})} })}
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-black focus:border-black sm:text-sm" className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
/> />
</div> </div>
<div className="flex rtl:space-x-reverse space-x-2"> <div className="flex space-x-2 rtl:space-x-reverse">
<Button loading={isSubmitting} className="justify-center w-7/12"> <Button loading={isSubmitting} className="w-7/12 justify-center">
{t("create_account")} {t("create_account")}
</Button> </Button>
<Button <Button
color="secondary" color="secondary"
className="justify-center w-5/12" className="w-5/12 justify-center"
onClick={() => onClick={() =>
signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string }) signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string })
}> }>

View file

@ -90,7 +90,7 @@ export function AvailabilityForm(props: inferQueryOutput<"viewer.availability">)
await createSchedule(values); await createSchedule(values);
}} }}
className="col-span-3 space-y-2 lg:col-span-2"> className="col-span-3 space-y-2 lg:col-span-2">
<div className="px-4 py-5 bg-white border border-gray-200 divide-y rounded-sm sm:p-6"> <div className="divide-y rounded-sm border border-gray-200 bg-white px-4 py-5 sm:p-6">
<h3 className="mb-5 text-base font-medium leading-6 text-gray-900">{t("change_start_end")}</h3> <h3 className="mb-5 text-base font-medium leading-6 text-gray-900">{t("change_start_end")}</h3>
<Schedule name="schedule" /> <Schedule name="schedule" />
</div> </div>
@ -107,12 +107,12 @@ export function AvailabilityForm(props: inferQueryOutput<"viewer.availability">)
<Button>{t("save")}</Button> <Button>{t("save")}</Button>
</div> </div>
</Form> </Form>
<div className="col-span-3 ltr:ml-2 rtl:mr-2 lg:col-span-1 min-w-40"> <div className="col-span-3 min-w-40 ltr:ml-2 rtl:mr-2 lg:col-span-1">
<div className="px-4 py-5 border border-gray-200 rounded-sm sm:p-6 "> <div className="rounded-sm border border-gray-200 px-4 py-5 sm:p-6 ">
<h3 className="text-base font-medium leading-6 text-gray-900"> <h3 className="text-base font-medium leading-6 text-gray-900">
{t("something_doesnt_look_right")} {t("something_doesnt_look_right")}
</h3> </h3>
<div className="max-w-xl mt-2 text-sm text-gray-500"> <div className="mt-2 max-w-xl text-sm text-gray-500">
<p>{t("troubleshoot_availability")}</p> <p>{t("troubleshoot_availability")}</p>
</div> </div>
<div className="mt-5"> <div className="mt-5">

View file

@ -16,14 +16,14 @@ type User = inferQueryOutput<"viewer.me">;
const AvailabilityView = ({ user }: { user: User }) => { const AvailabilityView = ({ user }: { user: User }) => {
const { t } = useLocale(); const { t } = useLocale();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [availability, setAvailability] = useState([]); const [availability, setAvailability] = useState<{ end: string; start: string }[]>([]);
const [selectedDate, setSelectedDate] = useState(dayjs()); const [selectedDate, setSelectedDate] = useState(dayjs());
function convertMinsToHrsMins(mins: number) { function convertMinsToHrsMins(mins: number) {
let h = Math.floor(mins / 60); let h = Math.floor(mins / 60);
let m = mins % 60; let m = mins % 60;
h = h < 10 ? "0" + h : h; h = h < 10 ? 0 + h : h;
m = m < 10 ? "0" + m : m; m = m < 10 ? 0 + m : m;
return `${h}:${m}`; return `${h}:${m}`;
} }
@ -51,12 +51,12 @@ const AvailabilityView = ({ user }: { user: User }) => {
}, [selectedDate]); }, [selectedDate]);
return ( return (
<div className="max-w-xl overflow-hidden bg-white rounded-sm shadow"> <div className="max-w-xl overflow-hidden rounded-sm bg-white shadow">
<div className="px-4 py-5 sm:p-6"> <div className="px-4 py-5 sm:p-6">
{t("overview_of_day")}{" "} {t("overview_of_day")}{" "}
<input <input
type="date" type="date"
className="inline h-8 p-0 border-none" className="inline h-8 border-none p-0"
defaultValue={selectedDate.format("YYYY-MM-DD")} defaultValue={selectedDate.format("YYYY-MM-DD")}
onChange={(e) => { onChange={(e) => {
setSelectedDate(dayjs(e.target.value)); setSelectedDate(dayjs(e.target.value));
@ -65,7 +65,7 @@ const AvailabilityView = ({ user }: { user: User }) => {
<small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small> <small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small>
<div className="mt-4 space-y-4"> <div className="mt-4 space-y-4">
<div className="overflow-hidden rounded-sm bg-brand"> <div className="overflow-hidden rounded-sm bg-brand">
<div className="px-4 py-2 sm:px-6 text-brandcontrast"> <div className="px-4 py-2 text-brandcontrast sm:px-6">
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)} {t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
</div> </div>
</div> </div>
@ -95,7 +95,7 @@ const AvailabilityView = ({ user }: { user: User }) => {
)} )}
<div className="overflow-hidden rounded-sm bg-brand"> <div className="overflow-hidden rounded-sm bg-brand">
<div className="px-4 py-2 sm:px-6 text-brandcontrast"> <div className="px-4 py-2 text-brandcontrast sm:px-6">
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)} {t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
</div> </div>
</div> </div>

View file

@ -45,7 +45,7 @@ export default function Bookings() {
return ( return (
<Shell heading={t("bookings")} subtitle={t("bookings_description")}> <Shell heading={t("bookings")} subtitle={t("bookings_description")}>
<BookingsShell> <BookingsShell>
<div className="flex flex-col -mx-4 sm:mx-auto"> <div className="-mx-4 flex flex-col sm:mx-auto">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> <div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
{query.status === "error" && ( {query.status === "error" && (
@ -54,9 +54,9 @@ export default function Bookings() {
{(query.status === "loading" || query.status === "idle") && <Loader />} {(query.status === "loading" || query.status === "idle") && <Loader />}
{query.status === "success" && !isEmpty && ( {query.status === "success" && !isEmpty && (
<> <>
<div className="mt-6 overflow-hidden border border-b border-gray-200 rounded-sm"> <div className="mt-6 overflow-hidden rounded-sm border border-b border-gray-200">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-gray-200" data-testid="bookings"> <tbody className="divide-y divide-gray-200 bg-white" data-testid="bookings">
{query.data.pages.map((page, index) => ( {query.data.pages.map((page, index) => (
<Fragment key={index}> <Fragment key={index}>
{page.bookings.map((booking) => ( {page.bookings.map((booking) => (
@ -67,7 +67,7 @@ export default function Bookings() {
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="text-center p-4" ref={buttonInView.ref}> <div className="p-4 text-center" ref={buttonInView.ref}>
<Button <Button
loading={query.isFetchingNextPage} loading={query.isFetchingNextPage}
disabled={!query.hasNextPage} disabled={!query.hasNextPage}

Some files were not shown because too many files have changed in this diff Show more