Compare commits
2 Commits
main
...
feat/one-t
Author | SHA1 | Date | |
---|---|---|---|
|
680862a7f8 | ||
|
b1a618b562 |
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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} </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">
|
||||
|
|
Loading…
Reference in New Issue
Block a user