Add OSHA Form 301 and Osha page components
- Implemented Form301 component for displaying OSHA Form 301 equivalent, including employee and case information. - Created Osha component to manage OSHA recordkeeping, including tabs for 300 Log, 300A Summary, Hours & rates, and Submit to OSHA. - Added functionality for printing and downloading CSV files for OSHA submissions. - Integrated loading and error handling states for data fetching.
This commit is contained in:
1 parent
3e964d1176
commit
61c876edec
59 files changed
+9180
-183
No files matched your search
+156
-11
@@ -1,9 +1,14 @@
|
||||
// Response shapes returned by the API. Dates are ISO 8601 strings.
|
||||
import type { SummaryTotals } from './osha.ts'
|
||||
import type {
|
||||
actionKinds,
|
||||
actionStatuses,
|
||||
AreaKind,
|
||||
employmentTypes,
|
||||
IncidentStatus,
|
||||
OshaCaseType,
|
||||
OshaOutcome,
|
||||
OshaSpecialCase,
|
||||
IncidentType,
|
||||
rcaMethods,
|
||||
Role,
|
||||
@@ -45,7 +50,18 @@ export interface Area {
|
||||
archivedAt: string | null
|
||||
}
|
||||
|
||||
export interface Site {
|
||||
export interface Establishment {
|
||||
streetAddress: string | null
|
||||
city: string | null
|
||||
state: string | null
|
||||
zip: string | null
|
||||
naicsCode: string | null
|
||||
industryDescription: string | null
|
||||
ein: string | null
|
||||
establishmentType: number
|
||||
}
|
||||
|
||||
export interface Site extends Partial<Establishment> {
|
||||
id: string
|
||||
name: string
|
||||
address: string | null
|
||||
@@ -153,8 +169,10 @@ export interface IncidentDetail extends Incident {
|
||||
actions: CorrectiveAction[]
|
||||
attachments: Attachment[]
|
||||
transitions: TransitionOption[]
|
||||
/** Number of injured or ill people recorded; details are only for safety managers. */
|
||||
injuredCount: number
|
||||
/** What the signed-in user may do on this incident. */
|
||||
can: { edit: boolean; manageInvestigation: boolean; manageActions: boolean }
|
||||
can: { edit: boolean; manageInvestigation: boolean; manageActions: boolean; manageOsha: boolean }
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
@@ -193,13 +211,140 @@ export interface OverviewStats {
|
||||
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 }[]
|
||||
/** A number for the selected period and the same-length period before it. */
|
||||
export interface Kpi {
|
||||
value: number | null
|
||||
previous: number | null
|
||||
}
|
||||
|
||||
/** Incident types grouped for trend charts. */
|
||||
export type TypeGroup = 'hazards' | 'injuries' | 'damage'
|
||||
|
||||
export interface AnalyticsStats {
|
||||
from: string
|
||||
to: string
|
||||
previousFrom: string
|
||||
bucket: 'week' | 'month'
|
||||
kpis: {
|
||||
reports: Kpi
|
||||
/** Leading: hazards and near misses reported. */
|
||||
leadingReports: Kpi
|
||||
injuries: Kpi
|
||||
/** Leading: hazards and near misses per injury or illness. */
|
||||
nearMissRatio: Kpi
|
||||
/** Leading: share of reports moved out of "reported" within 24 hours. */
|
||||
triagedWithin24h: Kpi
|
||||
/** Leading: share of actions completed by their due date. */
|
||||
actionsOnTime: Kpi
|
||||
averageDaysToClose: Kpi
|
||||
/** Lagging: OSHA-recordable cases (supervisors and above). */
|
||||
recordables: Kpi | null
|
||||
overdueActions: number
|
||||
}
|
||||
trend: ({ start: string } & Record<TypeGroup, number>)[]
|
||||
byLocation: { id: string | null; name: string; count: number }[]
|
||||
byRootCause: { category: RootCauseCategory; count: number }[]
|
||||
/** heatmap[weekday][hour], weekday 0 = Monday, in the server's time zone. */
|
||||
heatmap: number[][]
|
||||
actionsByMonth: { month: string; onTime: number; late: number }[]
|
||||
/** Rolling 12-month OSHA rates at each month end; null for reporters. */
|
||||
rates: { month: string; trir: number | null; dart: number | null }[] | null
|
||||
yearly: { year: number; recordables: number; dartCases: number; hours: number; trir: number | null; dart: number | null }[] | null
|
||||
}
|
||||
|
||||
export interface SavedView {
|
||||
id: string
|
||||
page: 'analytics' | 'incidents'
|
||||
name: string
|
||||
filters: Record<string, string>
|
||||
shared: boolean
|
||||
mine: boolean
|
||||
}
|
||||
|
||||
// ---- OSHA recordkeeping ----
|
||||
|
||||
export interface OshaCase {
|
||||
id: string
|
||||
incident: { id: string; number: number; reference: string; title: string; occurredAt: string }
|
||||
caseNumber: string
|
||||
name: string
|
||||
jobTitle: string | null
|
||||
employmentType: (typeof employmentTypes)[number]
|
||||
bodyPart: string | null
|
||||
natureOfInjury: string | null
|
||||
caseType: OshaCaseType
|
||||
workRelated: boolean | null
|
||||
newCase: boolean | null
|
||||
died: boolean
|
||||
dateOfDeath: string | null
|
||||
daysAwayFromWork: boolean
|
||||
daysAway: number
|
||||
restrictedOrTransfer: boolean
|
||||
daysRestricted: number
|
||||
medicalTreatment: boolean
|
||||
lossOfConsciousness: boolean
|
||||
significantDiagnosis: boolean
|
||||
specialCase: OshaSpecialCase
|
||||
privacyCase: boolean
|
||||
recordable: boolean
|
||||
outcome: OshaOutcome | null
|
||||
inpatientHospitalization: boolean
|
||||
amputation: boolean
|
||||
lossOfEye: boolean
|
||||
reportedToOshaAt: string | null
|
||||
/** Set when 1904.39 requires a direct report to OSHA. */
|
||||
severeReport: { withinHours: 8 | 24; reasons: string[]; dueBy: string } | null
|
||||
employeeAddress: string | null
|
||||
dateOfBirth: string | null
|
||||
dateOfHire: string | null
|
||||
sex: 'M' | 'F' | null
|
||||
physicianName: string | null
|
||||
treatmentFacility: string | null
|
||||
treatedInEmergencyRoom: boolean
|
||||
timeStartedWork: string | null
|
||||
activityBefore: string | null
|
||||
whatHappened: string | null
|
||||
objectSubstance: string | null
|
||||
/** Form 301 / ITA fields still empty for a recordable case. */
|
||||
missingFields: string[]
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/** One line of the Form 300 Log. */
|
||||
export interface OshaLogRow {
|
||||
caseId: string
|
||||
caseNumber: string
|
||||
incidentNumber: number
|
||||
/** "Privacy case" for privacy concern cases. */
|
||||
employeeName: string
|
||||
jobTitle: string
|
||||
dateOfInjury: string
|
||||
location: string
|
||||
description: string
|
||||
outcome: OshaOutcome
|
||||
daysAway: number
|
||||
daysRestricted: number
|
||||
caseType: OshaCaseType
|
||||
missingFields: string[]
|
||||
}
|
||||
|
||||
export interface OshaYear {
|
||||
site: { id: string; name: string } & Establishment
|
||||
companyName: string
|
||||
year: number
|
||||
rows: OshaLogRow[]
|
||||
totals: SummaryTotals
|
||||
months: { month: number; hours: number | null; averageEmployees: number | null }[]
|
||||
totalHoursWorked: number
|
||||
annualAverageEmployees: number
|
||||
maxEmployees: number
|
||||
rates: { trir: number | null; dart: number | null; ltir: number | null }
|
||||
checks: { errors: string[]; warnings: string[] }
|
||||
missingEstablishmentFields: string[]
|
||||
}
|
||||
|
||||
export interface Form301 {
|
||||
case: OshaCase
|
||||
establishment: { name: string; companyName: string } & Establishment
|
||||
incident: { reference: string; occurredAt: string; title: string; description: string | null; site: string; area: string | null; locationDetail: string | null }
|
||||
}
|
||||
Reference in new issue
Block a user