// DocumentTimeline — línea de tiempo de eventos de negocio del documento // («creó», «timbró», «envió a…», «abrió», «pagó», «solicitó cancelación», // «respuesta del SAT»). Complementa a (que muestra diffs de // campos): aquí cada entrada es un hecho legible, con actor y hora. import { useTranslation } from 'react-i18next' import { Circle } from 'lucide-react' import { cn } from '@asteby/metacore-ui/lib' import { DynamicIcon } from '../dynamic-icon' import { formatFieldValue, type FieldFormatOptions } from './format' import type { StatusTone } from './types' export interface TimelineEntry { id: string at: string /** Texto ya redactado («Timbró la factura»). */ label: string actor?: string detail?: string icon?: string tone?: StatusTone } export interface DocumentTimelineProps extends FieldFormatOptions { entries: TimelineEntry[] /** Texto del estado vacío; lleva `emptyAction` si el host da una acción. */ emptyText?: string className?: string } const TONE_DOT: Record = { neutral: 'text-muted-foreground', info: 'text-[var(--info,oklch(0.55_0.13_235))]', success: 'text-[var(--success,oklch(0.6_0.15_155))]', caution: 'text-[var(--warning,oklch(0.6_0.15_65))]', warning: 'text-[var(--warning,oklch(0.6_0.15_65))]', critical: 'text-destructive', } export function DocumentTimeline({ entries, emptyText, className, ...fmt }: DocumentTimelineProps) { const { t } = useTranslation() if (entries.length === 0) { return (

{emptyText ?? t('document.timeline_empty', { defaultValue: 'Aún no hay actividad en este documento.' })}

) } const sorted = [...entries].sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime()) return (
    {sorted.map((e) => (
  1. {e.icon ? : }

    {t(e.label, { defaultValue: e.label })}

    {formatFieldValue(e.at, 'datetime', fmt)} {e.actor ? ` · ${e.actor}` : ''}

    {e.detail ?

    {e.detail}

    : null}
  2. ))}
) }