diff --git a/apps/web/modules/bookings/hooks/useEventTypes.ts b/apps/web/modules/bookings/hooks/useEventTypes.ts deleted file mode 100644 index 4b7c47e9bd..0000000000 --- a/apps/web/modules/bookings/hooks/useEventTypes.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useSession } from "next-auth/react"; -import { useMemo } from "react"; - -import type { FacetedValue } from "@calcom/features/data-table"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { trpc } from "@calcom/trpc/react"; - -export function useEventTypes() { - const { data: user } = useSession(); - const { t } = useLocale(); - - const eventTypesQuery = trpc.viewer.eventTypes.listWithTeam.useQuery(undefined, { - enabled: !!user, - }); - - return useMemo(() => { - const eventTypes = eventTypesQuery.data || []; - - // Separate individual and team event types - const individualEvents = eventTypes.filter((el) => !el.team); - - // Group team events by team - const teamEventsMap = eventTypes.reduce((acc, event) => { - if (!event.team) return acc; - - const teamId = event.team.id; - if (!acc[teamId]) { - acc[teamId] = { - teamName: event.team.name, - events: [], - }; - } - acc[teamId].events.push(event); - return acc; - }, {} as Record); - - // Create flat array with section markers - const flatArray: FacetedValue[] = []; - - // Add individual events section if exists - if (individualEvents.length > 0) { - flatArray.push({ - section: t("individual"), - label: individualEvents[0].title, - value: individualEvents[0].id, - }); - flatArray.push( - ...individualEvents.slice(1).map((event) => ({ - label: event.title, - value: event.id, - })) - ); - } - - // Add each team's events as a separate section - Object.values(teamEventsMap).forEach(({ teamName, events }) => { - if (events.length > 0) { - flatArray.push({ - section: `${teamName} Events`, - label: events[0].title, - value: events[0].id, - }); - flatArray.push( - ...events.slice(1).map((event) => ({ - label: event.title, - value: event.id, - })) - ); - } - }); - - return flatArray; - }, [eventTypesQuery, t]); -} diff --git a/apps/web/modules/bookings/hooks/useFacetedUniqueValues.ts b/apps/web/modules/bookings/hooks/useFacetedUniqueValues.ts deleted file mode 100644 index c490ec1521..0000000000 --- a/apps/web/modules/bookings/hooks/useFacetedUniqueValues.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Table } from "@tanstack/react-table"; -import { useCallback } from "react"; - -import { convertFacetedValuesToMap, type FacetedValue } from "@calcom/features/data-table"; -import { trpc } from "@calcom/trpc/react"; - -import { useEventTypes } from "./useEventTypes"; - -export function useFacetedUniqueValues() { - const eventTypes = useEventTypes(); - const { data: teams } = trpc.viewer.teams.list.useQuery(); - const { data: members } = trpc.viewer.teams.listSimpleMembers.useQuery(); - - return useCallback( - (_: Table, columnId: string) => (): Map => { - if (columnId === "eventTypeId") { - return convertFacetedValuesToMap(eventTypes || []); - } else if (columnId === "teamId") { - return convertFacetedValuesToMap( - (teams || []).map((team) => ({ - label: team.name, - value: team.id, - })) - ); - } else if (columnId === "userId") { - return convertFacetedValuesToMap( - (members || []) - .map((member) => ({ - label: member.name, - value: member.id, - })) - .filter((option): option is { label: string; value: number } => Boolean(option.label)) - ); - } - return new Map(); - }, - [eventTypes, teams, members] - ); -} diff --git a/apps/web/modules/bookings/hooks/useProperHeightForMobile.ts b/apps/web/modules/bookings/hooks/useProperHeightForMobile.ts deleted file mode 100644 index c302847bd8..0000000000 --- a/apps/web/modules/bookings/hooks/useProperHeightForMobile.ts +++ /dev/null @@ -1,39 +0,0 @@ -// eslint-disable-next-line no-restricted-imports -import { debounce } from "lodash"; -import { useCallback, useEffect, useRef } from "react"; - -// Dynamically adjusts DataTable height on mobile to prevent nested scrolling -// and ensure the table fits within the viewport without overflowing (hacky) -export function useProperHeightForMobile(ref: React.RefObject) { - const lastOffsetY = useRef(); - const lastWindowHeight = useRef(); - const BOTTOM_NAV_HEIGHT = 64; - const BUFFER = 32; - - const updateHeight = useCallback( - debounce(() => { - if (!ref.current || window.innerWidth >= 640) return; - const rect = ref.current.getBoundingClientRect(); - if (rect.top !== lastOffsetY.current || window.innerHeight !== lastWindowHeight.current) { - lastOffsetY.current = rect.top; - lastWindowHeight.current = window.innerHeight; - const height = window.innerHeight - lastOffsetY.current - BOTTOM_NAV_HEIGHT - BUFFER; - ref.current.style.height = `${height}px`; - } - }, 200), - [ref.current] - ); - - useEffect(() => { - const handleResize = () => { - updateHeight(); - }; - - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, [updateHeight]); - - updateHeight(); -} diff --git a/apps/web/modules/bookings/views/bookings-listing-view.tsx b/apps/web/modules/bookings/views/bookings-listing-view.tsx index e18f06362f..d8b730aaff 100644 --- a/apps/web/modules/bookings/views/bookings-listing-view.tsx +++ b/apps/web/modules/bookings/views/bookings-listing-view.tsx @@ -7,19 +7,18 @@ import { getSortedRowModel, createColumnHelper, } from "@tanstack/react-table"; -import { useMemo, useRef } from "react"; +// eslint-disable-next-line no-restricted-imports +import { debounce } from "lodash"; +import { useMemo, useState, useRef, useEffect, useCallback } from "react"; +import type { z } from "zod"; import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components"; import dayjs from "@calcom/dayjs"; -import { - DataTableProvider, - DataTableWrapper, - DataTableFilters, - ColumnFilterType, - useFilterValue, - ZMultiSelectFilterValue, - ZDateRangeFilterValue, -} from "@calcom/features/data-table"; +import { FilterToggle } from "@calcom/features/bookings/components/FilterToggle"; +import { FiltersContainer } from "@calcom/features/bookings/components/FiltersContainer"; +import type { filterQuerySchema } from "@calcom/features/bookings/lib/useFilterQuery"; +import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery"; +import { DataTableProvider, DataTableWrapper } from "@calcom/features/data-table"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; @@ -31,11 +30,9 @@ import useMeQuery from "@lib/hooks/useMeQuery"; import BookingListItem from "@components/booking/BookingListItem"; import SkeletonLoader from "@components/booking/SkeletonLoader"; -import { useFacetedUniqueValues } from "~/bookings/hooks/useFacetedUniqueValues"; -import { useProperHeightForMobile } from "~/bookings/hooks/useProperHeightForMobile"; import type { validStatuses } from "~/bookings/lib/validStatuses"; -type BookingListingStatus = (typeof validStatuses)[number]; +type BookingListingStatus = z.infer>["status"]; type BookingOutput = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][0]; type RecurringInfo = { @@ -73,7 +70,7 @@ const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [ }, ]; -const descriptionByStatus: Record = { +const descriptionByStatus: Record, string> = { upcoming: "upcoming_bookings", recurring: "recurring_bookings", past: "past_bookings", @@ -105,29 +102,24 @@ type RowData = }; function BookingsContent({ status }: BookingsProps) { + const { data: filterQuery } = useFilterQuery(); + const { t } = useLocale(); const user = useMeQuery().data; + const [isFiltersVisible, setIsFiltersVisible] = useState(false); const tableContainerRef = useRef(null); useProperHeightForMobile(tableContainerRef); - const eventTypeIds = useFilterValue("eventTypeId", ZMultiSelectFilterValue)?.data as number[] | undefined; - const teamIds = useFilterValue("teamId", ZMultiSelectFilterValue)?.data as number[] | undefined; - const userIds = useFilterValue("userId", ZMultiSelectFilterValue)?.data as number[] | undefined; - const dateRange = useFilterValue("time", ZDateRangeFilterValue)?.data; - const query = trpc.viewer.bookings.get.useInfiniteQuery( { limit: 10, filters: { - status, - eventTypeIds, - teamIds, - userIds, - afterStartDate: dateRange?.startDate ?? undefined, - beforeEndDate: dateRange?.endDate ?? undefined, + ...filterQuery, + status: filterQuery.status ?? status, }, }, { + enabled: true, getNextPageParam: (lastPage) => lastPage.nextCursor, } ); @@ -136,60 +128,8 @@ function BookingsContent({ status }: BookingsProps) { const columnHelper = createColumnHelper(); return [ - columnHelper.accessor((row) => row.type === "data" && row.booking.eventType.id, { - id: "eventTypeId", - header: t("event_type"), - enableColumnFilter: true, - enableSorting: false, - cell: () => null, - filterFn: () => true, - meta: { - filter: { - type: ColumnFilterType.MULTI_SELECT, - }, - }, - }), - columnHelper.accessor((row) => row.type === "data" && row.booking.eventType.team?.id, { - id: "teamId", - header: t("team"), - enableColumnFilter: true, - enableSorting: false, - cell: () => null, - filterFn: () => true, - meta: { - filter: { - type: ColumnFilterType.MULTI_SELECT, - }, - }, - }), - columnHelper.accessor((row) => row.type === "data" && row.booking.user?.id, { - id: "userId", - header: t("people"), - enableColumnFilter: true, - enableSorting: false, - cell: () => null, - filterFn: () => true, - meta: { - filter: { - type: ColumnFilterType.MULTI_SELECT, - }, - }, - }), - columnHelper.accessor((row) => row, { - id: "time", - header: t("date_range"), - enableColumnFilter: true, - enableSorting: false, - cell: () => null, - filterFn: () => true, - meta: { - filter: { - type: ColumnFilterType.DATE_RANGE, - }, - }, - }), columnHelper.display({ - id: "customView", + id: "custom-view", cell: (props) => { if (props.row.original.type === "data") { const { booking, recurringInfo, isToday } = props.row.original; @@ -224,7 +164,7 @@ function BookingsContent({ status }: BookingsProps) { }, }), ]; - }, [user, status, t]); + }, [user, status]); const isEmpty = useMemo(() => !query.data?.pages[0]?.bookings.length, [query.data]); @@ -301,23 +241,12 @@ function BookingsContent({ status }: BookingsProps) { return merged; }, [bookingsToday, flatData, status]); - const getFacetedUniqueValues = useFacetedUniqueValues(); - const table = useReactTable({ data: finalData, columns, - initialState: { - columnVisibility: { - eventTypeId: false, - teamId: false, - userId: false, - time: false, - }, - }, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), - getFacetedUniqueValues, }); return ( @@ -329,14 +258,16 @@ function BookingsContent({ status }: BookingsProps) { name: t(tab.name), }))} /> + +
{query.status === "error" && ( )} {(query.status === "pending" || query.isPaused) && } - {query.status === "success" && ( + {query.status === "success" && !isEmpty && ( <> {!!bookingsToday.length && status === "upcoming" && ( @@ -352,30 +283,59 @@ function BookingsContent({ status }: BookingsProps) { fetchNextPage={query.fetchNextPage} isFetching={query.isFetching} variant="compact" - ToolbarLeft={ - <> - - - - - } - EmptyView={ -
- -
- } /> )} + {query.status === "success" && isEmpty && ( +
+ +
+ )}
); } + +// Dynamically adjusts DataTable height on mobile to prevent nested scrolling +// and ensure the table fits within the viewport without overflowing (hacky) +function useProperHeightForMobile(ref: React.RefObject) { + const lastOffsetY = useRef(); + const lastWindowHeight = useRef(); + const BOTTOM_NAV_HEIGHT = 64; + const BUFFER = 32; + + const updateHeight = useCallback( + debounce(() => { + if (!ref.current || window.innerWidth >= 640) return; + const rect = ref.current.getBoundingClientRect(); + if (rect.top !== lastOffsetY.current || window.innerHeight !== lastWindowHeight.current) { + lastOffsetY.current = rect.top; + lastWindowHeight.current = window.innerHeight; + const height = window.innerHeight - lastOffsetY.current - BOTTOM_NAV_HEIGHT - BUFFER; + ref.current.style.height = `${height}px`; + } + }, 200), + [ref.current] + ); + + useEffect(() => { + const handleResize = () => { + updateHeight(); + }; + + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("resize", handleResize); + }; + }, [updateHeight]); + + updateHeight(); +} diff --git a/packages/features/data-table/components/DataTable.tsx b/packages/features/data-table/components/DataTable.tsx index ef22c50506..54f9fcdcc7 100644 --- a/packages/features/data-table/components/DataTable.tsx +++ b/packages/features/data-table/components/DataTable.tsx @@ -265,6 +265,7 @@ function DataTableBody({ className={classNames(onRowMouseclick && "hover:cursor-pointer", "group")}> {row.getVisibleCells().map((cell) => { const column = cell.column; + const meta = column?.columnDef.meta; return ( = { testId?: string; @@ -25,7 +25,6 @@ export type DataTableWrapperProps = { totalDBRowCount?: number; ToolbarLeft?: React.ReactNode; ToolbarRight?: React.ReactNode; - EmptyView?: React.ReactNode; className?: string; containerClassName?: string; children?: React.ReactNode; @@ -45,7 +44,6 @@ export function DataTableWrapper({ hideHeader, ToolbarLeft, ToolbarRight, - EmptyView, className, containerClassName, children, @@ -81,12 +79,21 @@ export function DataTableWrapper({ })); }, [table, sorting, columnFilters, columnVisibility]); - const showEmptyView = table.getRowCount() === 0 && EmptyView; - return ( - <> + fetchMoreOnBottomReached(e.target as HTMLDivElement)}> {(ToolbarLeft || ToolbarRight || children) && ( -
+
{ToolbarLeft}
@@ -95,29 +102,14 @@ export function DataTableWrapper({
{children} + + )} + + {totalDBRowCount && ( +
+
)} - {showEmptyView && EmptyView} - {!showEmptyView && ( - fetchMoreOnBottomReached(e.target as HTMLDivElement)}> - {totalDBRowCount && ( -
- -
- )} -
- )} - + ); } diff --git a/packages/features/data-table/components/filters/ActiveFilters.tsx b/packages/features/data-table/components/filters/ActiveFilters.tsx index a8d7814dbc..988618f04a 100644 --- a/packages/features/data-table/components/filters/ActiveFilters.tsx +++ b/packages/features/data-table/components/filters/ActiveFilters.tsx @@ -5,8 +5,6 @@ import { type Table } from "@tanstack/react-table"; import { Fragment } from "react"; import { useDataTable, useFilterableColumns } from "../../hooks"; -import { ColumnFilterType } from "../../lib/types"; -import { DateRangeFilter } from "./DateRangeFilter"; import { FilterPopover } from "./FilterPopover"; // Add the new ActiveFilters component @@ -23,12 +21,7 @@ export function ActiveFilters({ table }: ActiveFiltersProps) { {activeFilters.map((filter) => { const column = filterableColumns.find((col) => col.id === filter.f); if (!column) return null; - - if (column.type === ColumnFilterType.DATE_RANGE) { - return ; - } else { - return ; - } + return ; })} ); diff --git a/packages/features/data-table/components/filters/AddFilterButton.tsx b/packages/features/data-table/components/filters/AddFilterButton.tsx index f9c7a00efe..065d42792e 100644 --- a/packages/features/data-table/components/filters/AddFilterButton.tsx +++ b/packages/features/data-table/components/filters/AddFilterButton.tsx @@ -62,10 +62,7 @@ function AddFilterButtonComponent( {t("no_columns_found")} {filterableColumns.map((column) => { - const showHiddenIndicator = - !table.getColumn(column.id)?.getIsVisible() && - table.initialState.columnVisibility?.[column.id] !== false; - + const isVisible = table.getColumn(column.id)?.getIsVisible(); if (activeFilters?.some((filter) => filter.f === column.id)) return null; return ( ( className="flex items-center justify-between px-4 py-2" data-testid={`add-filter-item-${column.id}`}> {startCase(column.title)} - {showHiddenIndicator && } + {!isVisible && } ); })} diff --git a/packages/features/data-table/components/filters/DateRangeFilter.tsx b/packages/features/data-table/components/filters/DateRangeFilter.tsx index 5fcd6ae757..25919845f1 100644 --- a/packages/features/data-table/components/filters/DateRangeFilter.tsx +++ b/packages/features/data-table/components/filters/DateRangeFilter.tsx @@ -15,9 +15,6 @@ import { Command, CommandList, CommandItem, - CommandGroup, - CommandSeparator, - buttonClasses, } from "@calcom/ui"; import { useDataTable, useFilterValue } from "../../hooks"; @@ -35,7 +32,6 @@ import { ZDateRangeFilterValue, ColumnFilterType } from "../../lib/types"; type DateRangeFilterProps = { column: Extract; - showClearButton?: boolean; }; const getDateRangeFromPreset = (val: string | null) => { @@ -76,9 +72,9 @@ const getDateRangeFromPreset = (val: string | null) => { return { startDate, endDate, preset }; }; -export const DateRangeFilter = ({ column, showClearButton = false }: DateRangeFilterProps) => { +export const DateRangeFilter = ({ column }: DateRangeFilterProps) => { const filterValue = useFilterValue(column.id, ZDateRangeFilterValue); - const { updateFilter, removeFilter } = useDataTable(); + const { updateFilter } = useDataTable(); const { t } = useLocale(); const currentDate = dayjs(); @@ -88,11 +84,7 @@ export const DateRangeFilter = ({ column, showClearButton = false }: DateRangeFi const [endDate, setEndDate] = useState( filterValue?.data.endDate ? dayjs(filterValue.data.endDate) : getDefaultEndDate() ); - const [selectedPreset, setSelectedPreset] = useState( - filterValue?.data.preset - ? PRESET_OPTIONS.find((o) => o.value === filterValue.data.preset) ?? DEFAULT_PRESET - : DEFAULT_PRESET - ); + const [selectedPreset, setSelectedPreset] = useState(DEFAULT_PRESET); const updateValues = ({ preset, @@ -200,23 +192,6 @@ export const DateRangeFilter = ({ column, showClearButton = false }: DateRangeFi ))} - {showClearButton && ( - <> - - - { - removeFilter(column.id); - }} - className={classNames( - "w-full justify-center text-center", - buttonClasses({ color: "secondary" }) - )}> - {t("clear")} - - - - )} diff --git a/packages/features/data-table/components/filters/FilterOptions.tsx b/packages/features/data-table/components/filters/FilterOptions.tsx index 598b130522..f1f6312cee 100644 --- a/packages/features/data-table/components/filters/FilterOptions.tsx +++ b/packages/features/data-table/components/filters/FilterOptions.tsx @@ -2,6 +2,7 @@ import type { FilterableColumn } from "../../lib/types"; import { ColumnFilterType } from "../../lib/types"; +import { DateRangeFilter } from "./DateRangeFilter"; import { MultiSelectFilterOptions } from "./MultiSelectFilterOptions"; import { NumberFilterOptions } from "./NumberFilterOptions"; import { SingleSelectFilterOptions } from "./SingleSelectFilterOptions"; @@ -20,6 +21,8 @@ export function FilterOptions({ column }: FilterOptionsProps) { return ; } else if (column.type === ColumnFilterType.NUMBER) { return ; + } else if (column.type === ColumnFilterType.DATE_RANGE) { + return ; } else { return null; } diff --git a/packages/features/data-table/components/filters/MultiSelectFilterOptions.tsx b/packages/features/data-table/components/filters/MultiSelectFilterOptions.tsx index 4f03bc9875..8b179d6e6a 100644 --- a/packages/features/data-table/components/filters/MultiSelectFilterOptions.tsx +++ b/packages/features/data-table/components/filters/MultiSelectFilterOptions.tsx @@ -32,42 +32,31 @@ export function MultiSelectFilterOptions({ column }: MultiSelectFilterOptionsPro {t("no_options_found")} - {column.options.map((option, index) => { + {column.options.map((option) => { if (!option) return null; - const { - label: optionLabel, - value: optionValue, - section, - } = typeof option === "string" ? { label: option, value: option, section: undefined } : option; + const { label: optionLabel, value: optionValue } = + typeof option === "string" ? { label: option, value: option } : option; return ( - <> - {section && index !== 0 &&
} - {section && ( -
- {section} -
- )} - { - const newFilterValue = filterValue?.data.includes(optionValue) - ? filterValue?.data.filter((value) => value !== optionValue) - : [...(filterValue?.data || []), optionValue]; - updateFilter(column.id, { type: ColumnFilterType.MULTI_SELECT, data: newFilterValue }); - }}> -
- {filterValue?.data.includes(optionValue) && ( - - )} -
- {optionLabel} -
- + { + const newFilterValue = filterValue?.data.includes(optionValue) + ? filterValue?.data.filter((value) => value !== optionValue) + : [...(filterValue?.data || []), optionValue]; + updateFilter(column.id, { type: ColumnFilterType.MULTI_SELECT, data: newFilterValue }); + }}> +
+ {filterValue?.data.includes(optionValue) && ( + + )} +
+ {optionLabel} +
); })}
diff --git a/packages/features/data-table/components/filters/SingleSelectFilterOptions.tsx b/packages/features/data-table/components/filters/SingleSelectFilterOptions.tsx index 936bc32213..a8a9e6c4ac 100644 --- a/packages/features/data-table/components/filters/SingleSelectFilterOptions.tsx +++ b/packages/features/data-table/components/filters/SingleSelectFilterOptions.tsx @@ -32,39 +32,28 @@ export function SingleSelectFilterOptions({ column }: SingleSelectFilterOptionsP {t("no_options_found")} - {column.options.map((option, index) => { + {column.options.map((option) => { if (!option) return null; - const { - label: optionLabel, - value: optionValue, - section, - } = typeof option === "string" ? { label: option, value: option, section: undefined } : option; + const { label: optionLabel, value: optionValue } = + typeof option === "string" ? { label: option, value: option } : option; return ( - <> - {section && index !== 0 &&
} - {section && ( -
- {section} -
- )} - { - updateFilter(column.id, { type: ColumnFilterType.SINGLE_SELECT, data: optionValue }); - }}> -
- {filterValue?.data === optionValue && ( - - )} -
- {optionLabel} -
- + { + updateFilter(column.id, { type: ColumnFilterType.SINGLE_SELECT, data: optionValue }); + }}> +
+ {filterValue?.data === optionValue && ( + + )} +
+ {optionLabel} +
); })}
diff --git a/packages/features/data-table/lib/types.ts b/packages/features/data-table/lib/types.ts index 6884a4fae0..82cf9cb0d7 100644 --- a/packages/features/data-table/lib/types.ts +++ b/packages/features/data-table/lib/types.ts @@ -128,11 +128,11 @@ export type FilterableColumn = { } & ( | { type: ColumnFilterType.SINGLE_SELECT; - options: FacetedValue[]; + options: Array<{ label: string; value: string | number }>; } | { type: ColumnFilterType.MULTI_SELECT; - options: FacetedValue[]; + options: Array<{ label: string; value: string | number }>; } | { type: ColumnFilterType.TEXT; @@ -186,5 +186,4 @@ export const ZColumnVisibility = z.record(z.string(), z.boolean()); export type FacetedValue = { label: string; value: string | number; - section?: string; }; diff --git a/packages/features/data-table/lib/utils.ts b/packages/features/data-table/lib/utils.ts index e689d64046..2b3cae53bc 100644 --- a/packages/features/data-table/lib/utils.ts +++ b/packages/features/data-table/lib/utils.ts @@ -151,7 +151,7 @@ export const dataTableFilter = (cellValue: unknown, filterValue: FilterValue) => export const convertFacetedValuesToMap = (array: FacetedValue[]) => { return new Map( - array.map((option) => [{ label: option.label, value: option.value, section: option.section }, 1]) + array.map((option) => [{ label: option.label, value: option.value }, 1]) ); }; @@ -161,8 +161,8 @@ export const convertMapToFacetedValues = (map: Map | undef } return Array.from(map.keys()).map((option) => { if (typeof option === "string") { - return { label: option, value: option, section: undefined }; + return { label: option, value: option }; } - return { label: option.label as string, value: option.value as string | number, section: option.section }; + return { label: option.label as string, value: option.value as string | number }; }); }; diff --git a/packages/trpc/server/routers/viewer/teams/_router.tsx b/packages/trpc/server/routers/viewer/teams/_router.tsx index dc7b5ddf4d..8301510019 100644 --- a/packages/trpc/server/routers/viewer/teams/_router.tsx +++ b/packages/trpc/server/routers/viewer/teams/_router.tsx @@ -123,13 +123,6 @@ export const viewerTeamsRouter = router({ const handler = await importHandler(namespaced("listMembers"), () => import("./listMembers.handler")); return handler(opts); }), - listSimpleMembers: authedProcedure.query(async (opts) => { - const handler = await importHandler( - namespaced("listSimpleMembers"), - () => import("./listSimpleMembers.handler") - ); - return handler(opts); - }), legacyListMembers: authedProcedure.input(ZLegacyListMembersInputSchema).query(async (opts) => { const handler = await importHandler( namespaced("legacyListMembers"), diff --git a/packages/trpc/server/routers/viewer/teams/listSimpleMembers.handler.ts b/packages/trpc/server/routers/viewer/teams/listSimpleMembers.handler.ts deleted file mode 100644 index cfa9a5b2b3..0000000000 --- a/packages/trpc/server/routers/viewer/teams/listSimpleMembers.handler.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Simplified version of legacyListMembers.handler.ts that returns basic member info. - * Used for filtering people on /bookings. - */ -import type { PrismaClient } from "@calcom/prisma"; -import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; - -type ListSimpleMembersOptions = { - ctx: { - user: NonNullable; - prisma: PrismaClient; - }; -}; - -export const listSimpleMembers = async ({ ctx }: ListSimpleMembersOptions) => { - const { prisma } = ctx; - const { isOrgAdmin } = ctx.user.organization; - const hasPermsToView = !ctx.user.organization.isPrivate || isOrgAdmin; - - if (!hasPermsToView) { - return []; - } - - // query all teams the user is a member of - const teamsToQuery = ( - await prisma.membership.findMany({ - where: { - userId: ctx.user.id, - accepted: true, - }, - select: { teamId: true }, - }) - ).map((membership) => membership.teamId); - - if (!teamsToQuery.length) { - return []; - } - - // Fetch unique users through memberships - const members = ( - await prisma.membership.findMany({ - where: { - accepted: true, - teamId: { in: teamsToQuery }, - }, - select: { - id: true, - accepted: true, - user: { - select: { - id: true, - name: true, - username: true, - avatarUrl: true, - email: true, - }, - }, - }, - distinct: ["userId"], - orderBy: [ - { userId: "asc" }, // First order by userId to ensure consistent ordering - { id: "asc" }, // Then by id as secondary sort - ], - }) - ).map((membership) => membership.user); - - return members; -}; - -export default listSimpleMembers;