+ className={classNames(
+ "bg-default dark:bg-default sticky top-[var(--calendar-dates-sticky-offset,0px)] z-[80] flex-none border-b sm:pr-8",
+ borderColor === "subtle" ? "border-b-subtle" : "border-b-default",
+ showBorder && (borderColor === "subtle" ? "border-r-subtle border-r" : "border-r-default border-r")
+ )}>
+
{days.map((day) => {
const isToday = dayjs().isSame(day, "day");
return (
@@ -29,8 +37,8 @@ export function DateValues({ days, containerNavRef }: Props) {
{day.format("dd")}{" "}
{day.format("D")}
@@ -38,8 +46,14 @@ export function DateValues({ days, containerNavRef }: Props) {
);
})}
-
-
+
+
{days.map((day) => {
const isToday = dayjs().isSame(day, "day");
return (
@@ -47,14 +61,14 @@ export function DateValues({ days, containerNavRef }: Props) {
key={day.toString()}
className={classNames(
"flex flex-1 items-center justify-center py-3 text-xs font-medium uppercase",
- isToday && "font-bold"
+ isToday && "text-default"
)}>
{formatDate(day)}{" "}
{day.format("DD")}
diff --git a/packages/features/calendars/weeklyview/components/currentTime/index.tsx b/packages/features/calendars/weeklyview/components/currentTime/index.tsx
index 56c94b7873..f8e1d78ba8 100644
--- a/packages/features/calendars/weeklyview/components/currentTime/index.tsx
+++ b/packages/features/calendars/weeklyview/components/currentTime/index.tsx
@@ -1,8 +1,8 @@
import { useEffect, useState, useRef } from "react";
import dayjs from "@calcom/dayjs";
+import { useTimePreferences } from "@calcom/features/bookings/lib";
-import { useBookerTime } from "../../../../bookings/Booker/components/hooks/useBookerTime";
import { useCalendarStore } from "../../state/store";
function calculateMinutesFromStart(startHour: number, currentHour: number, currentMinute: number) {
@@ -11,7 +11,8 @@ function calculateMinutesFromStart(startHour: number, currentHour: number, curre
return currentMinuteOfDay - startMinute;
}
-export function CurrentTime() {
+export function CurrentTime({ timezone }: { timezone: string }) {
+ const { timeFormat } = useTimePreferences();
const currentTimeRef = useRef(null);
const [scrolledIntoView, setScrolledIntoView] = useState(false);
const [currentTimePos, setCurrentTimePos] = useState(null);
@@ -19,7 +20,6 @@ export function CurrentTime() {
startHour: state.startHour || 0,
endHour: state.endHour || 23,
}));
- const { timeFormat, timezone } = useBookerTime();
useEffect(() => {
// Set the container scroll position based on the current time.
@@ -39,11 +39,10 @@ export function CurrentTime() {
if (!currentTimeRef.current || scrolledIntoView) return;
// Within a small timeout so element has time to render.
setTimeout(() => {
- // eslint-disable-next-line @calcom/eslint/no-scroll-into-view-embed -- Doesn't seem to cause any issue. Put it under condition if needed
+ // Doesn't seem to cause any issue. Put it under condition if needed
currentTimeRef?.current?.scrollIntoView({ block: "center" });
setScrolledIntoView(true);
}, 100);
- // eslint-disable-next-line react-hooks/exhaustive-deps
}, [startHour, endHour, scrolledIntoView, timezone]);
return (
@@ -55,7 +54,7 @@ export function CurrentTime() {
top: `calc(${currentTimePos}*var(--one-minute-height) + var(--calendar-offset-top))`,
zIndex: 70,
}}>
- {dayjs().tz(timezone).format(timeFormat)}
+ {dayjs().tz(timezone).format(timeFormat)}
diff --git a/packages/features/calendars/weeklyview/components/event/Empty.tsx b/packages/features/calendars/weeklyview/components/event/Empty.tsx
index 913f09980c..6ec98b2cad 100644
--- a/packages/features/calendars/weeklyview/components/event/Empty.tsx
+++ b/packages/features/calendars/weeklyview/components/event/Empty.tsx
@@ -3,10 +3,10 @@ import { shallow } from "zustand/shallow";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
+import { useTimePreferences } from "@calcom/features/bookings/lib";
import classNames from "@calcom/ui/classNames";
import { OutOfOfficeInSlots } from "../../../../bookings/Booker/components/OutOfOfficeInSlots";
-import { useBookerTime } from "../../../../bookings/Booker/components/hooks/useBookerTime";
import { useCalendarStore } from "../../state/store";
import type { CalendarAvailableTimeslots } from "../../types/state";
import type { GridCellToDateProps } from "../../utils";
@@ -33,13 +33,13 @@ export function EmptyCell(props: EmptyCellProps) {
}
type AvailableCellProps = {
+ timezone: string;
availableSlots: CalendarAvailableTimeslots;
day: GridCellToDateProps["day"];
startHour: GridCellToDateProps["startHour"];
};
-export function AvailableCellsForDay({ availableSlots, day, startHour }: AvailableCellProps) {
- const { timezone } = useBookerTime();
+export function AvailableCellsForDay({ timezone, availableSlots, day, startHour }: AvailableCellProps) {
const date = dayjs(day);
const dateFormatted = date.format("YYYY-MM-DD");
const slotsForToday = availableSlots && availableSlots[dateFormatted];
@@ -136,7 +136,7 @@ type CellProps = {
};
function Cell({ isDisabled, topOffsetMinutes, timeSlot }: CellProps) {
- const { timeFormat } = useBookerTime();
+ const { timeFormat } = useTimePreferences();
const { onEmptyCellClick, hoverEventDuration } = useCalendarStore(
(state) => ({
@@ -163,7 +163,7 @@ function Cell({ isDisabled, topOffsetMinutes, timeSlot }: CellProps) {
top: topOffsetMinutes ? `calc(${topOffsetMinutes}*var(--one-minute-height))` : undefined,
}}
onClick={() => {
- onEmptyCellClick && onEmptyCellClick(timeSlot.toDate());
+ onEmptyCellClick?.(timeSlot.toDate());
}}>
{!isDisabled && hoverEventDuration !== 0 && (
30 && "flex-col py-1",
options?.className
+ )}>
+ {options?.color && (
+
)}
- style={styles}>
-
-
{event.title}
- {eventDuration <= 30 && !event.options?.hideTime && (
-
+
30 && "flex-col py-1")}>
+
+
{event.title}
+ {eventDuration <= 30 && !event.options?.hideTime && (
+
+ {dayjs(event.start).format("HH:mm")} - {dayjs(event.end).format("HH:mm")}
+
+ )}
+
+ {eventDuration > 30 && !event.options?.hideTime && (
+
{dayjs(event.start).format("HH:mm")} - {dayjs(event.end).format("HH:mm")}
)}
+ {eventDuration > 45 && event.description && (
+
{event.description}
+ )}
- {eventDuration > 30 && !event.options?.hideTime && (
-
- {dayjs(event.start).format("HH:mm")} - {dayjs(event.end).format("HH:mm")}
-
- )}
- {eventDuration > 45 && event.description && (
-
{event.description}
- )}
);
diff --git a/packages/features/calendars/weeklyview/components/horizontalLines/index.tsx b/packages/features/calendars/weeklyview/components/horizontalLines/index.tsx
index b73c40be70..852eac14ce 100644
--- a/packages/features/calendars/weeklyview/components/horizontalLines/index.tsx
+++ b/packages/features/calendars/weeklyview/components/horizontalLines/index.tsx
@@ -2,15 +2,19 @@ import { useId } from "react";
import type dayjs from "@calcom/dayjs";
import { useTimePreferences } from "@calcom/features/bookings/lib";
+import classNames from "@calcom/ui/classNames";
+
+import type { BorderColor } from "../../types/common";
export const HorizontalLines = ({
hours,
- numberOfGridStopsPerCell,
containerOffsetRef,
+ borderColor,
}: {
hours: dayjs.Dayjs[];
numberOfGridStopsPerCell: number;
containerOffsetRef: React.RefObject
;
+ borderColor: BorderColor;
}) => {
const { timeFormat } = useTimePreferences();
// We need to force the minute to zero, because otherwise in ex GMT+5.5, it would show :30 minute times (but at the positino of :00)
@@ -19,7 +23,10 @@ export const HorizontalLines = ({
return (
diff --git a/packages/features/calendars/weeklyview/components/verticalLines/index.tsx b/packages/features/calendars/weeklyview/components/verticalLines/index.tsx
index dfc1f9560a..b021bdb0f4 100644
--- a/packages/features/calendars/weeklyview/components/verticalLines/index.tsx
+++ b/packages/features/calendars/weeklyview/components/verticalLines/index.tsx
@@ -1,6 +1,9 @@
import type dayjs from "@calcom/dayjs";
+import classNames from "@calcom/ui/classNames";
-export const VerticalLines = ({ days }: { days: dayjs.Dayjs[] }) => {
+import type { BorderColor } from "../../types/common";
+
+export const VerticalLines = ({ days, borderColor }: { days: dayjs.Dayjs[]; borderColor: BorderColor }) => {
const isRTL = () => {
let userLanguage = "en"; // Default to 'en' if navigator is not defined
@@ -15,8 +18,10 @@ export const VerticalLines = ({ days }: { days: dayjs.Dayjs[] }) => {
return (
((set) => ({
diff --git a/packages/features/calendars/weeklyview/types/common.ts b/packages/features/calendars/weeklyview/types/common.ts
new file mode 100644
index 0000000000..1b447070e7
--- /dev/null
+++ b/packages/features/calendars/weeklyview/types/common.ts
@@ -0,0 +1 @@
+export type BorderColor = "default" | "subtle";
diff --git a/packages/features/calendars/weeklyview/types/events.ts b/packages/features/calendars/weeklyview/types/events.ts
index 493750ea8f..5803d0a46d 100644
--- a/packages/features/calendars/weeklyview/types/events.ts
+++ b/packages/features/calendars/weeklyview/types/events.ts
@@ -11,7 +11,7 @@ export interface CalendarEvent {
status?: BookingStatus;
hideTime?: boolean;
allDay?: boolean;
- borderColor?: string;
+ color?: string;
className?: string;
"data-test-id"?: string;
};
diff --git a/packages/features/calendars/weeklyview/types/state.ts b/packages/features/calendars/weeklyview/types/state.ts
index a9fc46301b..00ebb33fd1 100644
--- a/packages/features/calendars/weeklyview/types/state.ts
+++ b/packages/features/calendars/weeklyview/types/state.ts
@@ -1,6 +1,7 @@
import type { IFromUser, IToUser } from "@calcom/features/availability/lib/getUserAvailability";
import type { TimeRange } from "@calcom/types/schedule";
+import type { BorderColor } from "./common";
import type { CalendarEvent } from "./events";
export type View = "month" | "week" | "day";
@@ -131,6 +132,21 @@ export type CalendarState = {
* Timezone to use for displaying times in the calendar
*/
timezone: string;
+ /**
+ * Show the background pattern for unavailable areas
+ * @default true
+ */
+ showBackgroundPattern?: boolean;
+ /**
+ * Show borders on the leftmost and rightmost sides of the calendar container
+ * @default true
+ */
+ showBorder?: boolean;
+ /**
+ * Border color for calendar cells
+ * @default "default"
+ */
+ borderColor?: BorderColor;
};
export type CalendarComponentProps = CalendarPublicActions & CalendarState & { isPending?: boolean };
diff --git a/packages/features/data-table/components/filters/ActiveFilters.tsx b/packages/features/data-table/components/filters/ActiveFilters.tsx
index 23cd495931..7e69e5031e 100644
--- a/packages/features/data-table/components/filters/ActiveFilters.tsx
+++ b/packages/features/data-table/components/filters/ActiveFilters.tsx
@@ -1,8 +1,6 @@
"use client";
import { type Table } from "@tanstack/react-table";
-// eslint-disable-next-line no-restricted-imports
-import { Fragment } from "react";
import { useDataTable, useFilterableColumns } from "../../hooks";
import { ColumnFilterType } from "../../lib/types";
@@ -12,9 +10,10 @@ import { FilterPopover } from "./FilterPopover";
// Add the new ActiveFilters component
interface ActiveFiltersProps
{
table: Table;
+ columnIdsToHide?: string[];
}
-export function ActiveFilters({ table }: ActiveFiltersProps) {
+export function ActiveFilters({ table, columnIdsToHide }: ActiveFiltersProps) {
const { activeFilters } = useDataTable();
const filterableColumns = useFilterableColumns(table);
@@ -22,7 +21,12 @@ export function ActiveFilters({ table }: ActiveFiltersProps) {
<>
{activeFilters.map((filter) => {
const column = filterableColumns.find((col) => col.id === filter.f);
- if (!column) return null;
+ if (!column) {
+ return null;
+ }
+ if (columnIdsToHide?.includes(column.id)) {
+ return null;
+ }
if (column.type === ColumnFilterType.DATE_RANGE) {
return (
diff --git a/packages/features/data-table/components/filters/AddFilterButton.tsx b/packages/features/data-table/components/filters/AddFilterButton.tsx
index 71c182f15f..d7399c63c8 100644
--- a/packages/features/data-table/components/filters/AddFilterButton.tsx
+++ b/packages/features/data-table/components/filters/AddFilterButton.tsx
@@ -1,7 +1,6 @@
"use client";
import { type Table } from "@tanstack/react-table";
-// eslint-disable-next-line no-restricted-imports
import startCase from "lodash/startCase";
import { forwardRef, useState } from "react";
@@ -17,17 +16,10 @@ import { useDataTable, useFilterableColumns } from "../../hooks";
export interface AddFilterButtonProps {
table: Table;
variant?: "base" | "sm";
- hideWhenFilterApplied?: boolean;
- showWhenFilterApplied?: boolean;
}
function AddFilterButtonComponent(
- {
- table,
- variant = "base",
- hideWhenFilterApplied = false,
- showWhenFilterApplied = false,
- }: AddFilterButtonProps,
+ { table, variant = "base" }: AddFilterButtonProps,
ref: React.Ref
) {
const { t } = useLocale();
@@ -39,14 +31,6 @@ function AddFilterButtonComponent(
(column) => !activeFilters?.some((filter) => filter.f === column.id)
);
- if (hideWhenFilterApplied && activeFilters?.length > 0) {
- return null;
- }
-
- if (showWhenFilterApplied && activeFilters?.length === 0) {
- return null;
- }
-
if (variant === "sm" && availableColumns.length === 0) {
return null;
}
diff --git a/packages/features/data-table/components/filters/FilterBar.tsx b/packages/features/data-table/components/filters/FilterBar.tsx
index df2161141e..362759c433 100644
--- a/packages/features/data-table/components/filters/FilterBar.tsx
+++ b/packages/features/data-table/components/filters/FilterBar.tsx
@@ -1,18 +1,30 @@
-import { type Table } from "@tanstack/react-table";
+"use client";
+import { type Table } from "@tanstack/react-table";
+import { useMemo } from "react";
+
+import { useDataTable } from "../../hooks";
import { ActiveFilters } from "./ActiveFilters";
import { AddFilterButton } from "./AddFilterButton";
interface FilterBarProps {
table: Table;
+ columnIdsToHide?: string[];
}
-export function FilterBar({ table }: FilterBarProps) {
+export function FilterBar({ table, columnIdsToHide }: FilterBarProps) {
+ const { activeFilters } = useDataTable();
+ const displayedFilterCount = useMemo(() => {
+ return (activeFilters ?? []).filter((filter) =>
+ columnIdsToHide ? !columnIdsToHide.includes(filter.f) : true
+ ).length;
+ }, [activeFilters, columnIdsToHide]);
+
return (
<>
-
-
-
+ {displayedFilterCount === 0 && }
+
+ {displayedFilterCount > 0 && }
>
);
}
diff --git a/packages/features/flags/config.ts b/packages/features/flags/config.ts
index 79582d7ac5..8002437a86 100644
--- a/packages/features/flags/config.ts
+++ b/packages/features/flags/config.ts
@@ -30,6 +30,7 @@ export type AppFlags = {
"calendar-subscription-sync": boolean;
"onboarding-v3": boolean;
"booker-botid": boolean;
+ "booking-calendar-view": boolean;
};
export type TeamFeatures = Record;
diff --git a/packages/features/flags/hooks/index.ts b/packages/features/flags/hooks/index.ts
index 05ce9e61ab..cc5c20b96e 100644
--- a/packages/features/flags/hooks/index.ts
+++ b/packages/features/flags/hooks/index.ts
@@ -29,6 +29,7 @@ const initialData: AppFlags = {
"calendar-subscription-sync": false,
"onboarding-v3": false,
"booker-botid": false,
+ "booking-calendar-view": false,
};
if (process.env.NEXT_PUBLIC_IS_E2E) {
diff --git a/packages/features/troubleshooter/components/LargeCalendar.tsx b/packages/features/troubleshooter/components/LargeCalendar.tsx
index 6ffd5ea060..de2f725a71 100644
--- a/packages/features/troubleshooter/components/LargeCalendar.tsx
+++ b/packages/features/troubleshooter/components/LargeCalendar.tsx
@@ -68,8 +68,8 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => {
start: new Date(event.start),
end: new Date(event.end),
options: {
- borderColor:
- event.source && calendarToColorMap[event.source] ? calendarToColorMap[event.source] : "black",
+ color:
+ event.source && calendarToColorMap[event.source] ? calendarToColorMap[event.source] : undefined,
status: BookingStatus.ACCEPTED,
"data-test-id": "troubleshooter-busy-event",
},
@@ -99,7 +99,7 @@ export const LargeCalendar = ({ extraDays }: { extraDays: number }) => {
start: dateOverrideStart.add(workingHoursForDay.startTime, "minutes").toDate(),
end: dateOverrideEnd.add(workingHoursForDay.endTime, "minutes").toDate(),
options: {
- borderColor: "black",
+ color: "black",
status: BookingStatus.ACCEPTED,
"data-test-id": "troubleshooter-busy-time",
},
diff --git a/packages/prisma/migrations/20251029103123_add_booking_calendar_view_feature_flag/migration.sql b/packages/prisma/migrations/20251029103123_add_booking_calendar_view_feature_flag/migration.sql
new file mode 100644
index 0000000000..2deec6873e
--- /dev/null
+++ b/packages/prisma/migrations/20251029103123_add_booking_calendar_view_feature_flag/migration.sql
@@ -0,0 +1,9 @@
+INSERT INTO
+ "Feature" (slug, enabled, description, "type")
+VALUES
+ (
+ 'booking-calendar-view',
+ false,
+ 'Enable booking calendar view for all users',
+ 'EXPERIMENT'
+ ) ON CONFLICT (slug) DO NOTHING;