// CIL[BEGIN chunk_id="WorkItemForm_module_header_1784779659" type="module_header" identity="module_header" parent="none"] // CIL[DOC-MACRO purpose="Render a form to create or edit a workbench work item"] // CIL[DOC-RESPONSIBILITY responsibility="Collect work item fields, validate them, and emit a data payload for create/update"] // CIL[DOC-CONTEXT module="workbench.components.work.WorkItemForm" layer="frontend-component" related_files="[\"custom/apps/workbench/components/work/badges.tsx\",\"custom/apps/workbench/components/work/options.ts\",\"custom/apps/workbench/pages/work/WorkItemDetailPage.tsx\"]"] // CIL[DOC-CONTRACT inputs="item record (optional) and submit/cancel callbacks" outputs="data payload for /api/admin-data" errors="none" side_effects="none"] // CIL[DOC-MICRO logic_summary="Maintains local form state, loads people for assignee selection, and converts values including is_blocked and order to the type-safe payload shape" notable_decisions="[]"] // CIL[DOC-LEARNING topic="runtime-type:boolean-formatting" content="admin-data returns is_blocked as a formatted string; use isBlockedValue when reading the initial value so 'No' is not treated as blocked"] // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"] // CIL[DOC-STABILITY stability="experimental"] // CIL[DOC-AUDIENCE audience="developer"] import { useCallback, useEffect, useImperativeHandle, useState, forwardRef, type ReactNode, type Ref } from 'react' import { apiFetch } from '@core/lib/api' import { Button } from '@core/components/ui/button' import { Input } from '@core/components/ui/input' import { Textarea } from '@core/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@core/components/ui/select' import { cn } from '@core/lib/utils' import { STATUS_OPTIONS, WORK_TYPE_OPTIONS, PRIORITY_OPTIONS, SEVERITY_OPTIONS, TEAM_OPTIONS, INCIDENT_STATUS_OPTIONS, INTAKE_OPTIONS, } from './options' import { isBlockedValue } from './badges' export interface WorkItemFormHandle { getPayload: () => { title: string description: string status: string data: any } } interface WorkItemFormProps { item?: any title: string onSubmit: (payload: any) => void | Promise onCancel?: () => void loading?: boolean mode?: 'create' | 'edit' itemLabel?: string } const UNASSIGNED_VALUE = '__unassigned__' const emptyForm = { description: '', status: 'triage', work_type: 'task', priority: '', severity: '', team: 'unassigned', assignee_id: UNASSIGNED_VALUE, agent_id: '', epic_id: '', sprint_id: '', order: '', story_points: '', intake_disposition: '', is_blocked: 'false', blocked_reason: '', started_at: '', resolved_at: '', closed_at: '', incident_status: '', commander_id: '', responders: '', affected_services: '', affected_accounts: '', mitigated_at: '', recovered_at: '', postmortem_id: '', cortex_ticket_id: '', cased_analysis_id: '', kb_article_id: '', cil_project_id: '', support_summary: '', ai_confidence: '', } function getInitialForm(item?: any): Record { if (!item) return emptyForm const d = item.data || {} return { description: item.description || '', status: item.status || 'triage', work_type: d.work_type || 'task', priority: d.priority || '', severity: d.severity || '', team: d.team || 'unassigned', assignee_id: d.assignee_id || UNASSIGNED_VALUE, agent_id: d.agent_id || '', epic_id: d.epic_id || '', sprint_id: d.sprint_id || '', order: d.order ?? '', story_points: d.story_points ?? '', intake_disposition: d.intake_disposition || '', is_blocked: isBlockedValue(d.is_blocked) ? 'true' : 'false', blocked_reason: d.blocked_reason || '', started_at: d.started_at || '', resolved_at: d.resolved_at || '', closed_at: d.closed_at || '', incident_status: d.incident_status || '', commander_id: d.commander_id || '', responders: Array.isArray(d.responders) ? JSON.stringify(d.responders) : d.responders || '', affected_services: Array.isArray(d.affected_services) ? JSON.stringify(d.affected_services) : d.affected_services || '', affected_accounts: Array.isArray(d.affected_accounts) ? JSON.stringify(d.affected_accounts) : d.affected_accounts || '', mitigated_at: d.mitigated_at || '', recovered_at: d.recovered_at || '', postmortem_id: d.postmortem_id || '', cortex_ticket_id: d.cortex_ticket_id || '', cased_analysis_id: d.cased_analysis_id || '', kb_article_id: d.kb_article_id || '', cil_project_id: d.cil_project_id || '', support_summary: d.support_summary || '', ai_confidence: d.ai_confidence ?? '', } } function toNumber(value: string): number | null { const n = Number(value) return value === '' || Number.isNaN(n) ? null : n } function toJson(value: string): any { const trimmed = value.trim() if (!trimmed) return null if ((trimmed.startsWith('[') && trimmed.endsWith(']')) || (trimmed.startsWith('{') && trimmed.endsWith('}'))) { try { return JSON.parse(trimmed) } catch { /* fall through */ } } return trimmed } interface FieldSelectProps { field: string label: string options: { value: string; label: string }[] value: string onChange: (field: string, value: string) => void placeholder?: string } function FieldSelect({ field, label, options, value, onChange, placeholder = 'Select...' }: FieldSelectProps) { return (
) } interface FieldInputProps { field: string label: string type?: string placeholder?: string value: string onChange: (field: string, value: string) => void } function FieldInput({ field, label, type = 'text', placeholder = '', value, onChange }: FieldInputProps) { return (
onChange(field, e.target.value)} placeholder={placeholder} className="h-8 w-full text-xs" />
) } interface FieldTextareaProps { field: string label: string rows?: number placeholder?: string value: string onChange: (field: string, value: string) => void } function FieldTextarea({ field, label, rows = 2, placeholder = '', value, onChange }: FieldTextareaProps) { return (