← back to Norma
components/drafts/DraftToolbar.tsx
142 lines
'use client';
import { useState } from 'react';
import { Save, Layers, History, Send, Check } from 'lucide-react';
import ExportMenu from '@/components/shared/ExportMenu';
import { useToast } from '@/components/ToastProvider';
interface DraftToolbarProps {
draftId: string;
isSaving: boolean;
isDirty: boolean;
onSave: () => void;
onToggleBoilerplate: () => void;
onToggleVersionHistory: () => void;
showVersionHistory: boolean;
}
export default function DraftToolbar({
draftId,
isSaving,
isDirty,
onSave,
onToggleBoilerplate,
onToggleVersionHistory,
showVersionHistory,
}: DraftToolbarProps) {
const { addToast } = useToast();
const [testSending, setTestSending] = useState(false);
const [testSent, setTestSent] = useState(false);
async function handleTestSend() {
setTestSending(true);
setTestSent(false);
try {
const res = await fetch(`/api/drafts/${draftId}/test-send`, { method: 'POST' });
if (!res.ok) throw new Error('Test send failed');
setTestSent(true);
setTimeout(() => setTestSent(false), 3000);
} catch {
addToast('Test send failed. Check server logs.', 'error');
} finally {
setTestSending(false);
}
}
return (
<div
className="flex items-center justify-between gap-2 px-3 py-2 flex-wrap"
style={{
borderTop: '1px solid var(--color-border)',
backgroundColor: 'var(--color-surface)',
}}
role="toolbar"
aria-label="Draft actions"
>
{/* Left group */}
<div className="flex items-center gap-1.5 flex-wrap">
{/* Save */}
<button
type="button"
onClick={onSave}
disabled={isSaving || !isDirty}
className="btn btn-primary btn-sm"
title={isSaving ? 'Saving...' : isDirty ? 'Save changes' : 'No unsaved changes'}
aria-label={isSaving ? 'Saving' : 'Save'}
>
{isSaving ? (
<span className="spinner" style={{ width: 12, height: 12, borderWidth: 1.5 }} />
) : (
<Save size={13} aria-hidden="true" />
)}
<span className="hidden sm:inline">{isSaving ? 'Saving...' : 'Save'}</span>
</button>
{/* Boilerplate */}
<button
type="button"
onClick={onToggleBoilerplate}
className="btn btn-secondary btn-sm"
title="Insert boilerplate block"
aria-label="Boilerplate blocks"
>
<Layers size={13} aria-hidden="true" />
<span className="hidden sm:inline">Boilerplate</span>
</button>
{/* Version History */}
<button
type="button"
onClick={onToggleVersionHistory}
className="btn btn-secondary btn-sm"
aria-pressed={showVersionHistory}
title="Version history"
aria-label="Version history"
>
<History size={13} aria-hidden="true" />
<span className="hidden sm:inline">History</span>
</button>
{/* Export */}
<ExportMenu draftId={draftId} />
</div>
{/* Right group */}
<div className="flex items-center gap-1.5">
{isDirty && !isSaving && (
<span className="text-xs" style={{ color: 'var(--color-warning)' }}>
Unsaved changes
</span>
)}
{!isDirty && !isSaving && (
<span className="text-xs" style={{ color: 'var(--color-text-muted)' }}>
All changes saved
</span>
)}
{/* Test Send */}
<button
type="button"
onClick={handleTestSend}
disabled={testSending}
className={`btn btn-sm ${testSent ? 'btn-secondary' : 'btn-secondary'}`}
title="Send test email"
aria-label="Send test email"
style={testSent ? { color: 'var(--color-success)', borderColor: 'var(--color-success)' } : {}}
>
{testSending ? (
<span className="spinner" style={{ width: 12, height: 12, borderWidth: 1.5 }} />
) : testSent ? (
<Check size={13} aria-hidden="true" />
) : (
<Send size={13} aria-hidden="true" />
)}
<span className="hidden sm:inline">
{testSending ? 'Sending...' : testSent ? 'Sent!' : 'Test Send'}
</span>
</button>
</div>
</div>
);
}