feat: implement permissions and actions management
CI / Lint, typecheck, test, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s

- 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.
This commit is contained in:
jayjennings committed 2026-10-06 11:20:44 -07:00
1 parent 708da6b9f7
commit 3f6dff5f24
94 files changed
+7213 -643

No files matched your search

+183
View File
@@ -0,0 +1,183 @@
// Response shapes returned by the API. Dates are ISO 8601 strings.
import type {
actionKinds,
actionStatuses,
AreaKind,
IncidentStatus,
IncidentType,
rcaMethods,
Role,
rootCauseCategories,
Severity,
} from './enums.ts'
export type ActionKind = (typeof actionKinds)[number]
export type ActionStatus = (typeof actionStatuses)[number]
export type RcaMethod = (typeof rcaMethods)[number]
export type RootCauseCategory = (typeof rootCauseCategories)[number]
export interface UserRef {
id: string
name: string
}
export interface User {
id: string
email: string
name: string
role: Role
active: boolean
lastLoginAt: string | null
createdAt: string
}
export interface Organization {
id: string
name: string
}
export interface Area {
id: string
siteId: string
parentId: string | null
name: string
kind: AreaKind
archivedAt: string | null
}
export interface Site {
id: string
name: string
address: string | null
timezone: string
archivedAt: string | null
areas: Area[]
}
export interface Incident {
id: string
number: number
reference: string
title: string
type: IncidentType
severity: Severity
status: IncidentStatus
occurredAt: string
reportedAt: string
updatedAt: string
closedAt: string | null
site: { id: string; name: string }
area: { id: string; name: string } | null
locationDetail: string | null
description: string | null
immediateAction: string | null
anonymous: boolean
reporter: UserRef | null
}
export interface Investigation {
id: string
lead: UserRef | null
method: RcaMethod
summary: string | null
startedAt: string
completedAt: string | null
}
export interface RootCause {
id: string
category: RootCauseCategory
description: string
whys: string[]
}
export interface CorrectiveAction {
id: string
incident: { number: number; reference: string; title: string }
rootCauseId: string | null
kind: ActionKind
title: string
description: string | null
assignee: UserRef | null
dueDate: string | null
status: ActionStatus
overdue: boolean
completedAt: string | null
verifiedBy: UserRef | null
verifiedAt: string | null
createdAt: string
}
export interface Attachment {
id: string
fileName: string
contentType: string
sizeBytes: number
uploadedBy: UserRef | null
createdAt: string
url: string
}
export interface TransitionOption {
to: IncidentStatus
label: string
/** Why the transition can't happen yet; absent when it is allowed. */
blockedReason?: string
}
export interface IncidentDetail extends Incident {
investigation: Investigation | null
rootCauses: RootCause[]
actions: CorrectiveAction[]
attachments: Attachment[]
transitions: TransitionOption[]
/** What the signed-in user may do on this incident. */
can: { edit: boolean; manageInvestigation: boolean; manageActions: boolean }
}
export interface Comment {
id: string
author: UserRef | null
body: string
createdAt: string
}
export interface TimelineEntry {
id: number
at: string
actor: UserRef | null
action: string
changes: Record<string, unknown> | null
}
export interface Paginated<T> {
items: T[]
page: number
pageSize: number
total: number
}
export interface OverviewStats {
openIncidents: number
reportedLast7Days: number
actionsDueThisWeek: number
overdueActions: number
/** Average days from report to close for incidents closed in the last 90 days. */
averageCloseDays: number | null
byStatus: Record<IncidentStatus, number>
actionProgress: { completed: number; total: number }
/** Incidents per week for the last 12 weeks, oldest first. */
weeklyTrend: { weekStart: string; count: number }[]
bySite: { siteId: string; name: string; count: number }[]
}
export interface AnalyticsStats {
periodDays: number
total: number
byType: { type: IncidentType; count: number }[]
bySeverity: { severity: Severity; count: number }[]
topRootCause: { category: RootCauseCategory; count: number; share: number } | null
/** Share of completed actions finished on or before their due date. */
onTimeActionRate: number | null
monthlyCompletedActions: { month: string; onTime: number; late: number }[]
}