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
@@ -14,6 +14,9 @@ export const actionKindEnum = pgEnum('action_kind', vocab.actionKinds)
|
||||
export const actionStatusEnum = pgEnum('action_status', vocab.actionStatuses)
|
||||
export const authTokenPurposeEnum = pgEnum('auth_token_purpose', ['magic_link', 'password_reset'])
|
||||
export const reportChannelEnum = pgEnum('report_channel', ['web', 'kiosk'])
|
||||
export const oshaCaseTypeEnum = pgEnum('osha_case_type', vocab.oshaCaseTypes)
|
||||
export const oshaOutcomeEnum = pgEnum('osha_outcome', vocab.oshaOutcomes)
|
||||
export const oshaSpecialCaseEnum = pgEnum('osha_special_case', vocab.oshaSpecialCases)
|
||||
|
||||
const id = () => uuid().primaryKey().defaultRandom()
|
||||
const timestamptz = () => timestamp({ withTimezone: true })
|
||||
@@ -91,6 +94,16 @@ export const sites = pgTable(
|
||||
name: text().notNull(),
|
||||
address: text(),
|
||||
timezone: text().notNull().default('UTC'),
|
||||
// OSHA establishment details (Form 300A and the ITA upload)
|
||||
streetAddress: text(),
|
||||
city: text(),
|
||||
state: text(),
|
||||
zip: text(),
|
||||
naicsCode: text(),
|
||||
industryDescription: text(),
|
||||
ein: text(),
|
||||
/** ITA establishment_type: 1 private, 2 state government, 3 local government. */
|
||||
establishmentType: integer().notNull().default(1),
|
||||
archivedAt: timestamptz(),
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
@@ -208,6 +221,42 @@ export const injuredPersons = pgTable(
|
||||
daysRestricted: integer().notNull().default(0),
|
||||
/** OSHA privacy case: name withheld from the 300 log. */
|
||||
privacyCase: boolean().notNull().default(false),
|
||||
|
||||
// OSHA recordability (29 CFR 1904). Null answers mean "not assessed yet".
|
||||
caseNumber: text(),
|
||||
workRelated: boolean(),
|
||||
newCase: boolean(),
|
||||
died: boolean().notNull().default(false),
|
||||
dateOfDeath: date({ mode: 'string' }),
|
||||
daysAwayFromWork: boolean().notNull().default(false),
|
||||
restrictedOrTransfer: boolean().notNull().default(false),
|
||||
medicalTreatment: boolean().notNull().default(false),
|
||||
lossOfConsciousness: boolean().notNull().default(false),
|
||||
significantDiagnosis: boolean().notNull().default(false),
|
||||
specialCase: oshaSpecialCaseEnum().notNull().default('none'),
|
||||
caseType: oshaCaseTypeEnum().notNull().default('injury'),
|
||||
/** Derived from the answers above with shared/osha.ts, stored for reporting. */
|
||||
recordable: boolean().notNull().default(false),
|
||||
outcome: oshaOutcomeEnum(),
|
||||
|
||||
// Severe injury reporting (1904.39)
|
||||
inpatientHospitalization: boolean().notNull().default(false),
|
||||
amputation: boolean().notNull().default(false),
|
||||
lossOfEye: boolean().notNull().default(false),
|
||||
reportedToOshaAt: timestamptz(),
|
||||
|
||||
// Form 301 details
|
||||
employeeAddress: text(),
|
||||
dateOfBirth: date({ mode: 'string' }),
|
||||
dateOfHire: date({ mode: 'string' }),
|
||||
sex: text(),
|
||||
physicianName: text(),
|
||||
treatmentFacility: text(),
|
||||
treatedInEmergencyRoom: boolean().notNull().default(false),
|
||||
timeStartedWork: text(),
|
||||
activityBefore: text(),
|
||||
whatHappened: text(),
|
||||
objectSubstance: text(),
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
},
|
||||
@@ -330,8 +379,30 @@ export const hoursWorked = pgTable(
|
||||
/** First day of the month, e.g. `2026-10-01`. */
|
||||
month: date({ mode: 'string' }).notNull(),
|
||||
hours: numeric({ precision: 12, scale: 2 }).notNull(),
|
||||
/** Average number of employees on the payroll that month (for the annual average). */
|
||||
averageEmployees: integer(),
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
},
|
||||
(t) => [uniqueIndex('hours_worked_site_month_unique').on(t.siteId, t.month)],
|
||||
)
|
||||
|
||||
/** Named filter sets for the Analytics and Incidents pages; private, or shared with the organization. */
|
||||
export const savedViews = pgTable(
|
||||
'saved_views',
|
||||
{
|
||||
id: id(),
|
||||
organizationId: uuid()
|
||||
.notNull()
|
||||
.references(() => organizations.id),
|
||||
userId: uuid()
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
page: text().notNull(),
|
||||
name: text().notNull(),
|
||||
filters: jsonb().$type<Record<string, string>>().notNull(),
|
||||
shared: boolean().notNull().default(false),
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [index('saved_views_organization_page_idx').on(t.organizationId, t.page)],
|
||||
)
|
||||
Reference in new issue
Block a user