ALL-1871 PR-1: Editable cell component + error-guided UI in PreviewTable
failed## ALL-1871 PR-1 - Frontend: Editable cells + error UI in PreviewTable
Linear: ALL-1871 ("In-line editing of CSV data in bulk upload preview")
Branch: wilbo/all-1871-editable-preview-cells
### What to build
Convert the read-only PreviewTable into an editable grid with error-guided UX. This is a PURE FRONTEND PR -- no backend changes, no re-validation calls yet (those come in PR-3).
### Key files
CREATE: apps/dashboard/components/pages/ConnectPage/BulkUpload/EditableCell.tsx
- Click-to-edit: renders a span normally; on click/focus switches to input
- Modified cells: visually distinct (e.g. bg-amber-50 border-amber-400)
- Error cells: red border + tooltip showing the validation error message
- Tab key: moves focus to next editable cell in the row, then first cell of next row
- Original value: accessible via tooltip on the modified indicator
- Props: { value: string|null, originalValue: string|null, error?: string, onChange: (val: string) => void, fieldId: string, rowIndex: number }
EDIT: apps/dashboard/components/pages/ConnectPage/BulkUpload/PreviewTable.tsx
- Replace renderCell(row.fieldName) renderers with EditableCell for all editable fields (skip rowNumber)
- Accept new props: editedCells: Record<number, Record<string, string>>, onCellEdit: (rowIndex: number, field: string, value: string) => void
- Error list items in bottom panel: make each entry clickable -- on click, scroll to that row and focus the cell
- Pass errorsByRow down to EditableCell for per-field error display
EDIT: apps/dashboard/components/pages/ConnectPage/BulkUploadTab.tsx
- Add const [editedCells, setEditedCells] = useState<Record<number, Record<string, string>>>({}) alongside previewData
- Handler: handleCellEdit = (rowIndex, field, value) => setEditedCells(prev => ({ ...prev, [rowIndex]: { ...prev[rowIndex], [field]: value } }))
- Reset editedCells in handleBackToUpload and handleUploadAnother
- Pass editedCells and onCellEdit down to PreviewTable
ValidationError shape (types.ts): { row: number; field: string; message: string; value?: unknown }
Note: row is 1-indexed (matches rowNumber). errorsByRow map in PreviewTable already groups by row.
### Acceptance test
- User sees preview table after uploading CSV with validation errors
- Clicking a cell makes it editable (input appears)
- Editing a cell marks it visually different from unedited cells
- Hovering a modified cell shows tooltip with original CSV value
- Cells with validation errors show red border and error tooltip
- Clicking an error in the error list scrolls to and focuses that cell
- Tab moves focus to next editable cell
### Does NOT include
- Re-validation calls (PR-3)
- Import sending edits to backend (PR-4)
- Any backend changes
### Dependencies
- None (first PR)
### Tests
- Update PreviewTable.test.tsx: renders EditableCell, calls onCellEdit on change, error cell has red border class
Event Timeline
created
status_change
queued → in_progress
failed
lease expired — re-queued for retry
in_progress → queued
status_change
queued → in_progress
failed
lease expired — re-queued for retry
in_progress → queued
status_change
queued → in_progress
failed
lease expired — max retries reached, marking failed (poison pill)
in_progress → failed