- Add permissions logic for status options in corrective actions. - Create tests for permissions functionality. - Implement queries for user authentication and actions management. - Develop Actions page to display and manage corrective actions. - Create authentication pages for login, setup, and password reset. - Add IncidentDetail page to view incident details and associated actions. - Implement Settings page for user management and password changes.
56 lines
2.8 KiB
TypeScript
56 lines
2.8 KiB
TypeScript
// Incident status workflow, shared so the web app can show the same options and reasons the API enforces.
|
|
import type { ActionStatus } from './api-types.ts'
|
|
import { hasRole, type IncidentStatus, type Role, type Severity } from './enums.ts'
|
|
|
|
export interface Transition {
|
|
from: IncidentStatus
|
|
to: IncidentStatus
|
|
minRole: Role
|
|
label: string
|
|
}
|
|
|
|
export const transitions: Transition[] = [
|
|
{ from: 'reported', to: 'triage', minRole: 'supervisor', label: 'Start triage' },
|
|
{ from: 'triage', to: 'investigation', minRole: 'supervisor', label: 'Start investigation' },
|
|
{ from: 'triage', to: 'actions', minRole: 'supervisor', label: 'Skip to corrective actions' },
|
|
{ from: 'triage', to: 'closed', minRole: 'supervisor', label: 'Close: no further action' },
|
|
{ from: 'investigation', to: 'actions', minRole: 'supervisor', label: 'Move to corrective actions' },
|
|
{ from: 'actions', to: 'investigation', minRole: 'supervisor', label: 'Back to investigation' },
|
|
{ from: 'actions', to: 'verification', minRole: 'supervisor', label: 'Send for verification' },
|
|
{ from: 'verification', to: 'actions', minRole: 'safety_manager', label: 'Return to actions' },
|
|
{ from: 'verification', to: 'closed', minRole: 'safety_manager', label: 'Verify and close' },
|
|
{ from: 'closed', to: 'investigation', minRole: 'safety_manager', label: 'Reopen' },
|
|
]
|
|
|
|
export interface WorkflowFacts {
|
|
severity: Severity
|
|
rootCauseCount: number
|
|
actionStatuses: ActionStatus[]
|
|
}
|
|
|
|
/** Why a transition isn't possible yet, or undefined if it is. */
|
|
export function transitionBlocker(transition: Transition, facts: WorkflowFacts): string | undefined {
|
|
const live = facts.actionStatuses.filter((status) => status !== 'cancelled')
|
|
if (transition.from === 'triage' && transition.to === 'closed' && (facts.severity === 'high' || facts.severity === 'critical')) {
|
|
return 'High and critical incidents must be investigated before closing'
|
|
}
|
|
if (transition.to === 'actions' && transition.from === 'investigation' && facts.rootCauseCount === 0) {
|
|
return 'Record at least one root cause first'
|
|
}
|
|
if (transition.to === 'verification') {
|
|
if (live.length === 0) return 'Add at least one corrective action first'
|
|
if (live.some((status) => status === 'open' || status === 'in_progress')) return 'All corrective actions must be done first'
|
|
}
|
|
if (transition.from === 'verification' && transition.to === 'closed' && live.some((status) => status !== 'verified')) {
|
|
return 'Verify every corrective action first'
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/** Transitions from `status` that `role` may perform, with blockers explained. */
|
|
export function availableTransitions(status: IncidentStatus, role: Role, facts: WorkflowFacts) {
|
|
return transitions
|
|
.filter((t) => t.from === status && hasRole(role, t.minRole))
|
|
.map((t) => ({ to: t.to, label: t.label, blockedReason: transitionBlocker(t, facts) }))
|
|
}
|