Compare commits

...

7 Commits

Author SHA1 Message Date
Udit Takkar 6370a6c48b
refactor: radio area group (#9113)
* refactor: radio area group

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore: remove select

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: e2e test

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 18:24:42 +00:00
Adithya Krishna d677192e6e
feat: Auto check PR titles if they follow conventional commits spec (#9109)
Signed-off-by: Adithya Krishna <adikrish@redhat.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 18:24:12 +00:00
Yatendra f96109324d
change /ee to /commercial (#8948)
Co-authored-by: ygpta <25252636+yatendraguptaofficial@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 17:50:30 +00:00
GitStart-Cal.com 4e88739c36
fix: Hidden event type switch behaviour is out of sync between event types page & single view (#9075)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 17:50:12 +00:00
GitStart-Cal.com 7e8a948fc5
chore: Rounding, frames and radio buttons of the booking forms not consistent (#8705)
* Rounding, frames and radio buttons of the booking forms not consistent

* Updates

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-25 15:53:42 +00:00
TachyonicBytes 428cfc529d
Add the or operator to the emails package.json as well (#9013)
Co-authored-by: TachyonicBytes <support@tachyonicbytes.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 15:52:43 +00:00
Udit Takkar a47a4f9fb0
fix: upgrade btn bug (#9104)
* fix: upgrade btn bug

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: use border-subtle

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-25 15:51:13 +00:00
26 changed files with 238 additions and 231 deletions

View File

@ -0,0 +1,21 @@
name: "Validate PRs"
on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize
permissions:
pull-requests: read
jobs:
validate-pr:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -238,20 +238,34 @@ function EventTypeSingleLayout({
<div className="flex items-center justify-end">
{!eventType.metadata.managedEventConfig && (
<>
<div className="sm:hover:bg-muted hidden items-center rounded-md px-2 lg:flex">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center whitespace-nowrap pr-2 sm:inline">
{t("hide_from_profile")}
</Skeleton>
<Switch
id="hiddenSwitch"
checked={formMethods.watch("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", e);
}}
/>
<div
className={classNames(
"sm:hover:bg-muted hidden cursor-pointer items-center rounded-md",
formMethods.watch("hidden") ? "px-2" : "",
"lg:flex"
)}>
{formMethods.watch("hidden") && (
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center whitespace-nowrap pr-2 sm:inline">
{t("hidden")}
</Skeleton>
)}
<Tooltip
content={
formMethods.watch("hidden") ? t("show_eventtype_on_profile") : t("hide_from_profile")
}>
<div className="self-center rounded-md p-2">
<Switch
id="hiddenSwitch"
checked={!formMethods.watch("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", !e);
}}
/>
</div>
</Tooltip>
</div>
<VerticalDivider className="hidden lg:block" />
</>

View File

@ -109,6 +109,7 @@
"react-intl": "^5.25.1",
"react-live-chat-loader": "^2.7.3",
"react-multi-email": "^0.5.3",
"react-phone-input-2": "^2.15.1",
"react-phone-number-input": "^3.2.7",
"react-schemaorg": "^2.0.0",
"react-select": "^5.7.0",

View File

@ -416,7 +416,8 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{!isManagedEventType && (
<>
{type.hidden && <Badge variant="gray">{t("hidden")}</Badge>}
<Tooltip content={t("show_eventtype_on_profile")}>
<Tooltip
content={type.hidden ? t("show_eventtype_on_profile") : t("hide_from_profile")}>
<div className="self-center rounded-md p-2">
<Switch
name="Hidden"

View File

@ -39,10 +39,10 @@ test.describe("Managed Event Types tests", () => {
await page.click("[data-testid=new-event-type-dropdown]");
await page.click("[data-testid=option-team-1]");
// Expecting we can add a managed event type as team owner
await expect(page.locator('input[value="MANAGED"]')).toBeVisible();
await expect(page.locator('button[value="MANAGED"]')).toBeVisible();
// Actually creating a managed event type to test things further
await page.click('input[value="MANAGED"]');
await page.click('button[value="MANAGED"]');
await page.fill("[name=title]", "managed");
await page.click("[type=submit]");
});

View File

@ -179,7 +179,7 @@ html.todesktop-platform-darwin.light main.bg-default{
background: rgba(255,255,255,0.8) !important
}
/*
/*
html.todesktop aside a {
height: 28px;
padding: 0px 8px;
@ -497,4 +497,16 @@ select:focus {
-webkit-appearance: none;
}
.react-tel-input .country-list .country:hover,
.react-tel-input .country-list .country.highlight {
@apply !bg-emphasis;
}
.react-tel-input .flag-dropdown .selected-flag,
.react-tel-input .flag-dropdown.open .selected-flag {
@apply !bg-default;
}
.react-tel-input .flag-dropdown {
@apply !border-r-default !border-y-0 !border-l-0 left-0.5;
}

View File

@ -129,7 +129,7 @@ function NumberWidget({ value, setValue, ...remainingProps }: TextLikeComponentP
type="number"
labelSrOnly={remainingProps.noLabel}
containerClassName="w-full"
className="focus:border-brand-default bg-default dark:bg-muted border-default disabled:bg-emphasis focus:ring-brand block w-full rounded-md text-sm disabled:hover:cursor-not-allowed"
className="bg-default border-default disabled:bg-emphasis focus:ring-brand-default dark:focus:border-emphasis block w-full rounded-md text-sm focus:border-neutral-300 disabled:hover:cursor-not-allowed"
value={value}
onChange={(e) => {
setValue(e.target.value);

View File

@ -1,4 +1,4 @@
The Cal.com Enterprise Edition (EE) license (the “EE License”)
The Cal.com Commercial License (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
@ -8,7 +8,7 @@ used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("EE Subscription")
and otherwise have a valid Cal.com Commercial License subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in

View File

@ -4,7 +4,7 @@
"version": "0.0.0",
"private": true,
"scripts": {
"dx": "docker compose up -d"
"dx": "docker compose up -d || docker-compose up -d"
},
"dependencies": {
"@calcom/dayjs": "*",

View File

@ -1,4 +1,4 @@
The Cal.com Enterprise Edition (EE) license (the “EE License”)
The Cal.com Commercial License (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
@ -8,7 +8,7 @@ used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("EE Subscription")
and otherwise have a valid Cal.com Commercial License subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in

View File

@ -1,4 +1,4 @@
The Cal.com Enterprise Edition (EE) license (the “EE License”)
The Cal.com Commercial License (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
@ -8,7 +8,7 @@ used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("EE Subscription")
and otherwise have a valid Cal.com Commercial License subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in

View File

@ -1,4 +1,4 @@
The Cal.com Enterprise Edition (EE) license (the “EE License”)
The Cal.com Commercial License (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
@ -8,7 +8,7 @@ used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("EE Subscription")
and otherwise have a valid Cal.com Commercial License subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in

View File

@ -1,6 +1,6 @@
{
"name": "@calcom/ee",
"description": "Cal.com Enterprise Edition features",
"description": "Cal.com Commercial License features",
"version": "0.0.0",
"private": true,
"license": "See license in LICENSE",

View File

@ -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>
);

View File

@ -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>
);
}

View File

@ -254,6 +254,9 @@ export default function CreateEventTypeDialog({
/>
)}
<RadioArea.Group
onValueChange={(val: SchedulingType) => {
form.setValue("schedulingType", val);
}}
className={classNames(
"mt-1 flex gap-4",
isAdmin && flags["managed-event-types"] && "flex-col"

View File

@ -134,7 +134,7 @@ export const Components: Record<BookingFieldType, Component> = {
const { t } = useLocale();
value = value || [];
const inputClassName =
"dark:placeholder:text-darkgray-600 focus:border-brand-default border-subtle block w-full rounded-md border-default text-sm focus:ring-black disabled:bg-emphasis disabled:hover:cursor-not-allowed dark:bg-transparent dark:selection:bg-green-500 disabled:dark:text-subtle";
"dark:placeholder:text-muted focus:border-emphasis border-subtle block w-full rounded-md border-default text-sm focus:ring-black disabled:bg-emphasis disabled:hover:cursor-not-allowed dark:selection:bg-green-500 disabled:dark:text-subtle bg-default";
return (
<>
{value.length ? (
@ -148,11 +148,11 @@ export const Components: Record<BookingFieldType, Component> = {
<EmailField
disabled={readOnly}
value={value[index]}
className={inputClassName}
onChange={(e) => {
value[index] = e.target.value;
setValue(value);
}}
className={inputClassName}
placeholder={placeholder}
label={<></>}
required
@ -251,7 +251,7 @@ export const Components: Record<BookingFieldType, Component> = {
}
setValue(newValue);
}}
className="dark:bg-darkgray-300 border-subtle text-emphasis h-4 w-4 rounded focus:ring-black ltr:mr-2 rtl:ml-2"
className="border-default dark:border-default hover:bg-subtle checked:hover:bg-brand-default checked:bg-brand-default dark:checked:bg-brand-default dark:bg-darkgray-100 dark:hover:bg-subtle dark:checked:hover:bg-brand-default h-4 w-4 cursor-pointer rounded ltr:mr-2 rtl:ml-2"
value={option.value}
checked={value.includes(option.value)}
/>
@ -310,7 +310,7 @@ export const Components: Record<BookingFieldType, Component> = {
type="radio"
disabled={readOnly}
name={name}
className="dark:bg-darkgray-300 border-subtle text-emphasis h-4 w-4 focus:ring-black ltr:mr-2 rtl:ml-2"
className="dark:checked:bg-brand-default dark:bg-darkgray-100 dark:hover:bg-subtle dark:checked:hover:bg-brand-default focus:border-brand-default focus:ring-brand-default border-emphasis h-4 w-4 cursor-pointer text-[--cal-brand] focus:ring-2 ltr:mr-2 rtl:ml-2"
value={option.value}
onChange={(e) => {
setValue({

View File

@ -201,7 +201,7 @@ const SettingsSidebarContainer = ({
alt="User Avatar"
/>
)}
<p className="text-sm font-medium leading-5 truncate">{t(tab.name)}</p>
<p className="truncate text-sm font-medium leading-5">{t(tab.name)}</p>
</div>
</div>
<div className="my-3 space-y-0.5">
@ -226,7 +226,7 @@ const SettingsSidebarContainer = ({
{tab && tab.icon && (
<tab.icon className="h-[16px] w-[16px] stroke-[2px] ltr:mr-3 rtl:ml-3 md:mt-0" />
)}
<p className="text-sm font-medium leading-5 truncate">{t(tab.name)}</p>
<p className="truncate text-sm font-medium leading-5">{t(tab.name)}</p>
</div>
</Link>
{teams &&

View File

@ -23,7 +23,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
{...props}
ref={ref}
className={classNames(
"hover:border-emphasis border-default bg-default placeholder:text-muted text-emphasis disabled:hover:border-default disabled:bg-subtle mb-2 block h-9 rounded-md border py-2 px-3 text-sm leading-4 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-1 disabled:cursor-not-allowed",
"hover:border-emphasis dark:focus:border-emphasis border-default bg-default placeholder:text-muted text-emphasis disabled:hover:border-default disabled:bg-subtle focus:ring-brand-default mb-2 block h-9 rounded-md border py-2 px-3 text-sm leading-4 focus:border-neutral-300 focus:outline-none focus:ring-2 disabled:cursor-not-allowed",
isFullWidth && "w-full",
props.className
)}
@ -129,7 +129,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
{addOnLeading || addOnSuffix ? (
<div
dir="ltr"
className="group relative mb-1 flex items-center rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-neutral-800 focus-within:ring-offset-1">
className="focus-within:ring-brand-default group relative mb-1 flex items-center rounded-md focus-within:outline-none focus-within:ring-2">
{addOnLeading && (
<Addon isFilled={addOnFilled} className={classNames("rounded-l-md border-r-0", addOnClassname)}>
{addOnLeading}
@ -280,7 +280,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function
ref={ref}
{...props}
className={classNames(
"hover:border-emphasis border-default bg-default placeholder:text-muted text-emphasis disabled:hover:border-default disabled:bg-subtle mb-2 block w-full rounded-md border py-2 px-3 text-sm focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-1 disabled:cursor-not-allowed",
"hover:border-emphasis border-default bg-default placeholder:text-muted text-emphasis disabled:hover:border-default disabled:bg-subtle focus:ring-brand-default mb-2 block w-full rounded-md border py-2 px-3 text-sm focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-1 disabled:cursor-not-allowed",
props.className
)}
/>

View File

@ -51,7 +51,7 @@ export const Select = <
dropdownIndicator: () => "text-default",
control: (state) =>
cx(
"bg-default border-default !min-h-9 h-9 text-sm leading-4 placeholder:text-sm placeholder:font-normal focus-within:ring-2 focus-within:ring-emphasis hover:border-emphasis rounded-md border ",
"bg-default border-default !min-h-9 h-9 text-sm leading-4 placeholder:text-sm placeholder:font-normal dark:focus:border-emphasis focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default hover:border-emphasis rounded-md border",
state.isMulti
? variant === "checkbox"
? "px-3 py-2 h-fit"

View File

@ -1,53 +1,69 @@
import { isSupportedCountry } from "libphonenumber-js";
import { useState } from "react";
import BasePhoneInput from "react-phone-number-input";
import type { Props, Country } from "react-phone-number-input";
import "react-phone-number-input/style.css";
import PhoneInput from "react-phone-input-2";
import "react-phone-input-2/lib/style.css";
import { classNames } from "@calcom/lib";
import { trpc } from "@calcom/trpc/react";
export type PhoneInputProps = Props<{
value: string;
export type PhoneInputProps = {
value?: string;
id?: string;
placeholder?: string;
required?: boolean;
className?: string;
name?: string;
}>;
disabled?: boolean;
onChange: (value: string) => void;
};
function PhoneInput({ name, className = "", onChange, ...rest }: PhoneInputProps) {
function BasePhoneInput({ name, className = "", onChange, ...rest }: PhoneInputProps) {
const defaultCountry = useDefaultCountry();
return (
<BasePhoneInput
<PhoneInput
{...rest}
flagUrl="/country-flag-icons/3x2/{XX}.svg"
international
defaultCountry={defaultCountry}
name={name}
onChange={onChange}
countrySelectProps={{ className: "text-emphasis" }}
numberInputProps={{
className: "border-0 text-sm focus:ring-0 bg-default text-default",
country={defaultCountry}
enableSearch
disableSearchIcon
inputProps={{
name: name,
required: rest.required,
placeholder: rest.placeholder,
}}
className={classNames(
"hover:border-emphasis border-default bg-default rounded-md border py-px pl-3 focus-within:border-neutral-300 focus-within:outline-none focus-within:ring-2 focus-within:ring-neutral-800 focus-within:ring-offset-1 disabled:cursor-not-allowed",
onChange={(value) => onChange(value)}
containerClass={classNames(
"hover:border-emphasis dark:focus:border-emphasis border-default !bg-default rounded-md border focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default disabled:cursor-not-allowed",
className
)}
inputClass="text-sm focus:ring-0 !bg-default text-default"
buttonClass="text-emphasis !bg-default hover:!bg-emphasis"
searchClass="!text-default !bg-default hover:!bg-emphasis"
dropdownClass="!text-default !bg-default"
inputStyle={{ width: "inherit", border: 0 }}
searchStyle={{
display: "flex",
flexDirection: "row",
alignItems: "center",
padding: "6px 12px",
gap: "8px",
width: "296px",
height: "28px",
marginLeft: "-4px",
}}
dropdownStyle={{ width: "max-content" }}
/>
);
}
const useDefaultCountry = () => {
const [defaultCountry, setDefaultCountry] = useState<Country>("US");
const [defaultCountry, setDefaultCountry] = useState("us");
trpc.viewer.public.countryCode.useQuery(undefined, {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
onSuccess: (data) => {
if (isSupportedCountry(data?.countryCode)) {
setDefaultCountry(data.countryCode as Country);
setDefaultCountry(data.countryCode);
}
},
});
@ -55,4 +71,4 @@ const useDefaultCountry = () => {
return defaultCountry;
};
export default PhoneInput;
export default BasePhoneInput;

View File

@ -11,7 +11,7 @@ export const Radio = (props: RadioGroupPrimitive.RadioGroupItemProps & { childre
<RadioGroupPrimitive.Item
{...props}
className={classNames(
"hover:bg-subtle border-default focus:ring-emphasis mt-0.5 h-4 w-4 flex-shrink-0 rounded-full border focus:ring-2",
"hover:bg-subtle border-default dark:checked:bg-brand-default dark:bg-darkgray-100 dark:hover:bg-subtle dark:checked:hover:bg-brand-default focus:ring-brand-default me-1.5 mt-0.5 h-4 w-4 flex-shrink-0 rounded-full border text-[--cal-brand] focus:ring-2 focus:ring-offset-1",
props.disabled && "opacity-60"
)}>
{props.children}
@ -21,7 +21,7 @@ export const Indicator = ({ disabled }: { disabled?: boolean }) => (
<RadioGroupPrimitive.Indicator
className={classNames(
"after:bg-default dark:after:bg-inverted relative flex h-full w-full items-center justify-center rounded-full bg-black after:h-[6px] after:w-[6px] after:rounded-full after:content-['']",
disabled ? "after:bg-muted" : "bg-black"
disabled ? "after:bg-muted" : "bg-brand-default"
)}
/>
);

View File

@ -1,59 +1,57 @@
import React from "react";
import { useId } from "@radix-ui/react-id";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import type { ReactNode } from "react";
import classNames from "@calcom/lib/classNames";
type RadioAreaProps = React.InputHTMLAttributes<HTMLInputElement> & { classNames?: { container?: string } };
type RadioAreaProps = RadioGroupPrimitive.RadioGroupItemProps & {
children: ReactNode;
classNames?: { container?: string };
};
const RadioArea = React.forwardRef<HTMLInputElement, RadioAreaProps>(
({ children, className, classNames: innerClassNames, ...props }, ref) => {
return (
<label className={classNames("relative flex", className)}>
<input
ref={ref}
className="text-emphasis bg-subtle border-emphasis focus:ring-none peer absolute top-[0.9rem] left-3 align-baseline"
type="radio"
{...props}
/>
<div
className={classNames(
"text-default peer-checked:border-emphasis border-subtle rounded-md border p-4 pt-3 pl-10",
innerClassNames?.container
)}>
{children}
</div>
</label>
);
}
);
type MaybeArray<T> = T[] | T;
type ChildrenOfType<T extends React.ElementType> = MaybeArray<
React.ReactElement<React.ComponentPropsWithoutRef<T>>
>;
interface RadioAreaGroupProps extends Omit<React.ComponentPropsWithoutRef<"div">, "onChange" | "children"> {
onChange?: (value: string) => void;
children: ChildrenOfType<typeof RadioArea>;
}
const RadioArea = ({ children, className, classNames: innerClassNames, ...props }: RadioAreaProps) => {
const radioAreaId = useId();
const id = props.id ?? radioAreaId;
const RadioAreaGroup = ({ children, className, onChange, ...passThroughProps }: RadioAreaGroupProps) => {
const childrenWithProps = React.Children.map(children, (child) => {
if (onChange && React.isValidElement(child)) {
return React.cloneElement(child, {
onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
},
});
}
return child;
});
return (
<div className={className} {...passThroughProps}>
{childrenWithProps}
<div
className={classNames(
"border-subtle [&:has(input:checked)]:border-emphasis relative flex items-start rounded-md border",
className
)}>
<RadioGroupPrimitive.Item
id={id}
{...props}
className={classNames(
"hover:bg-subtle border-default focus:ring-emphasis absolute top-[0.9rem] left-3 mt-0.5 h-4 w-4 flex-shrink-0 rounded-full border focus:ring-2",
props.disabled && "opacity-60"
)}>
<RadioGroupPrimitive.Indicator
className={classNames(
"after:bg-default dark:after:bg-inverted relative flex h-full w-full items-center justify-center rounded-full bg-black after:h-[6px] after:w-[6px] after:rounded-full after:content-['']",
props.disabled ? "after:bg-muted" : "bg-black"
)}
/>
</RadioGroupPrimitive.Item>
<label htmlFor={id} className={classNames("text-default p-4 pt-3 pl-10", innerClassNames?.container)}>
{children}
</label>
</div>
);
};
RadioAreaGroup.displayName = "RadioAreaGroup";
RadioArea.displayName = "RadioArea";
const RadioAreaGroup = ({
children,
className,
onValueChange,
...passThroughProps
}: RadioGroupPrimitive.RadioGroupProps) => {
return (
<RadioGroupPrimitive.Root className={className} onValueChange={onValueChange} {...passThroughProps}>
{children}
</RadioGroupPrimitive.Root>
);
};
const Item = RadioArea;
const Group = RadioAreaGroup;

View File

@ -1,68 +0,0 @@
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import React from "react";
import type { FieldValues, Path, UseFormReturn } from "react-hook-form";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { ChevronDown } from "@calcom/ui/components/icon";
import { RadioArea, RadioAreaGroup } from "./RadioAreaGroup";
interface OptionProps
extends Pick<React.OptionHTMLAttributes<HTMLOptionElement>, "value" | "label" | "className"> {
description?: string;
}
export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>;
interface RadioAreaSelectProps<TFieldValues extends FieldValues>
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "form"> {
options: OptionProps[]; // allow options to be passed programmatically, like options={}
onChange?: (value: string) => void;
form: UseFormReturn<TFieldValues>;
name: FieldPath<TFieldValues>;
}
export const Select = function RadioAreaSelect<TFieldValues extends FieldValues>(
props: RadioAreaSelectProps<TFieldValues>
) {
const { t } = useLocale();
const {
options,
form,
disabled = !options.length, // if not explicitly disabled and the options length is empty, disable anyway
placeholder = t("select"),
} = props;
const getLabel = (value: string | ReadonlyArray<string> | number | undefined) =>
options.find((option: OptionProps) => option.value === value)?.label;
return (
<Collapsible className={classNames("w-full", props.className)}>
<CollapsibleTrigger
type="button"
disabled={disabled}
className={classNames(
"focus:ring-primary-500 border-default bg-default mb-1 block w-full cursor-pointer rounded-sm border border p-2 text-left shadow-sm sm:text-sm",
disabled && "bg-emphasis cursor-default focus:ring-0 "
)}>
{getLabel(props.value) ?? placeholder}
<ChevronDown className="text-subtle float-right h-5 w-5" />
</CollapsibleTrigger>
<CollapsibleContent>
<RadioAreaGroup className="space-y-2 text-sm" onChange={props.onChange}>
{options.map((option) => (
<RadioArea
{...form.register(props.name)}
{...option}
key={Array.isArray(option.value) ? option.value.join(",") : `${option.value}`}>
<strong className="mb-1 block">{option.label}</strong>
<p>{option.description}</p>
</RadioArea>
))}
</RadioAreaGroup>
</CollapsibleContent>
</Collapsible>
);
};
export default Select;

View File

@ -1,3 +1,2 @@
export * as RadioGroup from "./RadioAreaGroup";
export { default as Select } from "./Select";
export { Group, Indicator, Label, Radio, RadioField } from "./Radio";

View File

@ -5212,6 +5212,7 @@ __metadata:
react-intl: ^5.25.1
react-live-chat-loader: ^2.7.3
react-multi-email: ^0.5.3
react-phone-input-2: ^2.15.1
react-phone-number-input: ^3.2.7
react-schemaorg: ^2.0.0
react-select: ^5.7.0
@ -17969,7 +17970,7 @@ __metadata:
languageName: node
linkType: hard
"classnames@npm:^2.2.5":
"classnames@npm:^2.2.5, classnames@npm:^2.2.6":
version: 2.3.2
resolution: "classnames@npm:2.3.2"
checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e
@ -28194,6 +28195,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.memoize@npm:^4.1.2":
version: 4.1.2
resolution: "lodash.memoize@npm:4.1.2"
checksum: 9ff3942feeccffa4f1fafa88d32f0d24fdc62fd15ded5a74a5f950ff5f0c6f61916157246744c620173dddf38d37095a92327d5fd3861e2063e736a5c207d089
languageName: node
linkType: hard
"lodash.merge@npm:^4.6.2":
version: 4.6.2
resolution: "lodash.merge@npm:4.6.2"
@ -28215,6 +28223,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.reduce@npm:^4.6.0":
version: 4.6.0
resolution: "lodash.reduce@npm:4.6.0"
checksum: 81f2a1045440554f8427f895ef479f1de5c141edd7852dde85a894879312801efae0295116e5cf830c531c1a51cdab8f3628c3ad39fa21a9874bb9158d9ea075
languageName: node
linkType: hard
"lodash.startcase@npm:^4.4.0":
version: 4.4.0
resolution: "lodash.startcase@npm:4.4.0"
@ -28222,6 +28237,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.startswith@npm:^4.2.1":
version: 4.2.1
resolution: "lodash.startswith@npm:4.2.1"
checksum: 1847371cbf6f32c4125a555847aff8a1ff1a977f40882b0ad4e2656a32e364793cfa7602915c9165cc0bba61fbd802e561888126d242faadcf3fc77215bab19b
languageName: node
linkType: hard
"lodash.uniq@npm:4.5.0":
version: 4.5.0
resolution: "lodash.uniq@npm:4.5.0"
@ -32045,21 +32067,23 @@ __metadata:
languageName: node
linkType: hard
"playwright-core@npm:1.34.0":
version: 1.34.0
resolution: "playwright-core@npm:1.34.0"
checksum: 1821ab254b3c756e52c144c26cae7250dd5a1c779277cdd583bd289501d45cceaea40a8185ebf7eaa56b8291e4a042e3eef65f511da2306349c275794c9a582d
"playwright-core@npm:1.33.0":
version: 1.33.0
resolution: "playwright-core@npm:1.33.0"
bin:
playwright: cli.js
checksum: 5fb7bda06a8b73b56b85b5a0b8f711211dde57a375d9379289e22239b2de879c6d93c8fdc9ba44b932bf100914ab1ca1a55697ad88440fdd0a39101fc020b77f
languageName: node
linkType: hard
"playwright@npm:^1.31.2":
version: 1.34.0
resolution: "playwright@npm:1.34.0"
version: 1.33.0
resolution: "playwright@npm:1.33.0"
dependencies:
playwright-core: 1.34.0
playwright-core: 1.33.0
bin:
playwright: cli.js
checksum: 5e5586e7c4e42f206526fe4306e35c68b7af353d6e3701c9e1045940aa3a916ac50b0b7c27a8eaeb3831ddc4f1146a367f98a5b6b841f977e9770b925e81877f
checksum: fb3934c56ed749cf412ea35b82052f013872d7f7b7747ab7042580af473dfc1b038956a31c132ee8c0098f74150c75e2073e9c737fd4c1ed54700a23fffc079f
languageName: node
linkType: hard
@ -33521,12 +33545,12 @@ __metadata:
linkType: hard
"react-fast-marquee@npm:^1.3.5":
version: 1.6.0
resolution: "react-fast-marquee@npm:1.6.0"
version: 1.5.2
resolution: "react-fast-marquee@npm:1.5.2"
peerDependencies:
react: ">= 16.8.0 || 18.0.0"
react-dom: ">= 16.8.0 || 18.0.0"
checksum: f448341d30ef417292d8f7618d4931190a6d4aa353fbd276da4dcecc49bb6a7a32962f9cc74fe0af2ea9617d7b453ed3efccf7a0e8709a8b66e04ec04f4ae18c
checksum: 2db7a61470a9e02707aa5a80792ec009a8694ef7283b01e30ec516893297794d7f466ae3230c59773bab357684803767c1ffab875175471c41b28a5055345130
languageName: node
linkType: hard
@ -33756,6 +33780,23 @@ __metadata:
languageName: node
linkType: hard
"react-phone-input-2@npm:^2.15.1":
version: 2.15.1
resolution: "react-phone-input-2@npm:2.15.1"
dependencies:
classnames: ^2.2.6
lodash.debounce: ^4.0.8
lodash.memoize: ^4.1.2
lodash.reduce: ^4.6.0
lodash.startswith: ^4.2.1
prop-types: ^15.7.2
peerDependencies:
react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0
react-dom: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0
checksum: 60cee455c7044da648a23312cae0077b4108dd24e26173a954904123decefae9875a1f8225a722a6c4d74c37b18ad3038ec6c302b64857f75089e544e8d48847
languageName: node
linkType: hard
"react-phone-number-input@npm:^3.2.7":
version: 3.2.7
resolution: "react-phone-number-input@npm:3.2.7"