diff --git a/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx b/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx index 2fa140478b..deefd91177 100644 --- a/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx @@ -1,6 +1,7 @@ import SettingsLayoutAppDir from "app/(use-page-wrapper)/settings/(settings-layout)/layout"; import { _generateMetadata, getTranslate } from "app/_utils"; +import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils"; import LegacyPage from "@calcom/features/ee/organizations/pages/members"; import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; @@ -14,7 +15,10 @@ const Page = async () => { const t = await getTranslate(); const children = ( - + ); diff --git a/apps/web/modules/insights/insights-routing-view.tsx b/apps/web/modules/insights/insights-routing-view.tsx index 3d830a8076..7406bf8830 100644 --- a/apps/web/modules/insights/insights-routing-view.tsx +++ b/apps/web/modules/insights/insights-routing-view.tsx @@ -1,6 +1,6 @@ "use client"; -import { DataTableProvider } from "@calcom/features/data-table"; +import { DataTableProvider } from "@calcom/features/data-table/DataTableProvider"; import { RoutingFormResponsesTable, FailedBookingsByField, diff --git a/apps/web/modules/members/members-view.tsx b/apps/web/modules/members/members-view.tsx index 81b8e2af06..3af6de5c17 100644 --- a/apps/web/modules/members/members-view.tsx +++ b/apps/web/modules/members/members-view.tsx @@ -1,5 +1,6 @@ "use client"; +import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils"; import MembersView from "@calcom/features/ee/organizations/pages/members"; import Shell from "@calcom/features/shell/Shell"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -11,7 +12,10 @@ const MembersPage: React.FC = () => { withoutMain={false} title={t("organization_members")} description={t("organization_description")} - subtitle={t("organization_description")}> + heading={t("organization_members")} + subtitle={t("organization_description")} + headerClassName="hidden md:block" + actions={
}> ); diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index daa2e4be7d..281f1cb7a5 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -3035,6 +3035,7 @@ "tip_username_plus": "Tip: You can a '+' between usernames: cal.com/anna+brian to make a dynamic group meeting", "user_has_no_team_yet": "You don't have a team yet", "no_team_members": "You don't have team members yet", + "display": "Display", "could_not_find_slug_to_publish_org": "Could not find slug to publish the organization", "picklist": "Picklist", "name_or_email": "Name or Email", diff --git a/packages/features/data-table/lib/context.tsx b/packages/features/data-table/DataTableProvider.tsx similarity index 85% rename from packages/features/data-table/lib/context.tsx rename to packages/features/data-table/DataTableProvider.tsx index 695f36369f..458650dea0 100644 --- a/packages/features/data-table/lib/context.tsx +++ b/packages/features/data-table/DataTableProvider.tsx @@ -2,10 +2,10 @@ import type { SortingState, OnChangeFn, VisibilityState } from "@tanstack/react-table"; import { useQueryState, parseAsArrayOf, parseAsJson, parseAsInteger } from "nuqs"; -import { createContext, useCallback } from "react"; +import { createContext, useCallback, useRef, useEffect } from "react"; import { z } from "zod"; -import { type FilterValue, ZFilterValue, ZSorting, ZColumnVisibility } from "./types"; +import { type FilterValue, ZFilterValue, ZSorting, ZColumnVisibility } from "./lib/types"; const ZActiveFilter = z.object({ f: z.string(), @@ -15,6 +15,8 @@ const ZActiveFilter = z.object({ type ActiveFilter = z.infer; export type DataTableContextType = { + ctaContainerRef?: React.RefObject; + activeFilters: ActiveFilter[]; clearAll: (exclude?: string[]) => void; addFilter: (columnId: string) => void; @@ -45,10 +47,15 @@ const DEFAULT_PAGE_SIZE = 10; interface DataTableProviderProps { children: React.ReactNode; + ctaContainerClassName?: string; defaultPageSize?: number; } -export function DataTableProvider({ children, defaultPageSize = DEFAULT_PAGE_SIZE }: DataTableProviderProps) { +export function DataTableProvider({ + children, + defaultPageSize = DEFAULT_PAGE_SIZE, + ctaContainerClassName, +}: DataTableProviderProps) { const [activeFilters, setActiveFilters] = useQueryState( "activeFilters", parseAsArrayOf(parseAsJson(ZActiveFilter.parse)).withDefault(DEFAULT_ACTIVE_FILTERS) @@ -119,9 +126,18 @@ export function DataTableProvider({ children, defaultPageSize = DEFAULT_PAGE_SIZ [setPageSize, setPageIndex] ); + const ctaContainerRef = useRef(null); + useEffect(() => { + if (ctaContainerClassName) { + const element = document.getElementsByClassName(ctaContainerClassName)[0] as HTMLDivElement; + ctaContainerRef.current = element; + } + }, [ctaContainerClassName]); + return ( ( { children, color = "secondary", - EndIcon = "sliders-vertical", + StartIcon = "sliders-vertical", table, ...rest }: ColumnVisiblityProps & ButtonProps, @@ -41,8 +41,8 @@ function ColumnVisibilityButtonComponent( return ( - diff --git a/packages/features/data-table/hooks/useDataTable.ts b/packages/features/data-table/hooks/useDataTable.ts index c62ecfa9ee..4a2a244445 100644 --- a/packages/features/data-table/hooks/useDataTable.ts +++ b/packages/features/data-table/hooks/useDataTable.ts @@ -1,6 +1,6 @@ import { useContext } from "react"; -import { DataTableContext } from "../lib/context"; +import { DataTableContext } from "../DataTableProvider"; export function useDataTable() { const context = useContext(DataTableContext); diff --git a/packages/features/data-table/index.ts b/packages/features/data-table/index.ts index 22eb20527b..3e592dc193 100644 --- a/packages/features/data-table/index.ts +++ b/packages/features/data-table/index.ts @@ -1,6 +1,6 @@ export * from "./components"; export * from "./lib/types"; export * from "./lib/utils"; -export * from "./lib/context"; +export * from "./DataTableProvider"; export * from "./lib/resizing"; export * from "./hooks"; diff --git a/packages/features/data-table/lib/resizing.ts b/packages/features/data-table/lib/resizing.ts index ab677598e7..1a6c84980a 100644 --- a/packages/features/data-table/lib/resizing.ts +++ b/packages/features/data-table/lib/resizing.ts @@ -1,3 +1,5 @@ +"use client"; + import type { Header, Table, ColumnSizingState } from "@tanstack/react-table"; // eslint-disable-next-line no-restricted-imports import debounce from "lodash/debounce"; diff --git a/packages/features/data-table/lib/utils.ts b/packages/features/data-table/lib/utils.ts index e689d64046..7e1e008e85 100644 --- a/packages/features/data-table/lib/utils.ts +++ b/packages/features/data-table/lib/utils.ts @@ -1,5 +1,3 @@ -"use client"; - import type { SingleSelectFilterValue, MultiSelectFilterValue, @@ -166,3 +164,5 @@ export const convertMapToFacetedValues = (map: Map | undef return { label: option.label as string, value: option.value as string | number, section: option.section }; }); }; + +export const CTA_CONTAINER_CLASS_NAME = "data_table_cta_container"; diff --git a/packages/features/ee/platform/pages/settings/members.tsx b/packages/features/ee/platform/pages/settings/members.tsx index a70c57ebd4..20b9704ab3 100644 --- a/packages/features/ee/platform/pages/settings/members.tsx +++ b/packages/features/ee/platform/pages/settings/members.tsx @@ -1,5 +1,6 @@ "use client"; +import { CTA_CONTAINER_CLASS_NAME } from "@calcom/features/data-table/lib/utils"; import Shell from "@calcom/features/shell/Shell"; import { UserListTable } from "@calcom/features/users/components/UserTable/UserListTable"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -66,7 +67,8 @@ const PlatformMembersView = () => { title={t("platform_members")} subtitle={t("platform_members_description")} withoutMain={false} - isPlatformUser={true}> + isPlatformUser={true} + actions={
}>
{!isPending && canLoggedInUserSeeMembers && }
); diff --git a/packages/features/settings/appDir/SettingsHeader.tsx b/packages/features/settings/appDir/SettingsHeader.tsx index 8a43d09e3e..7ab520def4 100644 --- a/packages/features/settings/appDir/SettingsHeader.tsx +++ b/packages/features/settings/appDir/SettingsHeader.tsx @@ -1,13 +1,14 @@ import React, { Suspense } from "react"; -import { Icon } from "@calcom/ui/components/icon"; import classNames from "@calcom/ui/classNames"; +import { Icon } from "@calcom/ui/components/icon"; interface HeaderProps { children: React.ReactNode; title?: string; description?: string; CTA?: React.ReactNode; + ctaClassName?: string; borderInShellHeader?: boolean; backButton?: boolean; } @@ -17,6 +18,7 @@ export default function Header({ title, description, CTA, + ctaClassName, borderInShellHeader, backButton, }: HeaderProps) { @@ -28,27 +30,29 @@ export default function Header({ borderInShellHeader && "rounded-t-lg border px-4 py-6 sm:px-6", borderInShellHeader === undefined && "mb-8 border-b pb-8" )}> -
- {backButton && ( - - - - )} -
- {title ? ( -

- {title} -

- ) : ( -
- )} - {description ? ( -

{description}

- ) : ( -
+
+
+ {backButton && ( + + + )} +
+ {title ? ( +

+ {title} +

+ ) : ( +
+ )} + {description ? ( +

{description}

+ ) : ( +
+ )} +
-
{CTA}
+
{CTA}
}>{children} diff --git a/packages/features/users/components/UserTable/UserListTable.tsx b/packages/features/users/components/UserTable/UserListTable.tsx index 44c8e1b590..96cea2033f 100644 --- a/packages/features/users/components/UserTable/UserListTable.tsx +++ b/packages/features/users/components/UserTable/UserListTable.tsx @@ -5,10 +5,11 @@ import { getCoreRowModel, getSortedRowModel, useReactTable, type ColumnDef } fro import { useSession } from "next-auth/react"; import { useQueryState, parseAsBoolean } from "nuqs"; import { useMemo, useReducer, useState } from "react"; +import { createPortal } from "react-dom"; import { - DataTableWrapper, DataTableProvider, + DataTableWrapper, DataTableToolbar, DataTableSelectionBar, DataTableFilters, @@ -16,6 +17,7 @@ import { ColumnFilterType, convertFacetedValuesToMap, useDataTable, + CTA_CONTAINER_CLASS_NAME, } from "@calcom/features/data-table"; import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { WEBAPP_URL } from "@calcom/lib/constants"; @@ -101,7 +103,7 @@ function reducer(state: UserTableState, action: UserTableAction): UserTableState export function UserListTable() { return ( - + ); @@ -138,7 +140,7 @@ function UserListTableContent() { const columnFilters = useColumnFilters(); - const { limit, offset } = useDataTable(); + const { limit, offset, ctaContainerRef } = useDataTable(); const { data, isPending } = trpc.viewer.organizations.listMembers.useQuery( { @@ -527,40 +529,9 @@ function UserListTableContent() { - } - ToolbarRight={ - <> - handleDownload()} - data-testid="export-members-button"> - {t("download")} - - - {adminOrOwner && ( - - dispatch({ - type: "INVITE_MEMBER", - payload: { - showModal: true, - }, - }) - } - data-testid="new-organization-member-button"> - {t("add")} - - )} - - }> + ToolbarRight={}> {numberOfSelectedRows >= 2 && dynamicLinkVisible && ( @@ -603,6 +574,40 @@ function UserListTableContent() { {state.impersonateMember.showModal && } {state.changeMemberRole.showModal && } {state.editSheet.showModal && } + + {ctaContainerRef?.current && + createPortal( +
+ handleDownload()} + data-testid="export-members-button"> + {t("download")} + + + {adminOrOwner && ( + + dispatch({ + type: "INVITE_MEMBER", + payload: { + showModal: true, + }, + }) + } + data-testid="new-organization-member-button"> + {t("add")} + + )} +
, + ctaContainerRef.current + )} ); }