import { useState, type ReactNode } from "react"; import { PlusIcon } from "lucide-react"; import type { ScopeId } from "@executor-js/sdk"; import { Button } from "../components/button"; import { CardStack, CardStackContent, CardStackEmpty, CardStackEntry, } from "../components/card-stack"; import { defaultHeaderAuthPresets, type HeaderAuthPreset, type HeaderState, SecretHeaderAuthRow, } from "./secret-header-auth"; import type { SecretPickerSecret } from "./secret-picker"; export interface HeadersListProps { readonly headers: readonly HeaderState[]; readonly onHeadersChange: (headers: HeaderState[]) => void; readonly existingSecrets?: readonly SecretPickerSecret[]; /** Presets offered in the quick-add picker. Defaults to `defaultHeaderAuthPresets`. */ readonly presets?: readonly HeaderAuthPreset[]; /** When true, only allow a single header (hide add button, disable remove). */ readonly singleHeader?: boolean; /** Text shown in the empty state. */ readonly emptyLabel?: ReactNode; /** * Display name of the source that owns these headers (e.g. "Axiom"). Used * to derive unique default secret labels/IDs like `axiom-authorization`. */ readonly sourceName?: string; /** When provided, inline-created secrets are written to this scope. */ readonly targetScope?: ScopeId; /** Alias for `targetScope`, used by source flows that choose a write scope. */ readonly writeScope?: ScopeId; } export function HeadersList({ headers, onHeadersChange, existingSecrets = [], presets = defaultHeaderAuthPresets, singleHeader = false, emptyLabel = "No headers", sourceName, targetScope, writeScope, }: HeadersListProps) { const [picking, setPicking] = useState(false); const canAddMore = !singleHeader || headers.length === 0; const addHeaderFromPreset = (preset: HeaderAuthPreset) => { onHeadersChange([ ...headers, { name: preset.name, prefix: preset.prefix, presetKey: preset.key, secretId: null, }, ]); setPicking(false); }; const updateHeader = ( index: number, update: Partial<{ name: string; secretId: string | null; prefix?: string; presetKey?: string; }>, ) => { onHeadersChange( headers.map((entry, i) => (i === index ? { ...entry, ...update } : entry)), ); }; const removeHeader = (index: number) => { onHeadersChange(headers.filter((_, i) => i !== index)); }; return ( {picking ? ( setPicking(false)} /> ) : headers.length === 0 ? ( canAddMore ? ( {emptyLabel}} onClick={() => setPicking(true)} /> ) : ( {emptyLabel} ) ) : ( <> {headers.map((header, index) => ( updateHeader(index, update)} onSelectSecret={(secretId) => updateHeader(index, { secretId })} onRemove={singleHeader ? undefined : () => removeHeader(index)} existingSecrets={existingSecrets} sourceName={sourceName} targetScope={targetScope ?? writeScope} /> ))} {canAddMore && setPicking(true)} />} )} ); } interface AddHeaderRowProps { readonly onClick: () => void; readonly leading?: ReactNode; } function AddHeaderRow({ onClick, leading }: AddHeaderRowProps) { return ( // oxlint-disable-next-line react/forbid-elements ); } interface HeaderPresetPickerProps { readonly presets: readonly HeaderAuthPreset[]; readonly onPick: (preset: HeaderAuthPreset) => void; readonly onCancel: () => void; } function HeaderPresetPicker({ presets, onPick, onCancel }: HeaderPresetPickerProps) { return ( {presets.map((preset) => ( ))} ); }