Public Booking Pages (#4063)
* Teams + /book * Consitant hoverstate across event-type cards * Fix darkmode hover on book a team member * Fix mobile wrapping * Fix mobile looking awful with long links * Fix storybook * Fix SB * Phone Input darkmode * Location radio styles darkmode * Off Topic: NIT add disable guests * Update highlights on /book and [user]/[type] * Fix preview URL * Fix hover state * Fix spacing for locations
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import AvatarGroup from "@calcom/ui/v2/core/AvatarGroup";
|
||||
@@ -52,8 +53,8 @@ const IMAGES = [
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<>
|
||||
<TooltipProvider>
|
||||
<AvatarGroup size="lg" items={IMAGES} />
|
||||
</>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,30 +8,33 @@ import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
|
||||
export default {
|
||||
title: "Button Group",
|
||||
component: ButtonGroup,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<TooltipProvider>
|
||||
<Story />
|
||||
</TooltipProvider>
|
||||
),
|
||||
],
|
||||
} as ComponentMeta<typeof ButtonGroup>;
|
||||
|
||||
export const Default = () => (
|
||||
<ButtonGroup>
|
||||
<TooltipProvider>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" />
|
||||
</TooltipProvider>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" />
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
||||
export const Combined = () => (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<TooltipProvider>
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" combined />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={ArrowLeft} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={ArrowRight} size="icon" color="secondary" combined />
|
||||
</ButtonGroup>
|
||||
</TooltipProvider>
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" combined />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={ArrowLeft} size="icon" color="secondary" combined />
|
||||
<Button StartIcon={ArrowRight} size="icon" color="secondary" combined />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,29 +6,27 @@ import { Props } from "./pages/AvailabilityPage";
|
||||
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
|
||||
return locations.length ? (
|
||||
<div>
|
||||
<div className="flex-warp dark:text-darkgray-600 mr-6 flex break-words text-sm text-gray-600">
|
||||
<p className="w-full">
|
||||
{locations.map((location) => {
|
||||
const eventLocationType = getEventLocationType(location.type);
|
||||
if (!eventLocationType) {
|
||||
// It's possible that the location app got uninstalled
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<span key={location.type} className="flex flex-row items-center text-sm font-medium">
|
||||
<img
|
||||
src={eventLocationType.iconUrl}
|
||||
className={classNames(
|
||||
"mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100",
|
||||
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
|
||||
)}
|
||||
alt={`${eventLocationType.label} icon`}
|
||||
/>
|
||||
<span key={location.type}>{locationKeyToString(location)} </span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-2 break-words text-sm text-gray-600">
|
||||
{locations.map((location) => {
|
||||
const eventLocationType = getEventLocationType(location.type);
|
||||
if (!eventLocationType) {
|
||||
// It's possible that the location app got uninstalled
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div key={location.type} className="flex flex-row items-center text-sm font-medium">
|
||||
<img
|
||||
src={eventLocationType.iconUrl}
|
||||
className={classNames(
|
||||
"mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100 ",
|
||||
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
|
||||
)}
|
||||
alt={`${eventLocationType.label} icon`}
|
||||
/>
|
||||
<span key={location.type}>{locationKeyToString(location)} </span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -97,8 +97,8 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
||||
<Link href={bookingUrl} prefetch={false}>
|
||||
<a
|
||||
className={classNames(
|
||||
"text-primary-500 hover:bg-brand hover:text-brandcontrast dark:hover:bg-darkmodebrand",
|
||||
"dark:hover:text-darkmodebrandcontrast dark:bg-darkgray-200 dark:hover:border-darkgray-900 mb-2 block rounded-md border bg-white py-2.5 text-sm font-medium hover:text-white dark:border-transparent dark:text-neutral-200",
|
||||
"text-primary-500 hover:border-gray-900 hover:bg-gray-50",
|
||||
"dark:bg-darkgray-200 dark:hover:bg-darkgray-300 dark:hover:border-darkmodebrand mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent dark:text-neutral-200",
|
||||
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
||||
)}
|
||||
data-testid="time">
|
||||
|
||||
@@ -33,7 +33,7 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
|
||||
};
|
||||
|
||||
return selectedTimeZone !== "" ? (
|
||||
<div className="max-w-80 absolute z-10 w-full rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm dark:border-0 dark:bg-gray-700">
|
||||
<div className="max-w-80 dark:border-darkgray-300 dark:bg-darkgray-200 absolute z-10 w-full rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
|
||||
<div className="mb-4 flex">
|
||||
<div className="text-sm font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
|
||||
@@ -468,7 +468,7 @@ const BookingPage = ({
|
||||
"dark:border-darkgray-300 rounded-md sm:border"
|
||||
)}>
|
||||
<div className="sm:flex">
|
||||
<div className="sm:dark:border-darkgray-300 dark:text-darkgray-600 flex flex-col space-y-2 px-6 pt-6 pb-0 text-gray-600 sm:w-1/2 sm:border-r sm:pb-6">
|
||||
<div className="sm:dark:border-darkgray-300 dark:text-darkgray-600 flex flex-col px-6 pt-6 pb-0 text-gray-600 sm:w-1/2 sm:border-r sm:pb-6">
|
||||
<UserAvatars
|
||||
profile={profile}
|
||||
users={eventType.users}
|
||||
@@ -479,41 +479,26 @@ const BookingPage = ({
|
||||
<h2 className="mt-2 break-words text-sm font-medium text-gray-500 dark:text-gray-300">
|
||||
{profile.name}
|
||||
</h2>
|
||||
<h1 className="font-cal dark:text-darkgray-900 mb-6 break-words text-2xl text-gray-900 ">
|
||||
<h1 className="font-cal dark:text-darkgray-900 break-words text-2xl text-gray-900 ">
|
||||
{eventType.title}
|
||||
</h1>
|
||||
{!!eventType.seatsPerTimeSlot && (
|
||||
<p
|
||||
className={`${
|
||||
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
|
||||
? "text-rose-600"
|
||||
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
|
||||
? "text-yellow-500"
|
||||
: "text-emerald-400"
|
||||
} mb-2`}>
|
||||
{booking
|
||||
? eventType.seatsPerTimeSlot - booking.attendees.length
|
||||
: eventType.seatsPerTimeSlot}{" "}
|
||||
/ {eventType.seatsPerTimeSlot} {t("seats_available")}
|
||||
</p>
|
||||
)}
|
||||
{eventType?.description && (
|
||||
<div className="dark:text-darkgray-600 flex py-1 text-sm font-medium text-gray-600">
|
||||
<div>
|
||||
<Icon.FiInfo className="dark:text-darkgray-600 mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
|
||||
<div className="mt-4 flex flex-col space-y-3 lg:mt-9">
|
||||
{eventType?.description && (
|
||||
<div className="dark:text-darkgray-600 flex text-sm font-medium text-gray-600">
|
||||
<div>
|
||||
<Icon.FiInfo className="dark:text-darkgray-600 mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
|
||||
</div>
|
||||
<EventTypeDescriptionSafeHTML eventType={eventType} />
|
||||
</div>
|
||||
<EventTypeDescriptionSafeHTML eventType={eventType} />
|
||||
</div>
|
||||
)}
|
||||
{eventType?.requiresConfirmation && (
|
||||
<div className="dark:text-darkgray-600 flex items-center text-sm font-medium text-gray-600">
|
||||
<div>
|
||||
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 " />
|
||||
)}
|
||||
{eventType?.requiresConfirmation && (
|
||||
<div className="dark:text-darkgray-600 flex items-center text-sm font-medium text-gray-600">
|
||||
<div>
|
||||
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 " />
|
||||
</div>
|
||||
{t("requires_confirmation")}
|
||||
</div>
|
||||
{t("requires_confirmation")}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col space-y-2">
|
||||
)}
|
||||
<p className="dark:text-darkgray-600 text-sm font-medium text-gray-600">
|
||||
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4" />
|
||||
{eventType.length} {t("minutes")}
|
||||
@@ -580,6 +565,24 @@ const BookingPage = ({
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{!!eventType.seatsPerTimeSlot && (
|
||||
<div className="text-bookinghighlight flex items-start text-sm">
|
||||
<Icon.FiUser className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
|
||||
<p
|
||||
className={`${
|
||||
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
|
||||
? "text-rose-600"
|
||||
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
|
||||
? "text-yellow-500"
|
||||
: "text-bookinghighlight"
|
||||
} mb-2 font-medium`}>
|
||||
{booking
|
||||
? eventType.seatsPerTimeSlot - booking.attendees.length
|
||||
: eventType.seatsPerTimeSlot}{" "}
|
||||
/ {eventType.seatsPerTimeSlot} {t("seats_available")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 sm:w-1/2">
|
||||
@@ -645,12 +648,12 @@ const BookingPage = ({
|
||||
<input
|
||||
type="radio"
|
||||
disabled={!!disableLocations}
|
||||
className="location h-4 w-4 border-gray-300 text-black focus:ring-black ltr:mr-2 rtl:ml-2"
|
||||
className="location dark:bg-darkgray-300 dark:border-darkgray-300 h-4 w-4 border-gray-300 text-black focus:ring-black ltr:mr-2 rtl:ml-2"
|
||||
{...bookingForm.register("locationType", { required: true })}
|
||||
value={location.type}
|
||||
defaultChecked={defaultChecked}
|
||||
/>
|
||||
<span className="text-sm ltr:ml-2 rtl:mr-2 dark:text-gray-500">
|
||||
<span className="text-sm ltr:ml-2 rtl:mr-2 dark:text-white">
|
||||
{locationKeyToString(location)}
|
||||
</span>
|
||||
</label>
|
||||
@@ -874,7 +877,15 @@ const BookingPage = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-2 rtl:space-x-reverse">
|
||||
<div className="flex justify-end space-x-2 rtl:space-x-reverse">
|
||||
<Button
|
||||
color="secondary"
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
// We override this for this component only for now - as we don't support darkmode everywhere in the app
|
||||
className="dark:border-none">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast rounded-md"
|
||||
@@ -882,13 +893,6 @@ const BookingPage = ({
|
||||
loading={mutation.isLoading || recurringMutation.isLoading}>
|
||||
{rescheduleUid ? t("reschedule") : t("confirm")}
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
className="rounded-md">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
{(mutation.isError || recurringMutation.isError) && (
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import classnames from "classnames";
|
||||
import Link from "next/link";
|
||||
import { TeamPageProps } from "pages/team/[slug]";
|
||||
import React from "react";
|
||||
@@ -6,11 +5,10 @@ import React from "react";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Avatar } from "@calcom/ui/v2";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
|
||||
type TeamType = TeamPageProps["team"];
|
||||
type MembersType = TeamType["members"];
|
||||
type MemberType = MembersType[number];
|
||||
@@ -19,47 +17,20 @@ const Team = ({ team }: TeamPageProps) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const Member = ({ member }: { member: MemberType }) => {
|
||||
const classes = classnames(
|
||||
"group",
|
||||
"relative",
|
||||
"flex flex-col",
|
||||
"space-y-4",
|
||||
"p-4",
|
||||
"min-w-full sm:min-w-64 sm:max-w-64",
|
||||
"bg-white dark:bg-neutral-800 dark:border-neutral-700 dark:bg-opacity-8",
|
||||
"border border-neutral-200",
|
||||
"hover:cursor-pointer",
|
||||
"hover:border-brand dark:border-neutral-700 dark:hover:border-neutral-600",
|
||||
"rounded-sm",
|
||||
"hover:shadow-md"
|
||||
);
|
||||
|
||||
return (
|
||||
<Link key={member.id} href={`/${member.username}`}>
|
||||
<div className={classes}>
|
||||
<Icon.FiArrowRight
|
||||
className={classnames(
|
||||
"text-black dark:text-white",
|
||||
"absolute top-4 right-4",
|
||||
"h-4 w-4",
|
||||
"transition-opacity",
|
||||
"opacity-0 group-hover:block group-hover:opacity-100"
|
||||
)}
|
||||
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
|
||||
<Avatar
|
||||
size="md"
|
||||
alt={member.name || ""}
|
||||
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Avatar
|
||||
alt={member.name || ""}
|
||||
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
|
||||
className="-mt-4 h-12 w-12"
|
||||
/>
|
||||
<section className="line-clamp-4 mt-2 w-full space-y-1">
|
||||
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
|
||||
<p className="text-sm font-normal text-neutral-500 dark:text-white">
|
||||
{member.bio || t("user_from_team", { user: member.name, team: team.name })}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
<section className="line-clamp-4 mt-2 w-full space-y-1">
|
||||
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
|
||||
<p className="line-clamp-3 overflow-ellipsis text-sm font-normal text-neutral-500 dark:text-white">
|
||||
{member.bio || t("user_from_team", { user: member.name, team: team.name })}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
@@ -82,13 +53,6 @@ const Team = ({ team }: TeamPageProps) => {
|
||||
return (
|
||||
<div>
|
||||
<Members members={team.members} />
|
||||
{team.eventTypes.length > 0 && (
|
||||
<aside className="mt-8 text-center dark:text-white">
|
||||
<Button color="secondary" href={`/team/${team.slug}`} shallow={true} StartIcon={Icon.FiArrowLeft}>
|
||||
{t("go_back")}
|
||||
</Button>
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
|
||||
|
||||
const animationRef = useRef(null);
|
||||
const seatsEnabled = !!eventType.seatsPerTimeSlot;
|
||||
|
||||
useEffect(() => {
|
||||
animationRef.current && autoAnimate(animationRef.current);
|
||||
@@ -172,7 +173,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
defaultValue={eventType.requiresConfirmation}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<div className="flex space-x-3 ">
|
||||
<Switch name="requireConfirmation" checked={value} onCheckedChange={(e) => onChange(e)} />
|
||||
<Switch
|
||||
name="requireConfirmation"
|
||||
checked={value}
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
disabled={seatsEnabled}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">
|
||||
{t("requires_confirmation")}
|
||||
@@ -184,6 +190,27 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<hr />
|
||||
<Controller
|
||||
name="disableGuests"
|
||||
control={formMethods.control}
|
||||
defaultValue={eventType.disableGuests}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<div className="flex space-x-3 ">
|
||||
<Switch
|
||||
name="disableGuests"
|
||||
checked={value}
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
disabled={seatsEnabled}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("disable_guests")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("disable_guests_description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<hr />
|
||||
<Controller
|
||||
name="hideCalendarNotes"
|
||||
|
||||
@@ -171,7 +171,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
||||
<div
|
||||
key={type.id}
|
||||
style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
|
||||
className="dark:bg-darkgray-100 group relative border-b border-neutral-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-white dark:border-neutral-700 dark:hover:border-neutral-600">
|
||||
className="dark:bg-darkgray-100 group relative border-b border-neutral-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50 dark:border-neutral-700 dark:hover:border-neutral-600">
|
||||
<Icon.FiArrowRight className="absolute right-4 top-4 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
||||
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
|
||||
<Link
|
||||
@@ -198,11 +198,11 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
||||
}}
|
||||
className="block w-full p-5"
|
||||
data-testid="event-type-link">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h2 className="dark:text-darkgray-700 text-sm font-semibold text-gray-700">
|
||||
<div className="flex flex-wrap items-center">
|
||||
<h2 className="dark:text-darkgray-700 pr-2 text-sm font-semibold text-gray-700">
|
||||
{type.title}
|
||||
</h2>
|
||||
<p className="dark:text-darkgray-600 text-sm font-normal leading-none text-gray-600">{`/${user.username}/${type.slug}`}</p>
|
||||
<p className="dark:text-darkgray-600 hidden text-sm font-normal leading-none text-gray-600 md:block">{`/${user.username}/${type.slug}`}</p>
|
||||
</div>
|
||||
<EventTypeDescription eventType={type} />
|
||||
</a>
|
||||
|
||||
@@ -12,6 +12,7 @@ import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Avatar } from "@calcom/ui/v2";
|
||||
import { Button } from "@calcom/ui/v2/core";
|
||||
import EventTypeDescription from "@calcom/ui/v2/modules/event-types/EventTypeDescription";
|
||||
|
||||
@@ -22,7 +23,6 @@ import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
import { HeadSeo } from "@components/seo/head-seo";
|
||||
import Team from "@components/team/screens/Team";
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
import AvatarGroup from "@components/ui/AvatarGroup";
|
||||
|
||||
export type TeamPageProps = inferSSRProps<typeof getServerSideProps>;
|
||||
@@ -42,25 +42,27 @@ function TeamPage({ team }: TeamPageProps) {
|
||||
);
|
||||
}, [telemetry, router.asPath]);
|
||||
|
||||
const eventTypes = (
|
||||
const EventTypes = () => (
|
||||
<ul className="">
|
||||
{team.eventTypes.map((type) => (
|
||||
{team.eventTypes.map((type, index) => (
|
||||
<li
|
||||
key={type.id}
|
||||
key={index}
|
||||
className={classNames(
|
||||
"hover:border-brand dark:bg-darkgray-200 group relative border-b border-neutral-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-white dark:border-neutral-700 dark:hover:border-neutral-600",
|
||||
isEmbed ? "" : "bg-white"
|
||||
"dark:bg-darkgray-100 dark:border-darkgray-200 group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:hover:border-neutral-600",
|
||||
!isEmbed && "bg-white"
|
||||
)}>
|
||||
<Icon.FiArrowRight className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
||||
<Link href={`${team.slug}/${type.slug}`}>
|
||||
<a className="flex justify-between p-5">
|
||||
<a className="flex justify-between px-6 py-4" data-testid="event-type-link">
|
||||
<div className="flex-shrink">
|
||||
<h2 className="font-cal font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
|
||||
<div className="flex flex-wrap items-center space-x-2">
|
||||
<h2 className="dark:text-darkgray-700 text-sm font-semibold text-gray-700">{type.title}</h2>
|
||||
<p className="dark:text-darkgray-600 hidden text-sm font-normal leading-none text-gray-600 md:block">{`/${team.slug}/${type.slug}`}</p>
|
||||
</div>
|
||||
<EventTypeDescription className="text-sm" eventType={type} />
|
||||
</div>
|
||||
<div className="mt-1 self-center">
|
||||
<AvatarGroup
|
||||
border="border-2 border-white dark:border-neutral-800"
|
||||
border="border-2 border-white"
|
||||
truncateAfter={4}
|
||||
className="flex flex-shrink-0"
|
||||
size={10}
|
||||
@@ -83,20 +85,20 @@ function TeamPage({ team }: TeamPageProps) {
|
||||
return (
|
||||
<div>
|
||||
<HeadSeo title={teamName} description={teamName} />
|
||||
<div className="dark:bg-darkgray-50 h-screen rounded-md bg-gray-100 px-4 pt-24 pb-12">
|
||||
<div className="dark:bg-darkgray-50 h-screen rounded-md bg-gray-100 px-4 pt-12 pb-12">
|
||||
<div className="max-w-96 mx-auto mb-8 text-center">
|
||||
<Avatar
|
||||
alt={teamName}
|
||||
imageSrc={getPlaceholderAvatar(team.logo, team.name)}
|
||||
className="mx-auto mb-4 h-20 w-20 rounded-full"
|
||||
/>
|
||||
<p className="font-cal mb-2 text-3xl tracking-wider text-gray-900 dark:text-white">{teamName}</p>
|
||||
<p className="mt-2 text-sm font-normal text-neutral-500 dark:text-white">{team.bio}</p>
|
||||
<Avatar alt={teamName} imageSrc={getPlaceholderAvatar(team.logo, team.name)} size="lg" />
|
||||
<p className="font-cal dark:text-darkgray-900 mb-2 text-2xl tracking-wider text-gray-900">
|
||||
{teamName}
|
||||
</p>
|
||||
<p className="dark:text-darkgray-500 mt-2 text-sm font-normal text-gray-500">{team.bio}</p>
|
||||
</div>
|
||||
{(showMembers.isOn || !team.eventTypes.length) && <Team team={team} />}
|
||||
{!showMembers.isOn && team.eventTypes.length > 0 && (
|
||||
<div className="mx-auto max-w-3xl ">
|
||||
<div className="dark:border-darkgray-300 rounded-md border">{eventTypes}</div>
|
||||
<div className="dark:border-darkgray-300 rounded-md border">
|
||||
<EventTypes />
|
||||
</div>
|
||||
<div className="relative mt-12">
|
||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||
<div className="dark:border-darkgray-300 w-full border-t border-gray-200" />
|
||||
@@ -110,8 +112,9 @@ function TeamPage({ team }: TeamPageProps) {
|
||||
|
||||
<aside className="mt-8 text-center dark:text-white">
|
||||
<Button
|
||||
color="secondary"
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiArrowRight}
|
||||
className="dark:hover:bg-darkgray-200"
|
||||
href={`/team/${team.slug}?members=1`}
|
||||
shallow={true}>
|
||||
{t("book_a_team_member")}
|
||||
|
||||
@@ -307,6 +307,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
<Tooltip content={t("preview") as string}>
|
||||
<Button
|
||||
color="minimalSecondary"
|
||||
target="_blank"
|
||||
size="icon"
|
||||
href={calLink}
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -19,8 +19,10 @@ function PhoneInput<FormValues>({ control, name, className, ...rest }: PhoneInpu
|
||||
name={name}
|
||||
control={control}
|
||||
countrySelectProps={{ className: "text-black" }}
|
||||
numberInputProps={{ className: "border-0 text-sm focus:ring-0 dark:bg-gray-700" }}
|
||||
className={`${className} border-1 focus-within:border-brand block w-full rounded-sm border border-gray-300 py-px pl-3 ring-black focus-within:ring-1 disabled:text-gray-500 disabled:opacity-50 dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500`}
|
||||
numberInputProps={{
|
||||
className: "border-0 text-sm focus:ring-0 dark:bg-darkgray-100 dark:placeholder:text-darkgray-600",
|
||||
}}
|
||||
className={`${className} border-1 focus-within:border-brand dark:bg-darkgray-100 dark:border-darkgray-300 block w-full rounded-md rounded-sm border border-gray-300 py-px pl-3 ring-black focus-within:ring-1 disabled:text-gray-500 disabled:opacity-50 dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import BaseSelect, {
|
||||
Props as SelectProps,
|
||||
} from "react-timezone-select";
|
||||
|
||||
import { InputComponent } from "./Select";
|
||||
import { InputComponent } from "@calcom/ui/v2/core/form/Select";
|
||||
|
||||
function TimezoneSelect({ className, ...props }: SelectProps) {
|
||||
return (
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Avatar(props: AvatarProps) {
|
||||
<AvatarPrimitive.Root
|
||||
className={classNames(
|
||||
sizeClassname,
|
||||
"relative inline-block aspect-square overflow-hidden rounded-full bg-gray-300"
|
||||
"dark:bg-darkgray-300 relative inline-block aspect-square overflow-hidden rounded-full bg-gray-300"
|
||||
)}>
|
||||
<AvatarPrimitive.Image src={imageSrc ?? undefined} alt={alt} className={rootClass} />
|
||||
<AvatarPrimitive.Fallback delayMs={600}>
|
||||
|
||||
@@ -8,6 +8,9 @@ import classNames from "@calcom/lib/classNames";
|
||||
const Switch = (
|
||||
props: React.ComponentProps<typeof PrimitiveSwitch.Root> & {
|
||||
label?: string;
|
||||
thumbProps?: {
|
||||
className?: string;
|
||||
};
|
||||
}
|
||||
) => {
|
||||
const { label, ...primitiveProps } = props;
|
||||
@@ -18,14 +21,18 @@ const Switch = (
|
||||
<PrimitiveSwitch.Root
|
||||
className={classNames(
|
||||
props.checked ? "bg-gray-900" : "bg-gray-200 hover:bg-gray-300",
|
||||
"focus:ring-brand-800 h-[24px] w-[40px] rounded-full p-[3px] shadow-inner"
|
||||
"focus:ring-brand-800 h-[24px] w-[40px] rounded-full p-[3px] shadow-none",
|
||||
props.className
|
||||
)}
|
||||
{...primitiveProps}>
|
||||
<PrimitiveSwitch.Thumb
|
||||
id={id}
|
||||
// Since we dont support global dark mode - we have to style dark mode components specifically on the instance for now
|
||||
// TODO: Remove once we support global dark mode
|
||||
className={classNames(
|
||||
"block h-[18px] w-[18px] translate-x-0 rounded-full bg-white shadow-md transition-transform",
|
||||
props.checked && "shadow-inner"
|
||||
"block h-[18px] w-[18px] translate-x-0 rounded-full bg-white transition-transform",
|
||||
props.checked && "shadow-inner",
|
||||
props.thumbProps?.className
|
||||
)}
|
||||
/>
|
||||
</PrimitiveSwitch.Root>
|
||||
|
||||
@@ -43,7 +43,7 @@ export const Day = ({
|
||||
<button
|
||||
style={props.disabled ? { ...disabledDateButtonEmbedStyles } : { ...enabledDateButtonEmbedStyles }}
|
||||
className={classNames(
|
||||
"disabled:text-bookinglighter absolute top-0 left-0 right-0 bottom-0 mx-auto w-full rounded-md border-2 border-transparent text-center font-medium disabled:cursor-default disabled:border-transparent disabled:font-light dark:hover:border-white disabled:dark:border-transparent",
|
||||
"disabled:text-bookinglighter dark:hover:border-darkmodebrand absolute top-0 left-0 right-0 bottom-0 mx-auto w-full rounded-md border-2 border-transparent text-center font-medium disabled:cursor-default disabled:border-transparent disabled:font-light disabled:dark:border-transparent",
|
||||
active
|
||||
? "dark:bg-darkmodebrand dark:text-darkmodebrandcontrast bg-brand text-brandcontrast border-2"
|
||||
: !props.disabled
|
||||
@@ -89,7 +89,7 @@ const Days = ({
|
||||
<div key={`e-${idx}`} />
|
||||
) : props.isLoading ? (
|
||||
<button
|
||||
className="absolute top-0 left-0 right-0 bottom-0 mx-auto flex w-full items-center justify-center rounded-md border-transparent bg-gray-50 text-center text-gray-400 opacity-50 dark:bg-black dark:text-gray-600/40"
|
||||
className=" dark:bg-darkgray-200 absolute top-0 left-0 right-0 bottom-0 mx-auto flex w-full items-center justify-center rounded-sm border-transparent bg-gray-50 text-center text-gray-400 opacity-50 dark:text-gray-400"
|
||||
key={`e-${idx}`}
|
||||
disabled>
|
||||
<SkeletonText width="5" height="4" />
|
||||
|
||||
Reference in New Issue
Block a user