← back to Norma
components/drafts/DraftCard.tsx
121 lines
'use client';
import { useState } from 'react';
import { ChevronDown, ChevronRight } from 'lucide-react';
import { useOrg } from '@/components/OrgProvider';
import DraftEditor, { type DraftData, type DraftStatus } from './DraftEditor';
interface DraftCardProps {
draft: DraftData;
onDraftUpdated: (updated: Partial<DraftData>) => void;
}
const STATUS_STYLE: Record<DraftStatus, { cls: string; label: string }> = {
draft: { cls: 'badge-warning', label: 'Draft' },
review: { cls: 'badge', label: 'In Review' },
approved: { cls: 'badge-success', label: 'Approved' },
sent: { cls: 'badge-success', label: 'Sent' },
};
/* Strip HTML tags to a plain-text snippet */
function previewSnippet(html: string | undefined | null, maxLen = 100): string {
if (!html) return '';
const plain = html
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
if (plain.length <= maxLen) return plain;
return plain.slice(0, maxLen).trimEnd() + '...';
}
export default function DraftCard({ draft, onDraftUpdated }: DraftCardProps) {
const [expanded, setExpanded] = useState(false);
const { org } = useOrg();
const lanes = org?.lanes ?? {};
const lane = lanes[draft.lane] ?? { label: draft.lane, color: '#888', description: '' };
const laneClass = `lane-${draft.lane.toLowerCase()}` as 'lane-a' | 'lane-b' | 'lane-c';
const statusMeta = STATUS_STYLE[draft.status] ?? STATUS_STYLE.draft;
const snippet = previewSnippet(draft.body_html);
if (expanded) {
return (
<DraftEditor
draft={draft}
onClose={() => setExpanded(false)}
onDraftUpdated={onDraftUpdated}
/>
);
}
return (
<button
type="button"
onClick={() => setExpanded(true)}
className="w-full text-left hover-lift focus-ring"
style={{
backgroundColor: 'var(--color-surface)',
border: '1px solid var(--color-border)',
borderTop: `3px solid ${lane.color}`,
borderRadius: 'var(--radius-md)',
padding: '0.625rem 0.75rem 0.625rem',
cursor: 'pointer',
display: 'block',
outline: 'none',
}}
aria-label={`Open editor for Lane ${draft.lane}: ${draft.subject}`}
>
<div className="flex items-center gap-2 min-w-0">
{/* Lane badge — left */}
<span className={`badge ${laneClass} shrink-0`} aria-label={`Lane ${draft.lane}`}>
{draft.lane}
</span>
{/* Subject + snippet — center */}
<div className="flex-1 min-w-0 text-left">
<p
className="text-sm font-medium truncate"
style={{ color: 'var(--color-text)' }}
>
{draft.subject || (
<span style={{ color: 'var(--color-text-muted)' }}>
{lane.label} — No subject yet
</span>
)}
</p>
{snippet && (
<p
className="text-xs truncate mt-0.5"
style={{ color: 'var(--color-text-muted)' }}
>
{snippet}
</p>
)}
</div>
{/* Status badge — right */}
<div className="flex items-center gap-2 shrink-0">
<span
className={`badge ${statusMeta.cls}`}
style={{ letterSpacing: '0.05em', fontSize: '0.7rem' }}
>
{statusMeta.label}
</span>
<ChevronRight
size={14}
style={{ color: 'var(--color-text-muted)' }}
aria-hidden="true"
/>
</div>
</div>
{/* Lane description sub-line */}
<p className="text-xs mt-1 ml-10" style={{ color: 'var(--color-text-muted)' }}>
{lane.description}
</p>
</button>
);
}
/* Named export for the chevron used in SessionCard collapse */
export { ChevronDown };