Compare commits
3 Commits
main
...
gh-readonl
Author | SHA1 | Date | |
---|---|---|---|
|
4431516674 | ||
|
05909d7001 | ||
|
a4ddf4233b |
|
@ -77,5 +77,5 @@ it("should render city name as option label if cityData is not empty", () => {
|
|||
});
|
||||
|
||||
it("should return timezone as option label if cityData is empty", () => {
|
||||
expect(handleOptionLabel(option, [])).toMatchInlineSnapshot(`"America/Los_Angeles GMT -8:00"`);
|
||||
expect(handleOptionLabel(option, [])).toMatchInlineSnapshot(`"America/Los Angeles GMT -8:00"`);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import jsonLogic from "json-logic-js";
|
||||
|
||||
// converts input to lowercase if string
|
||||
function normalize<T extends string | string[]>(input: T): T {
|
||||
if (typeof input === "string") {
|
||||
return input.toLowerCase() as T;
|
||||
}
|
||||
if (input instanceof Array) {
|
||||
return input.map((item) => {
|
||||
if (typeof item === "string") {
|
||||
return item.toLowerCase();
|
||||
}
|
||||
// if array item is not a string, return it as is
|
||||
return item;
|
||||
}) as T;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single Select equals and not equals uses it
|
||||
* Short Text equals and not equals uses it
|
||||
*/
|
||||
jsonLogic.add_operation("==", function (a: any, b: any) {
|
||||
return normalize(a) == normalize(b);
|
||||
});
|
||||
|
||||
jsonLogic.add_operation("===", function (a: any, b: any) {
|
||||
return normalize(a) === normalize(b);
|
||||
});
|
||||
|
||||
jsonLogic.add_operation("!==", function (a: any, b: any) {
|
||||
return normalize(a) !== normalize(b);
|
||||
});
|
||||
|
||||
jsonLogic.add_operation("!=", function (a: any, b: any) {
|
||||
return normalize(a) != normalize(b);
|
||||
});
|
||||
|
||||
/**
|
||||
* Multiselect "equals" and "not equals" uses it
|
||||
* Singleselect "any in" and "not in" uses it
|
||||
* Long Text/Short Text/Email/Phone "contains" also uses it.
|
||||
*/
|
||||
jsonLogic.add_operation("in", function (a: string, b: string | string[]) {
|
||||
const first = normalize(a);
|
||||
const second = normalize(b);
|
||||
if (!second) return false;
|
||||
return second.indexOf(first) !== -1;
|
||||
});
|
||||
|
||||
export default jsonLogic;
|
|
@ -1,5 +1,4 @@
|
|||
import type { App_RoutingForms_Form } from "@prisma/client";
|
||||
import jsonLogic from "json-logic-js";
|
||||
import { Utils as QbUtils } from "react-awesome-query-builder";
|
||||
import type { z } from "zod";
|
||||
|
||||
|
@ -8,6 +7,7 @@ import type { zodNonRouterRoute } from "../zod";
|
|||
import { getQueryBuilderConfig } from "./getQueryBuilderConfig";
|
||||
import { isFallbackRoute } from "./isFallbackRoute";
|
||||
import isRouter from "./isRouter";
|
||||
import jsonLogic from "./jsonLogicOverrides";
|
||||
|
||||
export function processRoute({
|
||||
form,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useRouter } from "next/navigation";
|
||||
import { useState, Suspense } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
@ -8,14 +9,7 @@ import type { RecordingItemSchema } from "@calcom/prisma/zod-utils";
|
|||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { PartialReference } from "@calcom/types/EventManager";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
UpgradeTeamsBadge,
|
||||
} from "@calcom/ui";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Download } from "@calcom/ui/components/icon";
|
||||
|
||||
|
@ -101,6 +95,7 @@ const useRecordingDownload = () => {
|
|||
const ViewRecordingsList = ({ roomName, hasTeamPlan }: { roomName: string; hasTeamPlan: boolean }) => {
|
||||
const { t } = useLocale();
|
||||
const { setRecordingId, isFetching, recordingId } = useRecordingDownload();
|
||||
const router = useRouter();
|
||||
|
||||
const { data: recordings } = trpc.viewer.getCalVideoRecordings.useQuery(
|
||||
{ roomName },
|
||||
|
@ -121,7 +116,7 @@ const ViewRecordingsList = ({ roomName, hasTeamPlan }: { roomName: string; hasTe
|
|||
{recordings.data.map((recording: RecordingItemSchema, index: number) => {
|
||||
return (
|
||||
<div
|
||||
className="flex w-full items-center justify-between rounded-md border px-4 py-2"
|
||||
className="border-subtle flex w-full items-center justify-between rounded-md border px-4 py-2"
|
||||
key={recording.id}>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-sm font-semibold">
|
||||
|
@ -138,7 +133,12 @@ const ViewRecordingsList = ({ roomName, hasTeamPlan }: { roomName: string; hasTe
|
|||
{t("download")}
|
||||
</Button>
|
||||
) : (
|
||||
<UpgradeTeamsBadge />
|
||||
<Button
|
||||
tooltip={t("upgrade_to_access_recordings_description")}
|
||||
className="ml-4 lg:ml-0"
|
||||
onClick={() => router.push("/teams")}>
|
||||
{t("upgrade")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { useRouter } from "next/router";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Users } from "@calcom/ui/components/icon";
|
||||
|
||||
export default function UpgradeRecordingBanner() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="bg-subtle flex items-start gap-2 rounded-md p-4">
|
||||
<Users className="dark:bg-gray-90 inline-block h-5 w-5" />
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-sm font-semibold">{t("upgrade_to_access_recordings_title")}</h2>
|
||||
<p className="text-sm font-normal">{t("upgrade_to_access_recordings_description")}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
router.push(`${WEBAPP_URL}/teams`);
|
||||
}}>
|
||||
{t("upgrade_now")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -35,5 +35,6 @@ export const addCitiesToDropdown = (cities: ICity[]) => {
|
|||
export const handleOptionLabel = (option: ITimezoneOption, cities: ICity[]) => {
|
||||
const timezoneValue = option.label.split(")")[0].replace("(", " ").replace("T", "T ");
|
||||
const cityName = option.label.split(") ")[1];
|
||||
return cities.length > 0 ? `${cityName}${timezoneValue}` : `${option.value}${timezoneValue}`;
|
||||
const refactoredOption = option.value.replace(/_/g, " ");
|
||||
return cities.length > 0 ? `${cityName}${timezoneValue}` : `${refactoredOption}${timezoneValue}`;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,13 @@ export function TimezoneSelect({
|
|||
});
|
||||
}, [components]);
|
||||
|
||||
// We use modifiedTimezones in place of the allTimezones object replacing any underscores in the curly braces
|
||||
// with spaces and removing the America/Detroit timezone, adding the America/New_York timezone instead.
|
||||
const modifiedTimezones = useMemo(() => {
|
||||
const { "America/Detroit": _, ...rest } = allTimezones;
|
||||
return { ...rest, "America/New_York": "New York" };
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseSelect
|
||||
className={className}
|
||||
|
@ -41,13 +48,15 @@ export function TimezoneSelect({
|
|||
isDisabled={isLoading}
|
||||
{...reactSelectProps}
|
||||
timezones={{
|
||||
...allTimezones,
|
||||
...modifiedTimezones,
|
||||
...addCitiesToDropdown(cities),
|
||||
"America/Asuncion": "Asuncion",
|
||||
}}
|
||||
onInputChange={handleInputChange}
|
||||
{...props}
|
||||
formatOptionLabel={(option) => <p className="truncate">{(option as ITimezoneOption).value}</p>}
|
||||
formatOptionLabel={(option) => (
|
||||
<p className="truncate">{(option as ITimezoneOption).value.replace(/_/g, " ")}</p>
|
||||
)}
|
||||
getOptionLabel={(option) => handleOptionLabel(option as ITimezoneOption, cities)}
|
||||
classNames={{
|
||||
...timezoneClassNames,
|
||||
|
|
|
@ -11,12 +11,12 @@ const outputDir = path.join(__dirname, "test-results");
|
|||
// Dev Server on local can be slow to start up and process requests. So, keep timeouts really high on local, so that tests run reliably locally
|
||||
|
||||
// So, if not in CI, keep the timers high, if the test is stuck somewhere and there is unnecessary wait developer can see in browser that it's stuck
|
||||
const DEFAULT_NAVIGATION_TIMEOUT = process.env.CI ? 15000 : 50000;
|
||||
const DEFAULT_EXPECT_TIMEOUT = process.env.CI ? 15000 : 50000;
|
||||
const DEFAULT_NAVIGATION_TIMEOUT = process.env.CI ? 15000 : 120000;
|
||||
const DEFAULT_EXPECT_TIMEOUT = process.env.CI ? 15000 : 120000;
|
||||
|
||||
// Test Timeout can hit due to slow expect, slow navigation.
|
||||
// So, it should me much higher than sum of expect and navigation timeouts as there can be many async expects and navigations in a single test
|
||||
const DEFAULT_TEST_TIMEOUT = process.env.CI ? 60000 : 120000;
|
||||
const DEFAULT_TEST_TIMEOUT = process.env.CI ? 60000 : 240000;
|
||||
|
||||
const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user