consolidate orgidps in import list

This commit is contained in:
miloschwartz
2026-05-03 14:16:29 -07:00
parent 81b8a8a9e3
commit eb515a8f7f

View File

@@ -25,7 +25,6 @@ import {
import { import {
ArrowRight, ArrowRight,
ArrowUpDown, ArrowUpDown,
KeyRound,
MoreHorizontal MoreHorizontal
} from "lucide-react"; } from "lucide-react";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
@@ -50,6 +49,7 @@ import { useQuery } from "@tanstack/react-query";
import { useDebounce } from "use-debounce"; import { useDebounce } from "use-debounce";
import type { ListUserAdminOrgIdpsResponse } from "@server/routers/orgIdp/types"; import type { ListUserAdminOrgIdpsResponse } from "@server/routers/orgIdp/types";
import { cn } from "@app/lib/cn"; import { cn } from "@app/lib/cn";
import { Badge } from "@app/components/ui/badge";
import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { isIdpGlobalModeBannerVisible } from "@app/components/IdpGlobalModeBanner"; import { isIdpGlobalModeBannerVisible } from "@app/components/IdpGlobalModeBanner";
@@ -63,6 +63,61 @@ export type IdpRow = {
type AdminIdpRow = ListUserAdminOrgIdpsResponse["idps"][number]; 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<number, GroupedImportableIdp>();
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({ function IdpImportRowIcon({
type, type,
variant variant
@@ -114,16 +169,22 @@ export default function IdpTable({ idps, orgId }: Props) {
); );
}, [adminIdpsRaw, orgId, idps]); }, [adminIdpsRaw, orgId, idps]);
const shownImportIdps = useMemo(() => { const importableGrouped = useMemo(
() => groupImportableIdps(importableIdps),
[importableIdps]
);
const shownImportGrouped = useMemo(() => {
const q = debouncedImportSearch.trim().toLowerCase(); const q = debouncedImportSearch.trim().toLowerCase();
if (!q) { if (!q) {
return importableIdps; return importableGrouped;
} }
return importableIdps.filter((row) => { return importableGrouped.filter((group) => {
const hay = `${row.orgName} ${row.name}`.toLowerCase(); const hay =
`${group.name} ${group.sources.map((s) => s.orgName).join(" ")}`.toLowerCase();
return hay.includes(q); return hay.includes(q);
}); });
}, [importableIdps, debouncedImportSearch]); }, [importableGrouped, debouncedImportSearch]);
const deleteIdp = async (idpId: number) => { const deleteIdp = async (idpId: number) => {
try { try {
@@ -364,31 +425,44 @@ export default function IdpTable({ idps, orgId }: Props) {
{t("idpImportEmpty")} {t("idpImportEmpty")}
</CommandEmpty> </CommandEmpty>
<CommandGroup> <CommandGroup>
{shownImportIdps.map((row) => ( {shownImportGrouped.map((group) => (
<CommandItem <CommandItem
key={`${row.idpId}:${row.orgId}`} key={group.idpId}
className="items-start gap-3 py-2.5" className="items-start gap-3 py-2.5"
value={`${row.idpId}:${row.orgId}:${row.orgName}:${row.name}`} value={`${group.idpId}:${group.name}:${group.sources.map((s) => s.orgName).join(" ")}`}
disabled={!canImportOrgOidcIdp} disabled={!canImportOrgOidcIdp}
onSelect={() => { onSelect={() => {
if (!canImportOrgOidcIdp) { if (!canImportOrgOidcIdp) {
return; return;
} }
void importIdp(row); void importIdp(
adminRowForImport(
group,
group.sources[0]
)
);
}} }}
> >
<div className="mt-0.5 shrink-0"> <div className="mt-0.5 shrink-0">
<IdpImportRowIcon <IdpImportRowIcon
type={row.type} type={group.type}
variant={row.variant} variant={group.variant}
/> />
</div> </div>
<div className="min-w-0 flex-1 text-left"> <div className="min-w-0 flex-1 text-left">
<div className="truncate font-medium leading-tight"> <div className="truncate font-medium leading-tight">
{row.orgName} {group.name}
</div> </div>
<div className="truncate text-sm leading-tight text-muted-foreground"> <div className="mt-1 flex flex-wrap gap-1">
{row.name} {group.sources.map((src) => (
<Badge
key={src.orgId}
variant="secondary"
className="max-w-full truncate font-normal"
>
{src.orgName}
</Badge>
))}
</div> </div>
</div> </div>
</CommandItem> </CommandItem>