diff --git a/src/components/OrgIdpTable.tsx b/src/components/OrgIdpTable.tsx index bdbaafa27..c0199c6d3 100644 --- a/src/components/OrgIdpTable.tsx +++ b/src/components/OrgIdpTable.tsx @@ -25,7 +25,6 @@ import { import { ArrowRight, ArrowUpDown, - KeyRound, MoreHorizontal } from "lucide-react"; import { useMemo, useState } from "react"; @@ -50,6 +49,7 @@ import { useQuery } from "@tanstack/react-query"; import { useDebounce } from "use-debounce"; import type { ListUserAdminOrgIdpsResponse } from "@server/routers/orgIdp/types"; import { cn } from "@app/lib/cn"; +import { Badge } from "@app/components/ui/badge"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { isIdpGlobalModeBannerVisible } from "@app/components/IdpGlobalModeBanner"; @@ -63,6 +63,61 @@ export type IdpRow = { type AdminIdpRow = ListUserAdminOrgIdpsResponse["idps"][number]; +type ImportSourceOrg = { orgId: string; orgName: string }; + +type GroupedImportableIdp = { + idpId: number; + name: string; + type: string; + variant: string; + tags: string | null; + sources: ImportSourceOrg[]; +}; + +function adminRowForImport( + group: GroupedImportableIdp, + source: ImportSourceOrg +): AdminIdpRow { + return { + idpId: group.idpId, + orgId: source.orgId, + orgName: source.orgName, + name: group.name, + type: group.type, + variant: group.variant, + tags: group.tags + }; +} + +function groupImportableIdps(rows: AdminIdpRow[]): GroupedImportableIdp[] { + const map = new Map(); + for (const row of rows) { + let g = map.get(row.idpId); + if (!g) { + g = { + idpId: row.idpId, + name: row.name, + type: row.type, + variant: row.variant, + tags: row.tags, + sources: [] + }; + map.set(row.idpId, g); + } + if (!g.sources.some((s) => s.orgId === row.orgId)) { + g.sources.push({ orgId: row.orgId, orgName: row.orgName }); + } + } + return Array.from(map.values()) + .map((item) => ({ + ...item, + sources: [...item.sources].sort((a, b) => + a.orgName.localeCompare(b.orgName) + ) + })) + .sort((a, b) => b.name.localeCompare(a.name)); +} + function IdpImportRowIcon({ type, variant @@ -114,16 +169,22 @@ export default function IdpTable({ idps, orgId }: Props) { ); }, [adminIdpsRaw, orgId, idps]); - const shownImportIdps = useMemo(() => { + const importableGrouped = useMemo( + () => groupImportableIdps(importableIdps), + [importableIdps] + ); + + const shownImportGrouped = useMemo(() => { const q = debouncedImportSearch.trim().toLowerCase(); if (!q) { - return importableIdps; + return importableGrouped; } - return importableIdps.filter((row) => { - const hay = `${row.orgName} ${row.name}`.toLowerCase(); + return importableGrouped.filter((group) => { + const hay = + `${group.name} ${group.sources.map((s) => s.orgName).join(" ")}`.toLowerCase(); return hay.includes(q); }); - }, [importableIdps, debouncedImportSearch]); + }, [importableGrouped, debouncedImportSearch]); const deleteIdp = async (idpId: number) => { try { @@ -364,31 +425,44 @@ export default function IdpTable({ idps, orgId }: Props) { {t("idpImportEmpty")} - {shownImportIdps.map((row) => ( + {shownImportGrouped.map((group) => ( s.orgName).join(" ")}`} disabled={!canImportOrgOidcIdp} onSelect={() => { if (!canImportOrgOidcIdp) { return; } - void importIdp(row); + void importIdp( + adminRowForImport( + group, + group.sources[0] + ) + ); }} >
- {row.orgName} + {group.name}
-
- {row.name} +
+ {group.sources.map((src) => ( + + {src.orgName} + + ))}