harryagasi
fix: keep suggested questions reachable, simplify sources control, fix dark-mode logo contrast
6b435ec
Raw
History Blame Contribute Delete
4.55 kB
import { useState } from "react";
import { AlertTriangle, Database, Loader2, Save } from "lucide-react";
import type { Analysis, DataBindItem } from "@/services/orchestrationApi";
import { DataBindSelector } from "./DataBindSelector";
interface AnalysisHeaderProps {
analysis: Analysis | null;
staleSources?: DataBindItem[];
onUpdateDataBind: (items: DataBindItem[]) => Promise<void>;
}
export function AnalysisHeader({ analysis, staleSources = [], onUpdateDataBind }: AnalysisHeaderProps) {
const [editingSources, setEditingSources] = useState(false);
const [draftBind, setDraftBind] = useState<DataBindItem[]>(analysis?.data_bind ?? []);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
if (!analysis) {
return (
<header className="border-b border-slate-200 bg-white px-5 py-4">
<p className="text-sm font-medium text-slate-900">Select or create an analysis</p>
<p className="text-xs text-slate-500">Knowledge setup comes first, then analysis, then agent conversation.</p>
</header>
);
}
const save = async () => {
setSaving(true);
setError(null);
try {
await onUpdateDataBind(draftBind);
setEditingSources(false);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to update sources");
} finally {
setSaving(false);
}
};
const openSourceEditor = () => {
setDraftBind(analysis.data_bind);
setEditingSources((open) => !open);
};
return (
<header className="border-b border-slate-200 bg-white px-5 py-3">
<div className="flex items-center justify-between gap-3">
<h1 className="min-w-0 truncate text-sm font-semibold text-slate-950" title={analysis.objective}>
{analysis.analysis_title}
</h1>
<div className="flex flex-shrink-0 items-center gap-2 text-xs text-slate-500">
<button
type="button"
title="Update bound knowledge sources for this analysis"
onClick={openSourceEditor}
className="inline-flex items-center gap-1 rounded-md border border-slate-200 px-2 py-1 hover:bg-slate-50 hover:text-slate-800"
>
<Database className="h-3.5 w-3.5" />
{analysis.data_bind.length} source{analysis.data_bind.length !== 1 ? "s" : ""}
</button>
</div>
</div>
{staleSources.length > 0 && (
<div className="mt-4 flex flex-col gap-3 rounded-md border border-amber-200 bg-amber-50 px-3 py-3 text-sm text-amber-900 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-start gap-2">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0" />
<div>
<p className="font-medium">Some bound sources are no longer available.</p>
<p className="mt-1 text-xs leading-5 text-amber-800">
Update sources before relying on this analysis: {staleSources.map((source) => source.name).join(", ")}.
</p>
</div>
</div>
<button
type="button"
title="Open source binding editor"
onClick={() => {
setDraftBind(analysis.data_bind);
setEditingSources(true);
}}
className="rounded-md bg-amber-900 px-3 py-2 text-xs font-medium text-white hover:bg-amber-800"
>
Update binding
</button>
</div>
)}
{editingSources && (
<div className="mt-4 rounded-lg border border-slate-200 bg-slate-50 p-3">
<DataBindSelector value={draftBind} onChange={setDraftBind} disabled={saving} />
{error && <p className="mt-2 text-xs text-red-600">{error}</p>}
<div className="mt-3 flex justify-end gap-2">
<button type="button" onClick={() => setEditingSources(false)} className="rounded-md px-3 py-2 text-sm text-slate-600 hover:bg-white">
Cancel
</button>
<button
type="button"
onClick={save}
disabled={saving || draftBind.length === 0}
className="inline-flex items-center gap-2 rounded-md bg-slate-900 px-3 py-2 text-sm font-medium text-white hover:bg-slate-800 disabled:opacity-50"
>
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
Save sources
</button>
</div>
</div>
)}
</header>
);
}