Compare commits

...

2 Commits

Author SHA1 Message Date
Udit Takkar 680862a7f8
Merge branch 'main' into feat/one-time-booking 2024-01-02 12:27:30 +05:30
Udit Takkar b1a618b562 feat: one off booking 2024-01-02 12:24:49 +05:30
7 changed files with 276 additions and 8 deletions

View File

@ -54,6 +54,7 @@ import {
Tooltip,
ArrowButton,
} from "@calcom/ui";
import { Ticket } from "@calcom/ui/components/icon";
import {
Clipboard,
Code,
@ -787,12 +788,17 @@ const CTA = ({ data }: { data: GetByViewerResponse }) => {
});
return (
<CreateButton
data-testid="new-event-type"
subtitle={t("create_event_on").toUpperCase()}
options={profileOptions}
createDialog={() => <CreateEventTypeDialog profileOptions={profileOptions} />}
/>
<div className="flex items-center gap-2">
<Button href="/one-time-booking" StartIcon={Ticket} color="secondary">
{t("one_time_link")}
</Button>
<CreateButton
data-testid="new-event-type"
subtitle={t("create_event_on").toUpperCase()}
options={profileOptions}
createDialog={() => <CreateEventTypeDialog profileOptions={profileOptions} />}
/>
</div>
);
};

View File

@ -0,0 +1,21 @@
import PickTimeSlots from "@calcom/features/one-time-booking/PickTimeSlots";
import { getLayout } from "@calcom/features/troubleshooter/layout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HeadSeo } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
const OneTimeBooking = () => {
const { t } = useLocale();
return (
<>
<HeadSeo title={t("one_time_link")} description={t("one_time_link_description")} />
<PickTimeSlots />
</>
);
};
OneTimeBooking.getLayout = getLayout;
OneTimeBooking.PageWrapper = PageWrapper;
export default OneTimeBooking;

View File

@ -923,6 +923,7 @@
"popular_categories": "Popular Categories",
"number_apps_one": "{{count}} App",
"number_apps_other": "{{count}} Apps",
"number_slots_selected":"{{count}} slots selected",
"trending_apps": "Trending Apps",
"most_popular": "Most Popular",
"installed_apps": "Installed Apps",

View File

@ -0,0 +1,50 @@
import {
FilterCheckboxFieldsContainer,
FilterCheckboxField,
} from "@calcom/features/filters/components/TeamsFilter";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, AnimatedPopover } from "@calcom/ui";
import { Layers, Clock } from "@calcom/ui/components/icon";
const Header = () => {
const { t, i18n } = useLocale();
return (
<div className="border-default relative z-10 flex border-b px-5 py-4 ltr:border-l rtl:border-r">
<div className="flex w-full items-center justify-between">
<div>{t("select_timeslots_to_make_available")}</div>
<div className="flex items-center gap-2">
<SelectedSlots />
<Button color="primary">{t("confirm")}</Button>
</div>
</div>
</div>
);
};
const SelectedSlots = () => {
const { t } = useLocale();
const numberOfSelectedSlots = 0;
return (
<AnimatedPopover
text={t("number_slots_selected", { count: numberOfSelectedSlots })}
prefix=""
popoverTriggerClassNames="mb-0 max-w-84"
StartIcon={Clock}>
<FilterCheckboxFieldsContainer>
<FilterCheckboxField
id="all"
icon={<Layers className="h-4 w-4" />}
checked={false}
onChange={() => {
console.log("all");
}}
label={t("all")}
/>
</FilterCheckboxFieldsContainer>
</AnimatedPopover>
);
};
export default Header;

View File

@ -0,0 +1,61 @@
import StickyBox from "react-sticky-box";
import classNames from "@calcom/lib/classNames";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { LargeCalendar } from "../troubleshooter/components/LargeCalendar";
import Header from "./Header";
import SideBar from "./SideBar";
const extraDaysConfig = {
desktop: 7,
tablet: 4,
};
const PickTimeSlots = () => {
const isMobile = useMediaQuery("(max-width: 768px)");
const isTablet = useMediaQuery("(max-width: 1024px)");
const extraDays = isTablet ? extraDaysConfig.tablet : extraDaysConfig.desktop;
return (
<div
className={classNames(
"text-default fixed inset-0 grid min-h-full w-full flex-col items-center overflow-clip ",
isMobile
? "[--one-time-booking-meta-width:0px]"
: "[--one-time-booking-meta-width:250px] lg:[--one-time-booking-meta-width:430px]"
)}>
<div
style={{
width: "100vw",
minHeight: "100vh",
height: "auto",
gridTemplateAreas: `
"meta header header"
"meta main main"
`,
gridTemplateColumns: "var(--one-time-booking-meta-width) 1fr",
gridTemplateRows: "70px auto",
}}
className={classNames(
"bg-default dark:bg-muted grid max-w-full items-start dark:[color-scheme:dark] sm:transition-[width] sm:duration-300 sm:motion-reduce:transition-none md:flex-row "
)}>
<div className={classNames("bg-default dark:bg-muted sticky top-0 z-10 [grid-area:header]")}>
{/* <TroubleshooterHeader extraDays={extraDays} isMobile={isMobile} /> */}
<Header />
</div>
<StickyBox key="meta" className={classNames("relative z-10")}>
<div className="ps-6">
<SideBar />
</div>
</StickyBox>
<div className="border-subtle sticky top-0 ml-[-1px] h-full [grid-area:main] md:border-l ">
<LargeCalendar extraDays={extraDays} />
</div>
</div>
</div>
);
};
export default PickTimeSlots;

