Add OSHA Form 301 and Osha page components
CI / Lint, typecheck, test, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s

- 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:
jayjennings committed 2026-10-06 13:09:57 -07:00
1 parent 3e964d1176
commit 61c876edec
59 files changed
+9180 -183

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
import { and, asc, eq, or } from 'drizzle-orm'
import { Hono } from 'hono'
import { z } from 'zod'
import type { SavedView } from '../../shared/api-types.ts'
import { hasRole } from '../../shared/enums.ts'
import { savedViewSchema } from '../../shared/schemas.ts'
import { requireAuth } from '../auth/middleware.ts'
import { savedViews } from '../db/schema.ts'
import { currentUser, fail, validate, type AppEnv } from '../lib/http.ts'
const toView = (row: typeof savedViews.$inferSelect, userId: string): SavedView => ({
id: row.id,
page: row.page as SavedView['page'],
name: row.name,
filters: row.filters,
shared: row.shared,
mine: row.userId === userId,
})
/** Saved filter sets. Supervisors and above can share views with the whole organization. */
export const viewRoutes = new Hono<AppEnv>()
.get('/', requireAuth, validate('query', z.object({ page: z.enum(['analytics', 'incidents']) })), async (c) => {
const me = currentUser(c)
const rows = await c
.get('db')
.select()
.from(savedViews)
.where(and(eq(savedViews.organizationId, me.organizationId), eq(savedViews.page, c.req.valid('query').page), or(eq(savedViews.userId, me.id), eq(savedViews.shared, true))))
.orderBy(asc(savedViews.name))
return c.json({ views: rows.map((row) => toView(row, me.id)) })
})
.post('/', requireAuth, validate('json', savedViewSchema), async (c) => {
const me = currentUser(c)
const input = c.req.valid('json')
if (input.shared && !hasRole(me.role, 'supervisor')) throw fail(403, 'Only supervisors and above can share views')
const [row] = await c
.get('db')
.insert(savedViews)
.values({ ...input, organizationId: me.organizationId, userId: me.id })
.returning()
return c.json({ view: toView(row, me.id) }, 201)
})
.delete('/:id', requireAuth, validate('param', z.object({ id: z.uuid() })), async (c) => {
const me = currentUser(c)
const db = c.get('db')
const [row] = await db
.select()
.from(savedViews)
.where(and(eq(savedViews.id, c.req.valid('param').id), eq(savedViews.organizationId, me.organizationId)))
if (!row || (!row.shared && row.userId !== me.id)) throw fail(404, 'View not found')
if (row.userId !== me.id && me.role !== 'admin') throw fail(403, 'Only the owner or an admin can delete this view')
await db.delete(savedViews).where(eq(savedViews.id, row.id))
return c.body(null, 204)
})