RowStatusChip.tsx2.0 KBView on GitHub
'use client';

/**
 * The reserved `_status` column, rendered as a per-row state chip.
 *
 * Progress needs no bespoke channel: a fan-out orchestrator writing `_status` is an ordinary
 * cell write that rides the same Y.js delta and SSE path as anything else
 * (apps/mail/docs/table-documents.md §3.2 step 7), so a 40-row run lights up row by row here
 * with nothing else in the client involved. `_error` is the failure text, surfaced on hover
 * rather than inline — a row that failed still has to fit on one line.
 */

import { Check, CircleDashed, Loader2, TriangleAlert } from 'lucide-react';

import { cn } from '@/lib/utils';
import { ROW_STATUS_LABELS } from './constants';

const ICON_BY_STATUS = {
  pending: CircleDashed,
  running: Loader2,
  done: Check,
  failed: TriangleAlert,
} as const;

const TONE_BY_STATUS = {
  pending: 'text-muted-foreground',
  running: 'text-primary',
  done: 'text-emerald-600 dark:text-emerald-400',
  failed: 'text-destructive',
} as const;

export interface RowStatusChipProps {
  /** Raw `_status` cell value. Anything outside the four known states renders verbatim. */
  status: string;
  /** Raw `_error` cell value, shown as the chip's title when present. */
  error?: string;
  className?: string;
}

export function RowStatusChip({ status, error, className }: RowStatusChipProps) {
  const key=[redacted];
  const known = key in ICON_BY_STATUS ? (key as keyof typeof ICON_BY_STATUS) : null;
  const Icon = known ? ICON_BY_STATUS[known] : null;
  const label = known ? ROW_STATUS_LABELS[known] : status.trim();
  if (!label) return null;

  return (
    <span
      title={error?.trim() || label}
      className={cn(
        'inline-flex max-w-full items-center gap-1 text-xs',
        known ? TONE_BY_STATUS[known] : 'text-muted-foreground',
        className,
      )}
    >
      {Icon && <Icon className={cn('size-3 shrink-0', known === 'running' && 'animate-spin')} />}
      <span className="truncate">{label}</span>
    </span>
  );
}