Skip to content

Commit bc9a9c0

Browse files
Recxsmacxcursoragentromitg2
authored
fix: Organised the OOO page (calcom#27865)
* fixed text issue * matched style of api keys page * organised the out of office page * Clean up unused imports in OutOfOfficeEntriesList Removed unused imports from OutOfOfficeEntriesList.tsx * fixed import issues * remove unused imports --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Romit <romitgabani1.work@gmail.com> Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
1 parent f8abce4 commit bc9a9c0

2 files changed

Lines changed: 155 additions & 141 deletions

File tree

apps/web/modules/settings/my-account/holidays-view.tsx

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"use client";
22

3-
import { memo, useMemo, useCallback } from "react";
4-
53
import dayjs from "@calcom/dayjs";
64
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
75
import { getHolidayEmoji } from "@calcom/lib/holidays/getHolidayEmoji";
@@ -10,12 +8,12 @@ import type { RouterOutputs } from "@calcom/trpc/react";
108
import { trpc } from "@calcom/trpc/react";
119
import { Alert } from "@calcom/ui/components/alert";
1210
import { Button } from "@calcom/ui/components/button";
13-
import { Select } from "@calcom/ui/components/form";
14-
import { Switch } from "@calcom/ui/components/form";
11+
import { Select, Switch } from "@calcom/ui/components/form";
12+
import { Icon } from "@calcom/ui/components/icon";
1513
import { SkeletonContainer, SkeletonText } from "@calcom/ui/components/skeleton";
16-
import { CalendarIcon, TriangleAlertIcon } from "@coss/ui/icons";
14+
import { TriangleAlertIcon } from "@coss/ui/icons";
1715
import { showToast } from "@calcom/ui/components/toast";
18-
16+
import { memo, useCallback, useMemo } from "react";
1917
import { OutOfOfficeToggleGroup } from "~/settings/outOfOffice/OutOfOfficeToggleGroup";
2018

2119
function HolidaysCTA() {
@@ -244,25 +242,37 @@ export function HolidaysView() {
244242

245243
if (isLoading) {
246244
return (
247-
<SettingsHeader title={t("holidays")} description={t("holidays_description")} CTA={<HolidaysCTA />}>
248-
<SkeletonContainer>
249-
<div className="space-y-4">
250-
<SkeletonText className="h-10 w-64" />
251-
<SkeletonText className="h-64 w-full" />
252-
</div>
253-
</SkeletonContainer>
245+
<SettingsHeader
246+
title={t("holidays")}
247+
description={t("holidays_description")}
248+
borderInShellHeader={true}
249+
CTA={<HolidaysCTA />}>
250+
<div className="border-subtle rounded-b-lg border border-t-0 px-4 py-6 sm:px-6">
251+
<SkeletonContainer>
252+
<div className="space-y-4">
253+
<SkeletonText className="h-10 w-64" />
254+
<SkeletonText className="h-64 w-full" />
255+
</div>
256+
</SkeletonContainer>
257+
</div>
254258
</SettingsHeader>
255259
);
256260
}
257261

258262
if (hasError) {
259263
return (
260-
<SettingsHeader title={t("holidays")} description={t("holidays_description")} CTA={<HolidaysCTA />}>
261-
<Alert
262-
severity="error"
263-
title={t("something_went_wrong")}
264-
message={countriesError?.message || settingsError?.message}
265-
/>
264+
<SettingsHeader
265+
title={t("holidays")}
266+
description={t("holidays_description")}
267+
borderInShellHeader={true}
268+
CTA={<HolidaysCTA />}>
269+
<div className="border-subtle rounded-b-lg border border-t-0 px-4 py-6 sm:px-6">
270+
<Alert
271+
severity="error"
272+
title={t("something_went_wrong")}
273+
message={countriesError?.message || settingsError?.message}
274+
/>
275+
</div>
266276
</SettingsHeader>
267277
);
268278
}
@@ -271,57 +281,60 @@ export function HolidaysView() {
271281
<SettingsHeader
272282
title={t("out_of_office")}
273283
description={t("out_of_office_description")}
284+
borderInShellHeader={true}
274285
CTA={<HolidaysCTA />}>
275-
<div className="space-y-6">
276-
{conflictsData?.conflicts && conflictsData.conflicts.length > 0 && (
277-
<ConflictWarning conflicts={conflictsData.conflicts} />
278-
)}
286+
<div className="border-subtle rounded-b-lg border border-t-0 px-4 py-6 sm:px-6">
287+
<div className="space-y-6">
288+
{conflictsData?.conflicts && conflictsData.conflicts.length > 0 && (
289+
<ConflictWarning conflicts={conflictsData.conflicts} />
290+
)}
279291

280-
<div className="border-subtle bg-muted overflow-hidden rounded-lg border p-5">
281-
{/* Header with title and country selector */}
282-
<div className="mb-4 flex items-center justify-between">
283-
<div>
284-
<h3 className="text-emphasis font-semibold">{t("holidays")}</h3>
285-
<p className="text-subtle text-sm">{t("holidays_description")}</p>
292+
<div className="border-subtle bg-muted overflow-hidden rounded-lg border p-5">
293+
{/* Header with title and country selector */}
294+
<div className="mb-4 flex items-center justify-between">
295+
<div>
296+
<h3 className="text-emphasis font-semibold">{t("holidays")}</h3>
297+
<p className="text-subtle text-sm">{t("holidays_description")}</p>
298+
</div>
299+
<CountrySelector
300+
countries={countries || []}
301+
value={settings?.countryCode || ""}
302+
onChange={handleCountryChange}
303+
isLoading={isLoadingCountries}
304+
/>
286305
</div>
287-
<CountrySelector
288-
countries={countries || []}
289-
value={settings?.countryCode || ""}
290-
onChange={handleCountryChange}
291-
isLoading={isLoadingCountries}
292-
/>
293-
</div>
294306

295-
{/* Holidays list - inner container */}
296-
{settings?.countryCode ? (
297-
<div className="border-subtle bg-default overflow-hidden rounded-md border">
298-
{settings.holidays && settings.holidays.length > 0 ? (
299-
settings.holidays.map((holiday) => (
300-
<HolidayListItem
301-
key={holiday.id}
302-
holiday={holiday}
303-
onToggle={handleToggleHoliday}
304-
isToggling={
305-
toggleHolidayMutation.isPending &&
306-
toggleHolidayMutation.variables?.holidayId === holiday.id
307-
}
308-
/>
309-
))
310-
) : (
311-
<div className="text-subtle py-8 text-center text-sm">
312-
{t("no_holidays_found_for_country")}
307+
{/* Holidays list - inner container */}
308+
{settings?.countryCode ? (
309+
<div className="border-subtle bg-default overflow-hidden rounded-md border justify-between">
310+
{settings.holidays && settings.holidays.length > 0 ? (
311+
settings.holidays.map((holiday) => (
312+
<HolidayListItem
313+
key={holiday.id}
314+
holiday={holiday}
315+
onToggle={handleToggleHoliday}
316+
isToggling={
317+
toggleHolidayMutation.isPending &&
318+
toggleHolidayMutation.variables?.holidayId === holiday.id
319+
}
320+
/>
321+
))
322+
) : (
323+
<div className="text-subtle py-8 text-center text-sm">
324+
{t("no_holidays_found_for_country")}
325+
</div>
326+
)}
327+
</div>
328+
) : (
329+
<div className="bg-default flex flex-col items-center rounded-md py-14 text-center">
330+
<div className="bg-emphasis mb-4 flex h-16 w-16 items-center justify-center rounded-full">
331+
<Icon name="calendar" className="text-default h-8 w-8" />
313332
</div>
314-
)}
315-
</div>
316-
) : (
317-
<div className="bg-default flex flex-col items-center rounded-md py-14 text-center">
318-
<div className="bg-emphasis mb-4 flex h-16 w-16 items-center justify-center rounded-full">
319-
<CalendarIcon className="text-default h-8 w-8" />
333+
<h4 className="text-emphasis mb-1 font-medium">{t("no_holidays_selected")}</h4>
334+
<p className="text-subtle text-sm">{t("select_country_to_see_holidays")}</p>
320335
</div>
321-
<h4 className="text-emphasis mb-1 font-medium">{t("no_holidays_selected")}</h4>
322-
<p className="text-subtle text-sm">{t("select_country_to_see_holidays")}</p>
323-
</div>
324-
)}
336+
)}
337+
</div>
325338
</div>
326339
</div>
327340
</SettingsHeader>

apps/web/modules/settings/outOfOffice/OutOfOfficeEntriesList.tsx

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,21 @@ export default function OutOfOfficeEntriesList({
7878
<SettingsHeader
7979
title={t("out_of_office")}
8080
description={t("out_of_office_description")}
81+
borderInShellHeader={true}
8182
CTA={
8283
<div className="flex gap-2">
8384
<OutOfOfficeToggleGroup />
8485
<CreateNewOutOfOfficeEntryButton data-testid="add_entry_ooo" onClick={onOpenCreateDialog} />
8586
</div>
8687
}>
87-
<DataTableProvider tableIdentifier={pathname} useSegments={useSegments}>
88-
<OutOfOfficeEntriesListContent
89-
onOpenCreateDialog={onOpenCreateDialog}
90-
onOpenEditDialog={onOpenEditDialog}
91-
/>
92-
</DataTableProvider>
88+
<div className="border-subtle rounded-b-lg border border-t-0 px-4 py-6 sm:px-6">
89+
<DataTableProvider tableIdentifier={pathname} useSegments={useSegments}>
90+
<OutOfOfficeEntriesListContent
91+
onOpenCreateDialog={onOpenCreateDialog}
92+
onOpenEditDialog={onOpenEditDialog}
93+
/>
94+
</DataTableProvider>
95+
</div>
9396
</SettingsHeader>
9497
);
9598
}
@@ -168,92 +171,90 @@ function OutOfOfficeEntriesListContent({
168171
}),
169172
...(selectedTab === OutOfOfficeTab.TEAM
170173
? [
171-
columnHelper.display({
172-
id: "member",
173-
header: `Member`,
174-
size: 300,
175-
cell: ({ row }) => {
176-
if (!row.original || !row.original.user || isPending || isFetching) {
177-
return <SkeletonText className="h-8 w-full" />;
178-
}
179-
const { avatarUrl, username, email, name } = row.original.user;
180-
const memberName =
181-
name ||
182-
(() => {
183-
const emailName = email.split("@")[0];
184-
return emailName.charAt(0).toUpperCase() + emailName.slice(1);
185-
})();
186-
return (
187-
<div className="flex items-center gap-2">
188-
<Avatar
189-
size="sm"
190-
alt={username || email}
191-
imageSrc={getUserAvatarUrl({
192-
avatarUrl,
193-
})}
194-
/>
195-
<div className="">
196-
<div
197-
data-testid={`ooo-member-${username}-username`}
198-
className="text-emphasis text-sm font-medium leading-none">
199-
{memberName}
200-
</div>
201-
<div
202-
data-testid={`ooo-member-${username}-email`}
203-
className="text-subtle mt-1 text-sm leading-none">
204-
{email}
205-
</div>
174+
columnHelper.display({
175+
id: "member",
176+
header: `Member`,
177+
size: 220,
178+
cell: ({ row }) => {
179+
if (!row.original || !row.original.user || isPending || isFetching) {
180+
return <SkeletonText className="h-8 w-full" />;
181+
}
182+
const { avatarUrl, username, email, name } = row.original.user;
183+
const memberName =
184+
name ||
185+
(() => {
186+
const emailName = email.split("@")[0];
187+
return emailName.charAt(0).toUpperCase() + emailName.slice(1);
188+
})();
189+
return (
190+
<div className="flex items-center gap-2">
191+
<Avatar
192+
size="sm"
193+
alt={username || email}
194+
imageSrc={getUserAvatarUrl({
195+
avatarUrl,
196+
})}
197+
/>
198+
<div className="">
199+
<div
200+
data-testid={`ooo-member-${username}-username`}
201+
className="text-emphasis text-sm font-medium leading-none">
202+
{memberName}
203+
</div>
204+
<div
205+
data-testid={`ooo-member-${username}-email`}
206+
className="text-subtle mt-1 text-sm leading-none">
207+
{email}
206208
</div>
207209
</div>
208-
);
209-
},
210-
}),
211-
]
210+
</div>
211+
);
212+
},
213+
}),
214+
]
212215
: []),
213216
columnHelper.display({
214217
id: "outOfOffice",
215218
header: `${t("out_of_office")} (${totalRowCount})`,
216-
size: selectedTab === OutOfOfficeTab.TEAM ? 370 : 660,
219+
size: 570,
217220
cell: ({ row }) => {
218221
const item = row.original;
219222
return (
220223
<>
221224
{row.original && !isPending && !isFetching ? (
222225
<div
223-
className="flex flex-row justify-between p-2"
226+
className="flex flex-row items-center gap-3 py-2"
224227
data-testid={`table-redirect-${item.toUser?.username || "n-a"}`}>
225-
<div className="flex flex-row items-center">
226-
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-gray-50">
227-
{item?.reason?.emoji || "🏝️"}
228-
</div>
228+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-subtle">
229+
{item?.reason?.emoji || "🏝️"}
230+
</div>
229231

230-
<div className="ml-2 flex flex-col">
231-
<p className="px-2 font-bold">
232-
{dayjs.utc(item.start).format("ll")} - {dayjs.utc(item.end).format("ll")}
233-
</p>
234-
<p className="px-2">
235-
{item.toUser?.username ? (
236-
<ServerTrans
237-
t={t}
238-
i18nKey="ooo_forwarding_to"
239-
values={{
240-
username: item.toUser?.username,
241-
}}
242-
components={[<span key="ooo-username" className="text-subtle font-bold" />]}
243-
/>
244-
) : (
245-
<>{t("ooo_not_forwarding")}</>
246-
)}
247-
</p>
248-
{item.notes && (
249-
<p className="px-2">
250-
<span className="text-subtle">{t("notes")}: </span>
251-
<span data-testid={`ooo-entry-note-${item.toUser?.username || "n-a"}`}>
252-
{item.notes}
253-
</span>
254-
</p>
232+
<div className="flex flex-col">
233+
<p className="font-bold">
234+
{dayjs.utc(item.start).format("ll")} - {dayjs.utc(item.end).format("ll")}
235+
</p>
236+
<p>
237+
{item.toUser?.username ? (
238+
<ServerTrans
239+
t={t}
240+
i18nKey="ooo_forwarding_to"
241+
values={{
242+
username: item.toUser?.username,
243+
}}
244+
components={[<span key="ooo-username" className="text-subtle font-bold" />]}
245+
/>
246+
) : (
247+
<>{t("ooo_not_forwarding")}</>
255248
)}
256-
</div>
249+
</p>
250+
{item.notes && (
251+
<p>
252+
<span className="text-subtle">{t("notes")}: </span>
253+
<span data-testid={`ooo-entry-note-${item.toUser?.username || "n-a"}`}>
254+
{item.notes}
255+
</span>
256+
</p>
257+
)}
257258
</div>
258259
</div>
259260
) : (

0 commit comments

Comments
 (0)