// CIL[BEGIN chunk_id="SprintDetailPage_module_header_1784761401" type="module_header" identity="module_header" parent="none"] // CIL[DOC-MACRO purpose="Detail/edit page for a workbench sprint record"] // CIL[DOC-RESPONSIBILITY responsibility="Render RecordDetailPage configured for the workbench_sprint type with active-sprint guard and automatic start/end dates"] // CIL[DOC-CONTEXT module="workbench.pages.work.SprintDetailPage" layer="frontend-page" related_files="[\"custom/apps/workbench/components/records/RecordDetailPage.tsx\",\"custom/apps/workbench/pages/work/SprintsPage.tsx\"]"] // CIL[DOC-CONTRACT inputs="route param id" outputs="JSX page" errors="none" side_effects="calls /api/admin-data to complete any existing active sprint for the same team"] // CIL[DOC-MICRO logic_summary="Provides the field map and routes for editing a sprint; enforces one active sprint per team and sets start_at on activation/end_at on completion" notable_decisions=["Auto-completes the previous active sprint for the same team when a new sprint is activated"]"] // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"] // CIL[DOC-STABILITY stability="experimental"] // CIL[DOC-AUDIENCE audience="developer"] import { useEffect, useMemo, useState } from 'react' import { useParams } from 'react-router-dom' import { useAppPath } from '@core/hooks/useAppPath' import { useWorkbenchRole } from '../../hooks/useWorkbenchRole' import { apiFetch } from '@core/lib/api' import { RecordDetailPage } from '../../components/records/RecordDetailPage' // CIL[END chunk_id="SprintDetailPage_module_header_1784761401"] // CIL[BEGIN chunk_id="SprintDetailPage_sprintDetailPage_1784761401" type="function" identity="SprintDetailPage" parent="none"] // CIL[DOC-MACRO description="Implementation of SprintDetailPage" macro="Renders and manages SprintDetailPage UI state"] // CIL[DOC-CONTRACT input_spec="See module header or type signature" output_spec="JSX or value" side_effects="none"] // CIL[DOC-STABILITY stability="experimental"] function isActivePhase(phase?: string): boolean { return phase === 'active' || phase === 'current' } function isCompletedPhase(phase?: string): boolean { return phase === 'completed' } export default function SprintDetailPage() { const { id } = useParams<{ id: string }>() const appPath = useAppPath() const { role } = useWorkbenchRole() const [sprints, setSprints] = useState([]) useEffect(() => { apiFetch('/api/admin-data?entity=items&type_slug=workbench_sprint&limit=500') .then(r => r.json()) .then(json => setSprints(Array.isArray(json?.data) ? json.data : [])) .catch(() => setSprints([])) }, []) const beforeSave = useMemo(() => async (payload: any, record: any) => { const data = payload.data || {} const now = new Date().toISOString() const nextPhase = data.phase ?? record?.data?.phase data.phase = nextPhase const team = data.team ?? record?.data?.team data.team = team if (isActivePhase(nextPhase) && !data.start_at) { data.start_at = now } if (isCompletedPhase(nextPhase) && !data.end_at) { data.end_at = now } if (isActivePhase(nextPhase) && team) { const currentId = record?.id || id const existing = sprints.find(s => s.id !== currentId && s.data?.team === team && isActivePhase(s.data?.phase) ) if (existing) { await apiFetch(`/api/admin-data?entity=items&id=${existing.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: { phase: 'completed', end_at: now } }), }) } } payload.data = data return payload }, [sprints, id]) return ( ) } // CIL[END chunk_id="SprintDetailPage_sprintDetailPage_1784761401"]