View File

@ -0,0 +1,121 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { ArrowLeft } from "lucide-react";
import Link from "next/link";
import { useForm, Controller } from "react-hook-form";
import * as z from "zod";
import { useTimePreferences } from "@calcom/features/bookings/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Skeleton, TextField, TimezoneSelect, Label, Button, Divider } from "@calcom/ui";
import { Plus } from "@calcom/ui/components/icon";
const BackButtonInSidebar = ({ name }: { name: string }) => {
return (
<Link
href="/event-types"
className="hover:bg-subtle group-hover:text-default text-emphasis group flex h-6 max-h-6 w-full flex-row items-center rounded-md py-2 pr-3">
<ArrowLeft className="h-4 w-4 stroke-[2px] ltr:mr-[10px] rtl:ml-[10px] rtl:rotate-180 md:mt-0" />
<Skeleton
title={name}
as="p"
className="max-w-36 min-h-4 truncate font-semibold"
loadingClassName="ms-3">
{name}
</Skeleton>
</Link>
);
};
const formSchema = z.object({
title: z.string(),
description: z.string(),
timeZone: z.string(),
duration: z.number(),
reserveTimeSlots: z.boolean(),
});
type formSchemaType = z.infer<typeof formSchema>;
const SideBar = () => {
const { t } = useLocale();
const { timezone } = useTimePreferences();
const formMethods = useForm<formSchemaType>({
mode: "onChange",
defaultValues: {
timeZone: timezone,
duration: 15,
reserveTimeSlots: false,
},
resolver: zodResolver(formSchema),
});
return (
<div className="relative z-10 hidden h-screen w-full flex-col gap-6 overflow-y-scroll py-6 pl-4 pr-6 sm:flex md:pl-0">
<BackButtonInSidebar name={t("back")} />
<div>
<p className="text-emphasis font-cal text-xl font-semibold">{t("one_time_link")}</p>
<p className="text-subtle mt-2 font-normal">{t("one_time_link_description")}</p>
</div>
<div className="space-y-6">
<TextField required label={t("title")} {...formMethods.register("title")} />
<TextField required label={t("description")} {...formMethods.register("description")} />
<TextField
required
type="number"
label={t("duration")}
{...formMethods.register("duration")}
addOnSuffix={<>{t("minutes")}</>}
min={1}
/>
<Controller
name="timeZone"
control={formMethods.control}
render={({ field: { value } }) => (
<div>
<Label className="text-emphasis">
<>{t("timezone")}</>
</Label>
<TimezoneSelect
id="timezone"
value={value}
onChange={(event) => {
if (event) formMethods.setValue("timeZone", event.value, { shouldDirty: true });
}}
/>
</div>
)}
/>
<div>
<Label className="text-emphasis">
<>{t("availability")}</>
</Label>
<Button color="secondary" StartIcon={Plus}>
{t("add_schedule")}
</Button>
</div>
<div>
<Label className="text-emphasis">
<>{t("location")}</>
</Label>
</div>
<Divider />
<div className="flex items-start gap-2">
<input
type="checkbox"
className="text-emphasis focus:ring-emphasis dark:text-muted border-default bg-default h-4 w-4 rounded"
/>
<div>
<p className="text-sm font-semibold leading-none">{t("reserve_times")}</p>
<p className="text-subtle">{t("put_placeholder_on_calendar ")}</p>
</div>
</div>
</div>
</div>
);
};
export default SideBar;

View File

@ -2,6 +2,7 @@ import * as Popover from "@radix-ui/react-popover";
import React from "react";
import { classNames } from "@calcom/lib";
import type { SVGComponent } from "@calcom/types/SVGComponent";
import { Tooltip } from "@calcom/ui";
import { ChevronDown } from "../icon";
@ -14,6 +15,7 @@ export const AnimatedPopover = ({
Trigger,
defaultOpen,
prefix,
StartIcon,
}: {
text: string;
count?: number;
@ -22,6 +24,7 @@ export const AnimatedPopover = ({
Trigger?: React.ReactNode;
defaultOpen?: boolean;
prefix?: string;
StartIcon?: SVGComponent | React.ElementType;
}) => {
const [open, setOpen] = React.useState(defaultOpen ?? false);
const ref = React.useRef<HTMLDivElement>(null);
@ -59,10 +62,15 @@ export const AnimatedPopover = ({
{Trigger ? (
Trigger
) : (
<div className="max-w-36 flex items-center">
<div className="max-w-40 flex items-center">
<Tooltip content={`${prefix}${text}`}>
<div className="flex select-none truncate font-medium">
<div className="flex select-none items-center truncate font-medium">
{prefix && <span className="text-subtle">{prefix}&nbsp;</span>}
{StartIcon && (
<div className="text-default flex h-4 w-4 items-center justify-center ltr:mr-2 rtl:ml-2">
<StartIcon className="h-4 w-4" />
</div>
)}
{text}
{count && count > 0 && (
<div className="text-emphasis flex items-center justify-center rounded-full font-semibold">