import { useEffect, useMemo, useState } from "react"; import type { ReactNode } from "react"; import { ScopeId } from "@executor-js/sdk"; import { useScope, useUserScope } from "../api/scope-context"; import { CardStack, CardStackContent, CardStackEntry, CardStackEntryContent, CardStackEntryDescription, CardStackEntryTitle, } from "../components/card-stack"; import { FilterTabs } from "../components/filter-tabs"; import { FieldLabel } from "../components/field"; import { HelpTooltip } from "../components/help-tooltip"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../components/select"; export interface CredentialTargetScopeOption { readonly scopeId: ScopeId; readonly label: string; readonly description: string; } export const credentialTargetScopeOptions = (input: { readonly sourceScope: ScopeId; readonly userScope: ScopeId; readonly sourceScopeLabel?: string; }): readonly CredentialTargetScopeOption[] => { if (input.sourceScope === input.userScope) { return [ { scopeId: input.userScope, label: "Personal", description: "Saved only for your account.", }, ]; } return [ { scopeId: input.userScope, label: "Personal", description: "Saved only for your account.", }, { scopeId: input.sourceScope, label: input.sourceScopeLabel ?? "Organization", description: "Shared with everyone who can use this source.", }, ]; }; export const normalizeCredentialTargetScope = ( value: ScopeId, options: readonly CredentialTargetScopeOption[], ): ScopeId => options.find((option) => option.scopeId === value)?.scopeId ?? options[0]!.scopeId; export function useCredentialTargetScope(input?: { readonly sourceScope?: ScopeId; readonly sourceScopeLabel?: string; readonly initialTargetScope?: ScopeId; }): { readonly credentialTargetScope: ScopeId; readonly setCredentialTargetScope: (scope: ScopeId) => void; readonly credentialScopeOptions: readonly CredentialTargetScopeOption[]; } { const routeScope = useScope(); const sourceScope = input?.sourceScope ?? routeScope; const userScope = useUserScope(); const credentialScopeOptions = useMemo( () => credentialTargetScopeOptions({ sourceScope, userScope, sourceScopeLabel: input?.sourceScopeLabel, }), [sourceScope, userScope, input?.sourceScopeLabel], ); const initialTargetScope = input?.initialTargetScope ?? credentialScopeOptions[0]!.scopeId; const [credentialTargetScope, setCredentialTargetScope] = useState( normalizeCredentialTargetScope(initialTargetScope, credentialScopeOptions), ); useEffect(() => { setCredentialTargetScope((current) => normalizeCredentialTargetScope( current === routeScope && input?.sourceScope !== undefined ? initialTargetScope : current, credentialScopeOptions, ), ); }, [credentialScopeOptions, initialTargetScope, input?.sourceScope, routeScope]); return { credentialTargetScope, setCredentialTargetScope, credentialScopeOptions, }; } export function CredentialTargetScopeSelector(props: { readonly value: ScopeId; readonly options: readonly CredentialTargetScopeOption[]; readonly onChange: (scope: ScopeId) => void; readonly title?: string; readonly description?: string; }) { if (props.options.length <= 1) return null; const active = props.options.find((option) => option.scopeId === props.value); return ( {props.title ?? "Credentials"} {props.description ?? active?.description ?? "Choose where new credentials are saved."} tabs={props.options.map((option) => ({ value: option.scopeId, label: option.label, }))} value={props.value} onChange={props.onChange} /> ); } export function CredentialScopeSection(props: { readonly value: ScopeId; readonly options: readonly CredentialTargetScopeOption[]; readonly onChange: (scope: ScopeId) => void; readonly children: ReactNode; readonly title?: string; readonly description?: string; }) { return (
{props.children}
); } export function CredentialScopeDropdown(props: { readonly value: ScopeId; readonly options: readonly CredentialTargetScopeOption[]; readonly onChange: (scope: ScopeId) => void; readonly label?: string; readonly help?: ReactNode; readonly className?: string; readonly triggerClassName?: string; readonly size?: "sm" | "default"; }) { const label = props.label ?? "Used by"; if (props.options.length <= 1) return null; return (
{label} {props.help ?? "Choose who can use these credentials."}
); } export function CredentialControlField(props: { readonly label: string; readonly children: ReactNode; readonly help?: ReactNode; }) { return (
{props.label} {props.help ? {props.help} : null}
{props.children}
); } export function CredentialUsageRow(props: { readonly value: ScopeId; readonly options: readonly CredentialTargetScopeOption[]; readonly onChange: (scope: ScopeId) => void; readonly children: ReactNode; readonly label?: string; readonly help?: ReactNode; }) { if (props.options.length <= 1) { return
{props.children}
; } return (
{props.children}
); }