Compare commits
3 Commits
main
...
gh-readonl
Author | SHA1 | Date | |
---|---|---|---|
199ebdefa4 | |||
76c286b795 | |||
c5bde759e4 |
|
@ -35,7 +35,6 @@ const Component = ({
|
|||
email,
|
||||
tos,
|
||||
privacy,
|
||||
isProOnly,
|
||||
teamsPlanRequired,
|
||||
descriptionItems,
|
||||
isTemplate,
|
||||
|
@ -151,7 +150,6 @@ const Component = ({
|
|||
{!isGlobal && (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
disableInstall={disableInstall}
|
||||
teamsPlanRequired={teamsPlanRequired}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
|
@ -192,7 +190,6 @@ const Component = ({
|
|||
) : (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
disableInstall={disableInstall}
|
||||
teamsPlanRequired={teamsPlanRequired}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
|
@ -362,7 +359,6 @@ export default function App(props: {
|
|||
tos?: string;
|
||||
privacy?: string;
|
||||
licenseRequired: AppType["licenseRequired"];
|
||||
isProOnly: AppType["isProOnly"];
|
||||
teamsPlanRequired: AppType["teamsPlanRequired"];
|
||||
descriptionItems?: Array<string | { iframe: IframeHTMLAttributes<HTMLIFrameElement> }>;
|
||||
isTemplate?: boolean;
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
useIsEmbed,
|
||||
} from "@calcom/embed-core/embed-iframe";
|
||||
import { createBooking, createRecurringBooking } from "@calcom/features/bookings/lib";
|
||||
import { useTimePreferences } from "@calcom/features/bookings/lib";
|
||||
import {
|
||||
getBookingFieldsWithSystemFields,
|
||||
SystemField,
|
||||
|
@ -39,7 +40,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { parseDate, parseRecurringDates } from "@calcom/lib/parse-dates";
|
||||
import { parseDate, parseDateTimeWithTimeZone, parseRecurringDates } from "@calcom/lib/parse-dates";
|
||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { TimeFormat } from "@calcom/lib/timeFormat";
|
||||
|
@ -231,6 +232,8 @@ const BookingPage = ({
|
|||
{}
|
||||
);
|
||||
|
||||
const { timezone } = useTimePreferences();
|
||||
|
||||
const reserveSlot = () => {
|
||||
if (queryDuration) {
|
||||
reserveSlotMutation.mutate({
|
||||
|
@ -602,7 +605,7 @@ const BookingPage = ({
|
|||
<Calendar className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||
{isClientTimezoneAvailable &&
|
||||
typeof booking.startTime === "string" &&
|
||||
parseDate(dayjs(booking.startTime), i18n.language, {
|
||||
parseDateTimeWithTimeZone(booking.startTime, i18n.language, timezone, {
|
||||
selectedTimeFormat: timeFormat,
|
||||
})}
|
||||
</p>
|
||||
|
|
|
@ -73,7 +73,6 @@ function SingleAppPage(props: inferSSRProps<typeof getStaticProps>) {
|
|||
email={data.email}
|
||||
licenseRequired={data.licenseRequired}
|
||||
teamsPlanRequired={data.teamsPlanRequired}
|
||||
isProOnly={data.isProOnly}
|
||||
descriptionItems={source.data?.items as string[] | undefined}
|
||||
isTemplate={data.isTemplate}
|
||||
dependencies={data.dependencies}
|
||||
|
|
|
@ -46,7 +46,6 @@ export default function OmniInstallAppButton({
|
|||
return (
|
||||
<InstallAppButton
|
||||
type={app.type}
|
||||
isProOnly={app.isProOnly}
|
||||
teamsPlanRequired={app.teamsPlanRequired}
|
||||
wrapperClassName={classNames("[@media(max-width:260px)]:w-full", className)}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
|
|
|
@ -49,7 +49,6 @@ export const InstallAppButtonWithoutPlanCheck = (
|
|||
|
||||
export const InstallAppButton = (
|
||||
props: {
|
||||
isProOnly?: App["isProOnly"];
|
||||
teamsPlanRequired?: App["teamsPlanRequired"];
|
||||
type: App["type"];
|
||||
wrapperClassName?: string;
|
||||
|
@ -86,7 +85,7 @@ export const InstallAppButton = (
|
|||
},
|
||||
true
|
||||
);
|
||||
}, [isUserLoading, user, router, props.isProOnly, hasTeamPlan, props.teamsPlanRequired]);
|
||||
}, [isUserLoading, user, router, hasTeamPlan, props.teamsPlanRequired]);
|
||||
|
||||
if (isUserLoading || isTeamPlanStatusLoading) {
|
||||
return null;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Fragment, useCallback, useEffect, useMemo, useState, useRef } from "react";
|
||||
import type {
|
||||
ArrayPath,
|
||||
Control,
|
||||
|
@ -382,6 +382,38 @@ const CopyTimes = ({
|
|||
}) => {
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
const { i18n, t } = useLocale();
|
||||
const itteratablesByKeyRef = useRef<(HTMLInputElement | HTMLButtonElement)[]>([]);
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, []);
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
const itteratables = itteratablesByKeyRef.current;
|
||||
const isActionRequired =
|
||||
event.key === "Tab" || event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter";
|
||||
if (!isActionRequired || !itteratables.length) return;
|
||||
event.preventDefault();
|
||||
const currentFocused = document.activeElement as HTMLInputElement | HTMLButtonElement;
|
||||
let currentIndex = itteratables.findIndex((checkbox) => checkbox === currentFocused);
|
||||
if (event.key === "Enter") {
|
||||
if (currentIndex === -1) return;
|
||||
currentFocused.click();
|
||||
return;
|
||||
}
|
||||
if (currentIndex === -1) {
|
||||
itteratables[0].focus();
|
||||
} else {
|
||||
// Move focus based on the arrow key pressed
|
||||
if (event.key === "ArrowUp") {
|
||||
currentIndex = (currentIndex - 1 + itteratables.length) % itteratables.length;
|
||||
} else if (event.key === "ArrowDown" || event.key === "Tab") {
|
||||
currentIndex = (currentIndex + 1) % itteratables.length;
|
||||
}
|
||||
itteratables[currentIndex].focus();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2 py-2">
|
||||
|
@ -402,6 +434,11 @@ const CopyTimes = ({
|
|||
setSelected([]);
|
||||
}
|
||||
}}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
itteratablesByKeyRef.current.push(ref as HTMLInputElement);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
|
@ -423,6 +460,12 @@ const CopyTimes = ({
|
|||
setSelected(selected.filter((item) => item !== weekdayIndex));
|
||||
}
|
||||
}}
|
||||
ref={(ref) => {
|
||||
if (ref && disabled !== weekdayIndex) {
|
||||
//we don't need to iterate over disabled elements
|
||||
itteratablesByKeyRef.current.push(ref as HTMLInputElement);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
|
@ -432,10 +475,24 @@ const CopyTimes = ({
|
|||
</div>
|
||||
<hr className="border-subtle" />
|
||||
<div className="space-x-2 px-2 rtl:space-x-reverse">
|
||||
<Button color="minimal" onClick={() => onCancel()}>
|
||||
<Button
|
||||
color="minimal"
|
||||
onClick={() => onCancel()}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
itteratablesByKeyRef.current.push(ref as HTMLButtonElement);
|
||||
}
|
||||
}}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => onClick(selected)}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => onClick(selected)}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
itteratablesByKeyRef.current.push(ref as HTMLButtonElement);
|
||||
}
|
||||
}}>
|
||||
{t("apply")}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,45 @@ export const parseDate = (date: string | null | Dayjs, language: string, options
|
|||
return processDate(date, language, options);
|
||||
};
|
||||
|
||||
const timeOptions: Intl.DateTimeFormatOptions = {
|
||||
hour12: true,
|
||||
hourCycle: "h12",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
};
|
||||
|
||||
const dateOptions: Intl.DateTimeFormatOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
};
|
||||
|
||||
export const parseDateTimeWithTimeZone = (
|
||||
date: Date,
|
||||
language: string,
|
||||
timezone: string,
|
||||
options?: ExtraOptions
|
||||
): string => {
|
||||
timeOptions.timeZone = timezone;
|
||||
dateOptions.timeZone = timezone;
|
||||
|
||||
if (options?.withDefaultTimeFormat) {
|
||||
timeOptions.hourCycle = "h12";
|
||||
} else if (options?.selectedTimeFormat) {
|
||||
timeOptions.hourCycle = options.selectedTimeFormat === TimeFormat.TWELVE_HOUR ? "h12" : "h24";
|
||||
if (timeOptions.hourCycle === "h24") {
|
||||
delete timeOptions.hour12;
|
||||
}
|
||||
}
|
||||
const formattedDate = new Date(date).toLocaleDateString(language, dateOptions);
|
||||
const formattedTime = new Date(date)
|
||||
.toLocaleTimeString(language, timeOptions)
|
||||
.replace(" ", "")
|
||||
.toLowerCase();
|
||||
return `${formattedTime}, ${formattedDate}`;
|
||||
};
|
||||
|
||||
export const parseRecurringDates = (
|
||||
{
|
||||
startDate,
|
||||
|
|
|
@ -129,7 +129,6 @@ export interface App {
|
|||
teamsPlanRequired?: {
|
||||
upgradeUrl: string;
|
||||
};
|
||||
isProOnly?: boolean;
|
||||
appData?: AppData;
|
||||
/**
|
||||
* @deprecated
|
||||
|
|
|
@ -96,7 +96,6 @@ export function AppCard({ app, credentials, searchText }: AppCardProps) {
|
|||
? !app.isGlobal && (
|
||||
<InstallAppButton
|
||||
type={app.type}
|
||||
isProOnly={app.isProOnly}
|
||||
teamsPlanRequired={app.teamsPlanRequired}
|
||||
disableInstall={!!app.dependencies && !app.dependencyData?.some((data) => !data.installed)}
|
||||
wrapperClassName="[@media(max-width:260px)]:w-full"
|
||||
|
@ -125,7 +124,6 @@ export function AppCard({ app, credentials, searchText }: AppCardProps) {
|
|||
credentials.length === 0 && (
|
||||
<InstallAppButton
|
||||
type={app.type}
|
||||
isProOnly={app.isProOnly}
|
||||
wrapperClassName="[@media(max-width:260px)]:w-full"
|
||||
disableInstall={!!app.dependencies && app.dependencyData?.some((data) => !data.installed)}
|
||||
teamsPlanRequired={app.teamsPlanRequired}
|
||||
|
|
Loading…
Reference in New Issue
Block a user