feat: Assign colors to events (#15298)

* feat: Assign colors to events

* update

* final update

* update

* fix requested changes error

* Update EventAdvancedTab.tsx

* add contrast check

* update

* fix contrsterror message visibility

* fix type error

* fix

* update test

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
This commit is contained in:
Anik Dhabal Babu
2024-08-14 15:10:40 +02:00
committed by GitHub
co-authored by Udit Takkar sean-brydon
parent 95e2ad3007
commit 528a4fbb97
23 changed files with 289 additions and 120 deletions
@@ -17,12 +17,14 @@ import { DuplicateDialog } from "@calcom/features/eventtypes/components/Duplicat
import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter";
import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery";
import Shell from "@calcom/features/shell/Shell";
import { parseEventTypeColor } from "@calcom/lib";
import { APP_NAME } from "@calcom/lib/constants";
import { WEBSITE_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { useGetTheme } from "@calcom/lib/hooks/useTheme";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { HttpError } from "@calcom/lib/http-error";
import type { User } from "@calcom/prisma/client";
@@ -213,6 +215,11 @@ const Item = ({
readOnly: boolean;
}) => {
const { t } = useLocale();
const { resolvedTheme, forcedTheme } = useGetTheme();
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
const parsedeventTypeColor = parseEventTypeColor(type.eventTypeColor);
const eventTypeColor =
parsedeventTypeColor && parsedeventTypeColor[hasDarkTheme ? "darkEventTypeColor" : "lightEventTypeColor"];
const content = () => (
<div>
@@ -238,40 +245,46 @@ const Item = ({
</div>
);
return readOnly ? (
<div className="flex-1 overflow-hidden pr-4 text-sm">
{content()}
<EventTypeDescription eventType={type} shortenDescription />
</div>
) : (
<Link
href={`/event-types/${type.id}?tabName=setup`}
className="flex-1 overflow-hidden pr-4 text-sm"
title={type.title}>
<div>
<span
className="text-default font-semibold ltr:mr-1 rtl:ml-1"
data-testid={`event-type-title-${type.id}`}>
{type.title}
</span>
{group.profile.slug ? (
<small
className="text-subtle hidden font-normal leading-4 sm:inline"
data-testid={`event-type-slug-${type.id}`}>
{`/${group.profile.slug}/${type.slug}`}
</small>
) : null}
{readOnly && (
<Badge variant="gray" className="ml-2" data-testid="readonly-badge">
{t("readonly")}
</Badge>
return (
<div className="relative flex-1 overflow-hidden pr-4 text-sm">
{eventTypeColor && (
<div className="absolute h-full w-0.5" style={{ backgroundColor: eventTypeColor }} />
)}
<div className="ml-3">
{readOnly ? (
<div>
{content()}
<EventTypeDescription eventType={type} shortenDescription />
</div>
) : (
<Link href={`/event-types/${type.id}?tabName=setup`} title={type.title}>
<div>
<span
className="text-default font-semibold ltr:mr-1 rtl:ml-1"
data-testid={`event-type-title-${type.id}`}>
{type.title}
</span>
{group.profile.slug ? (
<small
className="text-subtle hidden font-normal leading-4 sm:inline"
data-testid={`event-type-slug-${type.id}`}>
{`/${group.profile.slug}/${type.slug}`}
</small>
) : null}
{readOnly && (
<Badge variant="gray" className="ml-2" data-testid="readonly-badge">
{t("readonly")}
</Badge>
)}
</div>
<EventTypeDescription
eventType={{ ...type, descriptionAsSafeHTML: type.safeDescription }}
shortenDescription
/>
</Link>
)}
</div>
<EventTypeDescription
eventType={{ ...type, descriptionAsSafeHTML: type.safeDescription }}
shortenDescription
/>
</Link>
</div>
);
};
@@ -470,7 +483,7 @@ export const EventTypeList = ({
return (
<li key={type.id}>
<div className="hover:bg-muted flex w-full items-center justify-between transition">
<div className="group flex w-full max-w-full items-center justify-between overflow-hidden px-4 py-4 sm:px-6">
<div className="group flex w-full max-w-full items-center justify-between overflow-hidden py-4 pl-2 pr-4 sm:pl-3 sm:pr-6">
{!(firstItem && firstItem.id === type.id) && (
<ArrowButton onClick={() => moveEventType(index, -1)} arrowDirection="up" />
)}
@@ -282,6 +282,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
length: eventType.length,
hidden: eventType.hidden,
hashedLink: eventType.hashedLink?.link || undefined,
eventTypeColor: eventType.eventTypeColor || null,
periodDates: {
startDate: periodDates.startDate,
endDate: periodDates.endDate,
@@ -518,6 +519,8 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
const updatedFields: Partial<FormValues> = {};
Object.keys(dirtyFields).forEach((key) => {
const typedKey = key as keyof typeof dirtyFields;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
updatedFields[typedKey] = undefined;
const isDirty = isFieldDirty(typedKey);
if (isDirty) {
@@ -545,6 +548,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
onlyShowFirstAvailableSlot,
durationLimits,
recurringEvent,
eventTypeColor,
locations,
metadata,
customInputs,
@@ -615,6 +619,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
bookingLimits,
onlyShowFirstAvailableSlot,
durationLimits,
eventTypeColor,
seatsPerTimeSlot,
seatsShowAttendees,
seatsShowAvailabilityCount,
@@ -703,6 +708,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
onlyShowFirstAvailableSlot,
durationLimits,
recurringEvent,
eventTypeColor,
locations,
metadata,
customInputs,
@@ -765,6 +771,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf
bookingLimits,
onlyShowFirstAvailableSlot,
durationLimits,
eventTypeColor,
seatsPerTimeSlot,
seatsShowAttendees,
seatsShowAvailabilityCount,