dwijverma2's picture
Add component: ControlBar
2b2a0e9 verified
"use client";
import { useState } from "react";
export default function ControlBar({
onEnrich,
onApplyChanges,
onDownload,
enriched,
loading,
loadingMessage,
headingFontSize,
onFontSizeChange,
classifications,
}) {
const [showPromptEditor, setShowPromptEditor] = useState(false);
const [customPrompt, setCustomPrompt] = useState("");
const [model, setModel] = useState("llama3");
const headingCount = classifications.filter((c) => c.isHeading).length;
const handleEnrich = () => {
onEnrich(model, customPrompt || undefined);
};
const handleApplyManualChanges = () => {
const headingIndices = classifications
.filter((c) => c.isHeading)
.map((c) => c.index);
onApplyChanges(headingIndices);
};
return (
<div
className="border-b px-5 py-3"
style={{
background: "var(--bg-primary)",
borderColor: "var(--border-color)",
}}
>
{/* Main controls row */}
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
{/* Enrich / Re-enrich button */}
<button
onClick={handleEnrich}
disabled={loading}
className="text-sm font-medium px-4 py-2 rounded-lg transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
style={{
background: "var(--accent)",
color: "var(--bg-secondary)",
}}
onMouseEnter={(e) => {
if (!loading) e.target.style.background = "var(--accent-hover)";
}}
onMouseLeave={(e) => {
e.target.style.background = "var(--accent)";
}}
>
{enriched ? "⟳ Re-Enrich with AI" : "✦ Enrich with AI"}
</button>
{/* Apply manual changes */}
{enriched && (
<button
onClick={handleApplyManualChanges}
disabled={loading}
className="text-sm px-4 py-2 rounded-lg transition-colors cursor-pointer disabled:opacity-50"
style={{
background: "var(--bg-tertiary)",
color: "var(--text-primary)",
}}
onMouseEnter={(e) => {
if (!loading) e.target.style.background = "var(--bg-hover)";
}}
onMouseLeave={(e) => {
e.target.style.background = "var(--bg-tertiary)";
}}
>
Apply Changes
</button>
)}
{/* Download button */}
{enriched && (
<button
onClick={onDownload}
disabled={loading}
className="text-sm px-4 py-2 rounded-lg transition-colors cursor-pointer disabled:opacity-50"
style={{
background: "var(--bg-tertiary)",
color: "var(--success)",
}}
onMouseEnter={(e) => {
if (!loading) e.target.style.background = "var(--bg-hover)";
}}
onMouseLeave={(e) => {
e.target.style.background = "var(--bg-tertiary)";
}}
>
↓ Download Enriched
</button>
)}
</div>
<div className="flex items-center gap-4">
{/* Heading count badge */}
{enriched && (
<span
className="text-xs px-2.5 py-1 rounded-full"
style={{
background: "var(--heading-highlight)",
color: "var(--accent)",
}}
>
{headingCount} heading{headingCount !== 1 ? "s" : ""} detected
</span>
)}
{/* Font size control */}
<div className="flex items-center gap-2">
<label
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
Heading size:
</label>
<select
value={headingFontSize}
onChange={(e) => onFontSizeChange(Number(e.target.value))}
className="text-xs px-2 py-1 rounded border outline-none cursor-pointer"
style={{
background: "var(--bg-tertiary)",
borderColor: "var(--border-color)",
color: "var(--text-primary)",
}}
>
<option value={14}>14pt</option>
<option value={16}>16pt</option>
<option value={18}>18pt</option>
<option value={20}>20pt</option>
<option value={22}>22pt</option>
<option value={24}>24pt</option>
<option value={28}>28pt</option>
</select>
</div>
{/* Model selector */}
<div className="flex items-center gap-2">
<label
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
Model:
</label>
<input
type="text"
value={model}
onChange={(e) => setModel(e.target.value)}
className="text-xs px-2 py-1 rounded border outline-none w-28"
style={{
background: "var(--bg-tertiary)",
borderColor: "var(--border-color)",
color: "var(--text-primary)",
}}
placeholder="llama3"
/>
</div>
{/* Custom prompt toggle */}
<button
onClick={() => setShowPromptEditor(!showPromptEditor)}
className="text-xs px-3 py-1.5 rounded-lg transition-colors cursor-pointer"
style={{
background: showPromptEditor
? "var(--accent)"
: "var(--bg-tertiary)",
color: showPromptEditor
? "var(--bg-secondary)"
: "var(--text-secondary)",
}}
>
{showPromptEditor ? "Hide Prompt" : "Custom Prompt"}
</button>
</div>
</div>
{/* Custom prompt editor (collapsible) */}
{showPromptEditor && (
<div className="mt-3 pt-3 border-t" style={{ borderColor: "var(--border-color)" }}>
<label
className="text-xs block mb-2"
style={{ color: "var(--text-secondary)" }}
>
Custom system prompt for heading detection (leave empty for default):
</label>
<textarea
value={customPrompt}
onChange={(e) => setCustomPrompt(e.target.value)}
rows={4}
className="w-full text-xs px-3 py-2 rounded-lg border outline-none resize-y"
style={{
background: "var(--bg-tertiary)",
borderColor: "var(--border-color)",
color: "var(--text-primary)",
}}
placeholder={`You are a document structure analyzer specializing in SOP documents.\n\nYour task: Given a list of numbered paragraphs, identify which ones are HEADINGS vs body text.\n\nRespond with ONLY a JSON object: {"headings": [array of index numbers]}`}
/>
<p className="text-xs mt-1" style={{ color: "var(--text-muted)" }}>
The prompt must instruct the model to return JSON with a
&quot;headings&quot; array of paragraph index numbers.
</p>
</div>
)}
</div>
);
}