Sheet Section Audit
Architecture audit of the Sheet Section component
Sheet Section Component: Architecture Audit & Analysis
Source: This page is based on docs/SHEET_SECTION_CODE_AUDIT.md
Last Updated: January 15, 2026
Status: 🟡 Complete - Audit Finished, Optimization Opportunities Identified
Executive Summary
This document provides a comprehensive audit of the Sheet Section component - an embeddable, scoped sheet viewer/editor that can be added to workflow stages. Unlike the standalone Sheet page, Sheet Sections provide filtered, contextual views of sheet data within workflow runs.
Current State Assessment
| Area | Grade | Status | Key Characteristics |
|---|---|---|---|
| Configuration UI | B+ | 🟢 Clean, intuitive builder UI | ✅ DnD reordering, visibility toggles, clear UX |
| Data Scoping | A- | 🟢 Powerful filtering system | ✅ User + Session scopes, client-side filtering |
| State Management | B | 🟡 Complex syncing patterns | ⚠️ Multiple effects, form/UI state sync |
| Runtime Performance | B | 🟢 Good for typical use cases | ✅ Reuses Sheet component, ⚠️ Client-side filtering |
| Code Organization | B+ | 🟢 Well-separated concerns | ✅ Config/Runtime split, clear responsibilities |
| Modal Interactions | B- | 🟡 Functional but complex | ⚠️ Mutation-driven modal, delete during "add" flow |
Overall Grade: B+ (Good - Production Ready with Minor Issues)
Key Strengths:
- ✅ Powerful Data Scoping - Filter by user (all/current) and session (all/current) in 2x2 matrix
- ✅ Column Visibility Control - Drag-to-reorder with visibility toggles (eye icons)
- ✅ Integrated Sheet Component - Reuses main Sheet component for consistency
- ✅ Modal Record Editing - Full block rendering inside record modals
- ✅ Read-Only Mode - Flexible viewing without editing
- ✅ Create-on-the-fly - Can create new sheets directly from config UI
Key Opportunities:
- ⚠️ Complex State Syncing - Multiple useEffect hooks with interdependencies
- ⚠️ Client-Side Scope Filtering - O(N) filtering on every render
- ⚠️ Mutation-Driven Modal - Creates record on open, deletes on cancel (wasteful)
- ⚠️ No Pagination - Inherits limitation from Sheet component
- ⚠️ Type Safety Issues - Several
anytypes,@ts-ignorecomments - ⚠️ Invalidation Overkill - 5+ query invalidations on single mutation
Architecture Overview
Core Purpose
The Sheet Section is an embeddable component that displays filtered sheet data within workflow stages. It serves as:
- Contextual Data Viewer - Show only relevant rows (current user, current session)
- Record Manager - Add/edit/delete records through modal interface
- Data Collection Tool - Capture structured data during workflow execution
- Flexible Display - Configure which columns to show and in what order
Lifecycle
┌──────────────────────────────────────────────────────┐
│ Sheet Section Lifecycle │
└──────────────────────────────────────────────────────┘
1. CONFIGURATION (Workflow Builder)
│
├─ User adds Sheet Section to a page
├─ Selects/creates target sheet
├─ Configures column visibility & order
├─ Sets data scopes (user/session)
└─ Toggles read-only mode
│
▼
2. PERSISTENCE (Database)
│
├─ section.type = 'sheet'
├─ section.sheet_id = <selected sheet>
└─ section.options = {
viewableColumns: [{ id, order, value }],
readOnly: boolean,
userScope: 'all' | 'current',
sessionScope: 'all' | 'current'
}
│
▼
3. RUNTIME (Session Execution)
│
├─ Fetch sheet data (all rows/columns)
├─ Apply scope filters (client-side)
├─ Render filtered rows in Sheet component
├─ Show "Add Record" button (if not read-only)
└─ Handle record editing via modalFile Structure
components/
builder/components/sectionConfig/
└── SheetSectionConfig.tsx # Configuration UI (423 lines)
sections/
└── SheetSectionBody.tsx # Runtime renderer (322 lines)
sheet/
└── Sheet.tsx # Reusable sheet component
renderers/
└── BlocksRenderer.tsx # Block rendering in modalsData Scoping System
2x2 Scoping Matrix
Sheet Sections support powerful data scoping through two independent dimensions:
User Scope:
all- Show rows created by any usercurrent- Show only rows created by current user
Session Scope:
all- Show rows from any workflow sessioncurrent- Show only rows from current session
Examples:
| User Scope | Session Scope | Use Case |
|---|---|---|
current | current | "My tasks in this project" |
all | current | "All team tasks in this project" |
current | all | "All my tasks across all projects" |
all | all | "All tasks ever created" |
Implementation
// Apply scoping filters
const filteredRows = rows.filter(row => {
// User scope filter
if (userScope === 'current' && row.created_by !== currentUserId) {
return false
}
// Session scope filter
if (sessionScope === 'current' && row.workflow_run_id !== currentSessionId) {
return false
}
return true
})Configuration UI
Column Visibility & Ordering
File: SheetSectionConfig.tsx
Users can:
- Toggle visibility - Eye icon to show/hide columns
- Reorder columns - Drag-and-drop to change order
- See column types - Visual indicators for data types
Implementation:
// DnD Kit for drag-and-drop
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor)
)
const handleDragEnd = (event) => {
const { active, over } = event
if (active.id !== over.id) {
setColumns(arrayMove(columns, oldIndex, newIndex))
}
}Sheet Selection
Features:
- Async search for existing sheets
- Create new sheet inline
- Clear visual feedback
// React-select with async creatable
<AsyncCreatable
loadOptions={searchSheets}
onCreateOption={createNewSheet}
placeholder="Select or create sheet..."
/>Runtime Component
File: SheetSectionBody.tsx
Responsibilities:
- Fetch sheet data
- Apply scope filters
- Render Sheet component
- Handle "Add Record" button
- Manage record editing modal
Key Pattern:
// Reuse Sheet component with scoped data
<Sheet
sheetId={section.sheet_id}
rows={filteredRows}
columns={visibleColumns}
readOnly={section.options.readOnly}
onRowClick={openRecordModal}
/>Modal Interactions
Current Pattern (Mutation-Driven)
Flow:
- User clicks "Add Record"
- System creates empty row in database
- Modal opens with new row ID
- User fills in data via blocks
- If user cancels, row is deleted
Issues:
- Wasteful database writes
- Orphaned rows if modal crashes
- Complex cleanup logic
Proposed Pattern (Optimistic)
Flow:
- User clicks "Add Record"
- Modal opens with local state
- User fills in data
- On save, create row in database
- On cancel, discard local state
Benefits:
- No wasteful writes
- Simpler cleanup
- Better UX (instant modal open)
Performance Characteristics
Current Performance
| Dataset Size | Performance | Notes |
|---|---|---|
| <100 rows | ✅ Excellent | No issues |
| 100-500 rows | ✅ Good | Minor lag on filter |
| 500-1000 rows | ⚠️ Acceptable | Noticeable lag |
| >1000 rows | ❌ Poor | Significant lag |
Bottlenecks
- Client-Side Filtering - O(N) filtering on every render
- No Pagination - Inherits from Sheet component
- Query Invalidation - 5+ invalidations on single mutation
- State Syncing - Multiple useEffect hooks
State Management
Current Pattern
Multiple State Sources:
- React Hook Form (configuration)
- Local useState (UI state)
- React Query (server state)
- Zustand (global state)
Syncing Issues:
// ⚠️ Complex syncing with multiple effects
useEffect(() => {
if (formData.columns !== uiColumns) {
setUIColumns(formData.columns)
}
}, [formData.columns])
useEffect(() => {
if (uiColumns !== formData.columns) {
setValue('columns', uiColumns)
}
}, [uiColumns])Proposed Pattern
Single Source of Truth:
// ✅ GOOD: Single state source
const { columns, setColumns } = useFieldArray({
control,
name: 'columns'
})
// No separate UI state neededOptimization Opportunities
1. Server-Side Scoping
Move scope filtering to backend:
// ✅ GOOD: Server-side scoping
const { data } = useGetSheetData(sheetId, {
userScope: 'current',
sessionScope: 'current',
userId: currentUserId,
sessionId: currentSessionId
})
// ❌ BAD: Client-side scoping
const filtered = rows.filter(applyScopes)2. Optimistic Modal Pattern
Implement optimistic UI for record creation:
// ✅ GOOD: Optimistic creation
const [draftRow, setDraftRow] = useState(null)
const openModal = () => {
setDraftRow({ /* empty row */ })
}
const saveRow = () => {
createRow(draftRow)
}3. Reduce Invalidations
Invalidate only necessary queries:
// ✅ GOOD: Targeted invalidation
queryClient.invalidateQueries(['sheet', sheetId])
// ❌ BAD: Broad invalidation
queryClient.invalidateQueries(['sheet'])
queryClient.invalidateQueries(['cells'])
queryClient.invalidateQueries(['rows'])
// ... (5+ invalidations)