"use client"; import { useEffect, useState } from "react"; import { Button } from "@app/components/ui/button"; import { Badge } from "@app/components/ui/badge"; import { Alert, AlertDescription } from "@app/components/ui/alert"; import { useTranslations } from "next-intl"; import LoginPasswordForm from "@app/components/LoginPasswordForm"; import { LookupUserResponse } from "@server/routers/auth/lookupUser"; import UserProfileCard from "@app/components/UserProfileCard"; import IdpTypeIcon from "@app/components/IdpTypeIcon"; import { generateOidcUrlProxy } from "@app/actions/server"; import { redirect as redirectTo, useRouter, useSearchParams } from "next/navigation"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import { Separator } from "@app/components/ui/separator"; type SmartLoginOrgSelectorProps = { identifier: string; lookupResult: LookupUserResponse; redirect?: string; forceLogin?: boolean; onUseDifferentAccount?: () => void; }; type OrgBucket = { orgId: string; orgName: string; idps: Array<{ idpId: number; name: string; variant: string | null; }>; hasInternalAuth: boolean; }; type GroupedLoginIdp = { idpId: number; name: string; variant: string | null; orgs: { orgId: string; orgName: string }[]; }; function buildOrgMap(lookupResult: LookupUserResponse) { const orgMap = new Map(); for (const account of lookupResult.accounts) { for (const org of account.orgs) { if (!orgMap.has(org.orgId)) { orgMap.set(org.orgId, { orgId: org.orgId, orgName: org.orgName, idps: org.idps, hasInternalAuth: org.hasInternalAuth }); } else { const existing = orgMap.get(org.orgId)!; const existingIdpIds = new Set( existing.idps.map((i) => i.idpId) ); for (const idp of org.idps) { if (!existingIdpIds.has(idp.idpId)) { existing.idps.push(idp); } } if (org.hasInternalAuth) { existing.hasInternalAuth = true; } } } } return Array.from(orgMap.values()); } function groupIdpsAcrossOrgs(orgs: OrgBucket[]): GroupedLoginIdp[] { const map = new Map(); for (const org of orgs) { for (const idp of org.idps) { let g = map.get(idp.idpId); if (!g) { g = { idpId: idp.idpId, name: idp.name, variant: idp.variant, orgs: [] }; map.set(idp.idpId, g); } if (!g.orgs.some((o) => o.orgId === org.orgId)) { g.orgs.push({ orgId: org.orgId, orgName: org.orgName }); } } } return Array.from(map.values()) .map((g) => ({ ...g, orgs: [...g.orgs].sort((a, b) => a.orgName.localeCompare(b.orgName)) })) .sort((a, b) => b.name.localeCompare(a.name)); } export default function SmartLoginOrgSelector({ identifier, lookupResult, redirect, forceLogin, onUseDifferentAccount }: SmartLoginOrgSelectorProps) { const t = useTranslations(); const [showPasswordForm, setShowPasswordForm] = useState(false); const [error, setError] = useState(null); const [pendingIdpId, setPendingIdpId] = useState(null); const params = useSearchParams(); const router = useRouter(); const orgs = buildOrgMap(lookupResult); const groupedIdps = groupIdpsAcrossOrgs(orgs); const hasInternalAccount = lookupResult.accounts.some( (acc) => acc.hasInternalAuth ); function goToApp() { const url = window.location.href.split("?")[0]; router.push(url); } useEffect(() => { if (params.get("gotoapp")) { goToApp(); } }, []); async function loginWithIdp(idpId: number, orgId: string) { setPendingIdpId(idpId); setError(null); let redirectToUrl: string | undefined; try { const safeRedirect = cleanRedirect(redirect || "/"); const response = await generateOidcUrlProxy( idpId, safeRedirect, undefined, forceLogin ); if (response.error) { setError(response.message); setPendingIdpId(null); return; } const data = response.data; if (data?.redirectUrl) { redirectToUrl = data.redirectUrl; } } catch { setError( t("loginError", { defaultValue: "An unexpected error occurred. Please try again." }) ); } if (redirectToUrl) { redirectTo(redirectToUrl); } else { setPendingIdpId(null); } } if (showPasswordForm) { return (
); } return (
{hasInternalAccount && (
)} {groupedIdps.length > 0 ? (
{error && ( {error} )}
{t("idpContinue")}
{params.get("gotoapp") ? ( ) : ( groupedIdps.map((group) => { const effectiveType = group.variant || group.name.toLowerCase(); const sourceOrgId = group.orgs[0].orgId; return ( ); }) )}
) : null}
); }