@omersx/bemms 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +203 -2
  3. package/drizzle.config.ts +11 -0
  4. package/next.config.ts +51 -0
  5. package/package.json +99 -3
  6. package/postcss.config.mjs +7 -0
  7. package/public/hospital_demo_devices.csv +32 -0
  8. package/public/icons/icon-192.png +0 -0
  9. package/public/icons/icon-512.png +0 -0
  10. package/public/icons/icon-maskable-512.png +0 -0
  11. package/public/manifest.webmanifest +31 -0
  12. package/public/sw.js +177 -0
  13. package/src/app/(auth)/layout.tsx +20 -0
  14. package/src/app/(auth)/login/page.tsx +135 -0
  15. package/src/app/(dashboard)/admin/audit-logs/page.tsx +178 -0
  16. package/src/app/(dashboard)/admin/data/page.tsx +37 -0
  17. package/src/app/(dashboard)/admin/departments/[deptId]/edit/page.tsx +206 -0
  18. package/src/app/(dashboard)/admin/departments/[deptId]/page.tsx +71 -0
  19. package/src/app/(dashboard)/admin/departments/new/page.tsx +6 -0
  20. package/src/app/(dashboard)/admin/departments/page.tsx +6 -0
  21. package/src/app/(dashboard)/admin/general/page.tsx +374 -0
  22. package/src/app/(dashboard)/admin/hospitals/[hospitalId]/edit/page.tsx +304 -0
  23. package/src/app/(dashboard)/admin/hospitals/[hospitalId]/page.tsx +94 -0
  24. package/src/app/(dashboard)/admin/hospitals/new/page.tsx +302 -0
  25. package/src/app/(dashboard)/admin/hospitals/page.tsx +6 -0
  26. package/src/app/(dashboard)/admin/layout.tsx +49 -0
  27. package/src/app/(dashboard)/admin/organizations/[orgId]/edit/page.tsx +172 -0
  28. package/src/app/(dashboard)/admin/organizations/[orgId]/page.tsx +82 -0
  29. package/src/app/(dashboard)/admin/organizations/new/page.tsx +150 -0
  30. package/src/app/(dashboard)/admin/organizations/page.tsx +6 -0
  31. package/src/app/(dashboard)/admin/page.tsx +258 -0
  32. package/src/app/(dashboard)/admin/roles/page.tsx +153 -0
  33. package/src/app/(dashboard)/admin/settings/page.tsx +69 -0
  34. package/src/app/(dashboard)/admin/users/[userId]/edit/page.tsx +153 -0
  35. package/src/app/(dashboard)/admin/users/[userId]/page.tsx +317 -0
  36. package/src/app/(dashboard)/admin/users/invite/page.tsx +3 -0
  37. package/src/app/(dashboard)/admin/users/new/page.tsx +5 -0
  38. package/src/app/(dashboard)/admin/users/page.tsx +23 -0
  39. package/src/app/(dashboard)/dashboard/page.tsx +462 -0
  40. package/src/app/(dashboard)/departments/[deptId]/page.tsx +39 -0
  41. package/src/app/(dashboard)/departments/new/page.tsx +330 -0
  42. package/src/app/(dashboard)/departments/page.tsx +52 -0
  43. package/src/app/(dashboard)/devices/[id]/edit/page.tsx +131 -0
  44. package/src/app/(dashboard)/devices/[id]/page.tsx +408 -0
  45. package/src/app/(dashboard)/devices/[id]/qr/page.tsx +54 -0
  46. package/src/app/(dashboard)/devices/new/page.tsx +382 -0
  47. package/src/app/(dashboard)/devices/page.tsx +80 -0
  48. package/src/app/(dashboard)/engineering-team/page.tsx +41 -0
  49. package/src/app/(dashboard)/layout.tsx +43 -0
  50. package/src/app/(dashboard)/maintenance/checklists/page.tsx +57 -0
  51. package/src/app/(dashboard)/maintenance/page.tsx +231 -0
  52. package/src/app/(dashboard)/maintenance/plans/page.tsx +142 -0
  53. package/src/app/(dashboard)/maintenance/tasks/[id]/page.tsx +997 -0
  54. package/src/app/(dashboard)/maintenance/tasks/page.tsx +119 -0
  55. package/src/app/(dashboard)/more/page.tsx +310 -0
  56. package/src/app/(dashboard)/my-department/page.tsx +151 -0
  57. package/src/app/(dashboard)/notifications/notification-list.tsx +103 -0
  58. package/src/app/(dashboard)/notifications/page.tsx +59 -0
  59. package/src/app/(dashboard)/profile/page.tsx +27 -0
  60. package/src/app/(dashboard)/reports/costs/page.tsx +127 -0
  61. package/src/app/(dashboard)/reports/history/page.tsx +105 -0
  62. package/src/app/(dashboard)/reports/inventory/page.tsx +116 -0
  63. package/src/app/(dashboard)/reports/maintenance/page.tsx +136 -0
  64. package/src/app/(dashboard)/reports/page.tsx +120 -0
  65. package/src/app/(dashboard)/reports/tickets/page.tsx +139 -0
  66. package/src/app/(dashboard)/reports/workload/page.tsx +135 -0
  67. package/src/app/(dashboard)/rounds/[id]/page.tsx +197 -0
  68. package/src/app/(dashboard)/rounds/history/page.tsx +82 -0
  69. package/src/app/(dashboard)/rounds/page.tsx +107 -0
  70. package/src/app/(dashboard)/scan/[code]/page.tsx +154 -0
  71. package/src/app/(dashboard)/scan/page.tsx +73 -0
  72. package/src/app/(dashboard)/team/page.tsx +42 -0
  73. package/src/app/(dashboard)/tickets/[id]/page.tsx +465 -0
  74. package/src/app/(dashboard)/tickets/create/page.tsx +722 -0
  75. package/src/app/(dashboard)/tickets/page.tsx +55 -0
  76. package/src/app/(dashboard)/tickets/triage/page.tsx +161 -0
  77. package/src/app/api/admin/data/export/route.ts +316 -0
  78. package/src/app/api/auth/[...nextauth]/route.ts +3 -0
  79. package/src/app/api/health/route.ts +46 -0
  80. package/src/app/api/notifications/sse/route.ts +91 -0
  81. package/src/app/api/notifications/subscribe/route.ts +54 -0
  82. package/src/app/api/notifications/vapid-key/route.ts +7 -0
  83. package/src/app/api/reports/export/route.ts +102 -0
  84. package/src/app/api/tickets/[id]/attachments/route.ts +129 -0
  85. package/src/app/api/tickets/[id]/live-sync/route.ts +52 -0
  86. package/src/app/error.tsx +54 -0
  87. package/src/app/globals.css +89 -0
  88. package/src/app/layout.tsx +59 -0
  89. package/src/app/not-found.tsx +32 -0
  90. package/src/app/page.tsx +5 -0
  91. package/src/components/admin/add-department-user-dialog.tsx +759 -0
  92. package/src/components/admin/add-engineer-dialog.tsx +773 -0
  93. package/src/components/admin/add-user-type-dialog.tsx +89 -0
  94. package/src/components/admin/audit-log-export.tsx +113 -0
  95. package/src/components/admin/audit-log-filters.tsx +255 -0
  96. package/src/components/admin/audit-log-table.tsx +383 -0
  97. package/src/components/admin/data-management/danger-zone.tsx +301 -0
  98. package/src/components/admin/data-management/data-management-tabs.tsx +61 -0
  99. package/src/components/admin/data-management/export-center.tsx +301 -0
  100. package/src/components/admin/data-management/import-wizard.tsx +558 -0
  101. package/src/components/admin/settings-forms/features-form.tsx +115 -0
  102. package/src/components/admin/settings-forms/health-panel.tsx +182 -0
  103. package/src/components/admin/settings-forms/localization-form.tsx +203 -0
  104. package/src/components/admin/settings-forms/retention-form.tsx +164 -0
  105. package/src/components/admin/settings-forms/security-form.tsx +186 -0
  106. package/src/components/admin/system-settings-tabs.tsx +88 -0
  107. package/src/components/admin/user-role-dialog.tsx +293 -0
  108. package/src/components/admin/user-status-dialog.tsx +166 -0
  109. package/src/components/admin/users-page-client.tsx +514 -0
  110. package/src/components/dashboard/action-buttons.tsx +93 -0
  111. package/src/components/dashboard/admin-alerts.tsx +86 -0
  112. package/src/components/dashboard/availability-alerts.tsx +65 -0
  113. package/src/components/dashboard/maintenance-due-list.tsx +87 -0
  114. package/src/components/dashboard/round-widget.tsx +110 -0
  115. package/src/components/dashboard/stat-card.tsx +38 -0
  116. package/src/components/dashboard/ticket-list-compact.tsx +86 -0
  117. package/src/components/departments/department-cards-view.tsx +244 -0
  118. package/src/components/departments/department-detail-view.tsx +467 -0
  119. package/src/components/departments/department-visuals.tsx +190 -0
  120. package/src/components/departments/my-department-view.tsx +333 -0
  121. package/src/components/devices/change-status-dialog.tsx +146 -0
  122. package/src/components/devices/device-action-buttons.tsx +125 -0
  123. package/src/components/devices/device-availability-banner.tsx +59 -0
  124. package/src/components/devices/device-badges.tsx +55 -0
  125. package/src/components/devices/device-documents-tab.tsx +93 -0
  126. package/src/components/devices/device-maintenance-tab.tsx +113 -0
  127. package/src/components/devices/device-mini-qr.tsx +109 -0
  128. package/src/components/devices/device-qr-card.tsx +187 -0
  129. package/src/components/devices/device-signatures-tab.tsx +607 -0
  130. package/src/components/devices/device-tickets-tab.tsx +139 -0
  131. package/src/components/devices/device-timelines.tsx +124 -0
  132. package/src/components/devices/qr-camera-scanner.tsx +167 -0
  133. package/src/components/devices/qr-code-display.tsx +139 -0
  134. package/src/components/devices/quick-add-category-dialog.tsx +135 -0
  135. package/src/components/devices/quick-add-manufacturer-dialog.tsx +212 -0
  136. package/src/components/devices/transfer-device-dialog.tsx +190 -0
  137. package/src/components/layout/header.tsx +80 -0
  138. package/src/components/layout/mobile-nav.tsx +91 -0
  139. package/src/components/layout/more-item-badge.tsx +15 -0
  140. package/src/components/layout/nav-badge.tsx +72 -0
  141. package/src/components/layout/sidebar.tsx +297 -0
  142. package/src/components/maintenance/maintenance-badges.tsx +579 -0
  143. package/src/components/maintenance/maintenance-nav.tsx +107 -0
  144. package/src/components/maintenance/parts-costs-manager.tsx +909 -0
  145. package/src/components/maintenance/signature-card.tsx +413 -0
  146. package/src/components/maintenance/task-action-bar.tsx +955 -0
  147. package/src/components/notifications/notification-bell.tsx +367 -0
  148. package/src/components/notifications/notification-item.tsx +93 -0
  149. package/src/components/notifications/notification-preferences.tsx +182 -0
  150. package/src/components/profile/user-profile-view.tsx +626 -0
  151. package/src/components/providers/nav-counts-provider.tsx +235 -0
  152. package/src/components/providers/session-provider.tsx +7 -0
  153. package/src/components/providers/theme-provider.tsx +124 -0
  154. package/src/components/pwa/install-prompt.tsx +82 -0
  155. package/src/components/pwa/sw-register.tsx +43 -0
  156. package/src/components/reports/export-button.tsx +62 -0
  157. package/src/components/reports/filter-chips.tsx +47 -0
  158. package/src/components/reports/summary-cards.tsx +40 -0
  159. package/src/components/reports/transparency-header.tsx +54 -0
  160. package/src/components/rounds/department-visit-sheet.tsx +199 -0
  161. package/src/components/rounds/mention-textarea.tsx +187 -0
  162. package/src/components/rounds/round-calendar.tsx +233 -0
  163. package/src/components/rounds/round-department-list.tsx +281 -0
  164. package/src/components/rounds/round-history-client.tsx +218 -0
  165. package/src/components/rounds/start-round-card.tsx +184 -0
  166. package/src/components/shared/confirm-dialog.tsx +67 -0
  167. package/src/components/shared/data-table.tsx +104 -0
  168. package/src/components/shared/page-header.tsx +20 -0
  169. package/src/components/shared/sign-out-button.tsx +52 -0
  170. package/src/components/shared/status-badge.tsx +42 -0
  171. package/src/components/shared/table-search.tsx +55 -0
  172. package/src/components/signatures/sign-action-dialog.tsx +253 -0
  173. package/src/components/signatures/signature-history.tsx +121 -0
  174. package/src/components/signatures/signature-status-panel.tsx +164 -0
  175. package/src/components/team/department-coverage.tsx +141 -0
  176. package/src/components/team/edit-engineer-dialog.tsx +242 -0
  177. package/src/components/team/engineer-card.tsx +180 -0
  178. package/src/components/team/engineer-details-dialog.tsx +432 -0
  179. package/src/components/team/schedule-calendar.tsx +204 -0
  180. package/src/components/team/staff-engineer-team-view.tsx +368 -0
  181. package/src/components/team/team-header-stats.tsx +88 -0
  182. package/src/components/team/team-page-client.tsx +216 -0
  183. package/src/components/theme/theme-selector-card.tsx +63 -0
  184. package/src/components/theme/theme-toggle.tsx +65 -0
  185. package/src/components/tickets/staff-status-label.tsx +58 -0
  186. package/src/components/tickets/staff-ticket-card.tsx +109 -0
  187. package/src/components/tickets/staff-ticket-progress-tracker.tsx +405 -0
  188. package/src/components/tickets/staff-tickets-view.tsx +182 -0
  189. package/src/components/tickets/ticket-action-buttons.tsx +873 -0
  190. package/src/components/tickets/ticket-auto-refresh.tsx +197 -0
  191. package/src/components/tickets/ticket-badges.tsx +43 -0
  192. package/src/components/tickets/ticket-card.tsx +197 -0
  193. package/src/components/tickets/ticket-cards-view.tsx +395 -0
  194. package/src/components/tickets/ticket-comment-section.tsx +684 -0
  195. package/src/components/tickets/ticket-dispatch-status.tsx +184 -0
  196. package/src/components/tickets/ticket-list-auto-refresh.tsx +55 -0
  197. package/src/components/tickets/ticket-reporter-profile.tsx +235 -0
  198. package/src/components/tickets/ticket-signatures-panel.tsx +32 -0
  199. package/src/components/tickets/ticket-timeline.tsx +131 -0
  200. package/src/components/tickets/ticket-triage-panel.tsx +124 -0
  201. package/src/components/tickets/tickets-nav.tsx +107 -0
  202. package/src/components/ui/avatar.tsx +43 -0
  203. package/src/components/ui/badge.tsx +34 -0
  204. package/src/components/ui/button.tsx +58 -0
  205. package/src/components/ui/card.tsx +77 -0
  206. package/src/components/ui/checkbox.tsx +31 -0
  207. package/src/components/ui/dialog.tsx +123 -0
  208. package/src/components/ui/dropdown-menu.tsx +126 -0
  209. package/src/components/ui/form.tsx +179 -0
  210. package/src/components/ui/input.tsx +24 -0
  211. package/src/components/ui/label.tsx +27 -0
  212. package/src/components/ui/popover.tsx +34 -0
  213. package/src/components/ui/progress.tsx +36 -0
  214. package/src/components/ui/scroll-area.tsx +47 -0
  215. package/src/components/ui/select.tsx +160 -0
  216. package/src/components/ui/separator.tsx +25 -0
  217. package/src/components/ui/sheet.tsx +131 -0
  218. package/src/components/ui/switch.tsx +30 -0
  219. package/src/components/ui/table.tsx +121 -0
  220. package/src/components/ui/tabs.tsx +62 -0
  221. package/src/components/ui/textarea.tsx +23 -0
  222. package/src/components/ui/tooltip.tsx +27 -0
  223. package/src/lib/actions/__tests__/devices.test.ts +176 -0
  224. package/src/lib/actions/__tests__/signatures.test.ts +303 -0
  225. package/src/lib/actions/__tests__/tickets.test.ts +243 -0
  226. package/src/lib/actions/audit-logs.ts +298 -0
  227. package/src/lib/actions/checklists.ts +429 -0
  228. package/src/lib/actions/dashboard.ts +358 -0
  229. package/src/lib/actions/data-management.ts +723 -0
  230. package/src/lib/actions/departments.ts +369 -0
  231. package/src/lib/actions/device-categories.ts +117 -0
  232. package/src/lib/actions/devices.ts +748 -0
  233. package/src/lib/actions/engineer-rounds.ts +973 -0
  234. package/src/lib/actions/engineers.ts +285 -0
  235. package/src/lib/actions/hospitals.ts +148 -0
  236. package/src/lib/actions/locations.ts +115 -0
  237. package/src/lib/actions/maintenance-plans.ts +316 -0
  238. package/src/lib/actions/maintenance-tasks.ts +415 -0
  239. package/src/lib/actions/manufacturers.ts +132 -0
  240. package/src/lib/actions/nav-counts.ts +144 -0
  241. package/src/lib/actions/notifications.ts +136 -0
  242. package/src/lib/actions/organizations.ts +99 -0
  243. package/src/lib/actions/qr.ts +147 -0
  244. package/src/lib/actions/report-history.ts +77 -0
  245. package/src/lib/actions/reports.ts +192 -0
  246. package/src/lib/actions/roles.ts +32 -0
  247. package/src/lib/actions/signature-auth.ts +377 -0
  248. package/src/lib/actions/signatures.ts +532 -0
  249. package/src/lib/actions/system-settings.ts +274 -0
  250. package/src/lib/actions/tickets.ts +1445 -0
  251. package/src/lib/actions/users.ts +740 -0
  252. package/src/lib/audit/index.ts +103 -0
  253. package/src/lib/auth/__tests__/rbac.test.ts +33 -0
  254. package/src/lib/auth/index.ts +338 -0
  255. package/src/lib/auth/password.ts +10 -0
  256. package/src/lib/auth/rbac.ts +103 -0
  257. package/src/lib/auth/types.ts +57 -0
  258. package/src/lib/constants/departments.ts +16 -0
  259. package/src/lib/constants/manufacturers.ts +32 -0
  260. package/src/lib/db/index.ts +23 -0
  261. package/src/lib/db/schema/audit-logs.ts +42 -0
  262. package/src/lib/db/schema/departments.ts +39 -0
  263. package/src/lib/db/schema/device-categories.ts +38 -0
  264. package/src/lib/db/schema/devices.ts +195 -0
  265. package/src/lib/db/schema/engineer-profiles.ts +63 -0
  266. package/src/lib/db/schema/engineer-rounds.ts +115 -0
  267. package/src/lib/db/schema/enums.ts +148 -0
  268. package/src/lib/db/schema/hospitals.ts +34 -0
  269. package/src/lib/db/schema/index.ts +24 -0
  270. package/src/lib/db/schema/locations.ts +46 -0
  271. package/src/lib/db/schema/maintenance-plans.ts +99 -0
  272. package/src/lib/db/schema/maintenance-records.ts +161 -0
  273. package/src/lib/db/schema/maintenance-tasks.ts +196 -0
  274. package/src/lib/db/schema/manufacturers.ts +60 -0
  275. package/src/lib/db/schema/notifications.ts +48 -0
  276. package/src/lib/db/schema/organizations.ts +24 -0
  277. package/src/lib/db/schema/report-history.ts +28 -0
  278. package/src/lib/db/schema/roles.ts +59 -0
  279. package/src/lib/db/schema/sessions.ts +46 -0
  280. package/src/lib/db/schema/signature-events.ts +48 -0
  281. package/src/lib/db/schema/signature-policies.ts +60 -0
  282. package/src/lib/db/schema/signatures.ts +87 -0
  283. package/src/lib/db/schema/tickets.ts +169 -0
  284. package/src/lib/db/schema/user-assignments.ts +85 -0
  285. package/src/lib/db/schema/users.ts +39 -0
  286. package/src/lib/db/seed.ts +131 -0
  287. package/src/lib/logger/__tests__/logger.test.ts +154 -0
  288. package/src/lib/logger/formatters.ts +159 -0
  289. package/src/lib/logger/index.ts +184 -0
  290. package/src/lib/logger/types.ts +48 -0
  291. package/src/lib/notifications/create.ts +71 -0
  292. package/src/lib/notifications/dispatcher.ts +490 -0
  293. package/src/lib/notifications/index.ts +7 -0
  294. package/src/lib/notifications/push-sender.ts +128 -0
  295. package/src/lib/notifications/push.ts +64 -0
  296. package/src/lib/notifications/triggers.ts +166 -0
  297. package/src/lib/utils/__tests__/crypto.test.ts +30 -0
  298. package/src/lib/utils/__tests__/export.test.ts +55 -0
  299. package/src/lib/utils/__tests__/nav-badge.test.ts +147 -0
  300. package/src/lib/utils/__tests__/staff-status.test.ts +50 -0
  301. package/src/lib/utils/attestations.ts +19 -0
  302. package/src/lib/utils/crypto.ts +18 -0
  303. package/src/lib/utils/csv-parser.ts +181 -0
  304. package/src/lib/utils/export.ts +45 -0
  305. package/src/lib/utils/nav-badge.ts +225 -0
  306. package/src/lib/utils/sound.ts +152 -0
  307. package/src/lib/utils/staff-status.ts +115 -0
  308. package/src/lib/utils.ts +6 -0
  309. package/src/lib/validators/__tests__/auth.test.ts +127 -0
  310. package/src/lib/validators/__tests__/data-management.test.ts +182 -0
  311. package/src/lib/validators/__tests__/devices-profile.test.ts +129 -0
  312. package/src/lib/validators/__tests__/devices.test.ts +98 -0
  313. package/src/lib/validators/__tests__/engineer-rounds.test.ts +170 -0
  314. package/src/lib/validators/__tests__/maintenance.test.ts +178 -0
  315. package/src/lib/validators/__tests__/signatures.test.ts +154 -0
  316. package/src/lib/validators/__tests__/system-settings.test.ts +119 -0
  317. package/src/lib/validators/__tests__/tickets.test.ts +176 -0
  318. package/src/lib/validators/__tests__/users.test.ts +92 -0
  319. package/src/lib/validators/auth.ts +32 -0
  320. package/src/lib/validators/data-management.ts +67 -0
  321. package/src/lib/validators/departments.ts +14 -0
  322. package/src/lib/validators/devices.ts +94 -0
  323. package/src/lib/validators/engineer-rounds.ts +70 -0
  324. package/src/lib/validators/hospitals.ts +17 -0
  325. package/src/lib/validators/locations.ts +15 -0
  326. package/src/lib/validators/maintenance.ts +211 -0
  327. package/src/lib/validators/organizations.ts +11 -0
  328. package/src/lib/validators/reports.ts +31 -0
  329. package/src/lib/validators/signatures.ts +49 -0
  330. package/src/lib/validators/system-settings.ts +55 -0
  331. package/src/lib/validators/tickets.ts +125 -0
  332. package/src/lib/validators/users.ts +43 -0
  333. package/src/middleware.ts +45 -0
  334. package/tailwind.config.ts +76 -0
  335. package/tsconfig.json +28 -0
@@ -0,0 +1,107 @@
1
+ import { auth } from '@/lib/auth';
2
+ import { redirect } from 'next/navigation';
3
+ import { getTodayRound, isEngineerRoundEnabled } from '@/lib/actions/engineer-rounds';
4
+ import { hasRole } from '@/lib/auth/rbac';
5
+ import { PageHeader } from '@/components/shared/page-header';
6
+ import { Button } from '@/components/ui/button';
7
+ import { Card, CardContent } from '@/components/ui/card';
8
+ import { StartRoundCard } from '@/components/rounds/start-round-card';
9
+ import { RoundDepartmentList } from '@/components/rounds/round-department-list';
10
+ import { History, CalendarDays, Users, AlertCircle, Coffee } from 'lucide-react';
11
+ import Link from 'next/link';
12
+
13
+ export const dynamic = 'force-dynamic';
14
+
15
+ export default async function RoundsPage() {
16
+ const session = await auth();
17
+ if (!session?.user) redirect('/login');
18
+
19
+ const user = session.user as any;
20
+ const enabled = await isEngineerRoundEnabled();
21
+
22
+ if (!enabled) {
23
+ return (
24
+ <div className="max-w-2xl mx-auto space-y-6">
25
+ <PageHeader
26
+ title="Engineer's Round"
27
+ description="Daily department rounds"
28
+ />
29
+ <Card>
30
+ <CardContent className="pt-6 text-center space-y-3">
31
+ <AlertCircle className="h-10 w-10 mx-auto text-muted-foreground" />
32
+ <h3 className="font-semibold">Feature Not Enabled</h3>
33
+ <p className="text-sm text-muted-foreground">
34
+ The Engineer&apos;s Round feature is not enabled for your organization.
35
+ An administrator can enable it in Organization Settings → Features.
36
+ </p>
37
+ </CardContent>
38
+ </Card>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ const result = await getTodayRound();
44
+
45
+ const isManager = hasRole(user.roles || [], 'BIOMED_MGR', 'HOSP_ADMIN', 'ORG_ADMIN', 'SYS_ADMIN');
46
+
47
+ return (
48
+ <div className="max-w-2xl mx-auto space-y-6">
49
+ <PageHeader
50
+ title="Engineer's Round"
51
+ description="Daily department inspection round"
52
+ >
53
+ <Link href="/rounds/history">
54
+ <Button variant="outline" size="sm" className="gap-1.5">
55
+ <History className="h-4 w-4" />
56
+ History
57
+ </Button>
58
+ </Link>
59
+ </PageHeader>
60
+
61
+ {/* Error state */}
62
+ {!result.success && (
63
+ <Card>
64
+ <CardContent className="pt-6 text-center space-y-3">
65
+ <AlertCircle className="h-10 w-10 mx-auto text-destructive" />
66
+ <h3 className="font-semibold">Unable to load round</h3>
67
+ <p className="text-sm text-muted-foreground">{result.error}</p>
68
+ </CardContent>
69
+ </Card>
70
+ )}
71
+
72
+ {/* Off day */}
73
+ {result.success && result.isOffDay && (
74
+ <Card>
75
+ <CardContent className="pt-6 text-center space-y-3">
76
+ <Coffee className="h-10 w-10 mx-auto text-muted-foreground" />
77
+ <h3 className="font-semibold">Day Off</h3>
78
+ <p className="text-sm text-muted-foreground">
79
+ Today is not a scheduled working day. Enjoy your time off!
80
+ </p>
81
+ <Link href="/rounds/history">
82
+ <Button variant="outline" size="sm" className="mt-2 gap-1.5">
83
+ <History className="h-4 w-4" />
84
+ View Round History
85
+ </Button>
86
+ </Link>
87
+ </CardContent>
88
+ </Card>
89
+ )}
90
+
91
+ {/* Can start a round */}
92
+ {result.success && result.canStart && !result.data && (
93
+ <StartRoundCard
94
+ hospitalId={result.hospitalId!}
95
+ hospitalName={result.hospitalName}
96
+ departments={result.departments || []}
97
+ shiftType={result.shiftType || 'morning'}
98
+ />
99
+ )}
100
+
101
+ {/* Round in progress or completed */}
102
+ {result.success && result.data && (
103
+ <RoundDepartmentList round={result.data as any} />
104
+ )}
105
+ </div>
106
+ );
107
+ }
@@ -0,0 +1,154 @@
1
+ import { resolveDeviceScan } from "@/lib/actions/qr"
2
+ import { getDeviceActiveTickets } from "@/lib/actions/tickets"
3
+ import { Card, CardContent, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
4
+ import { AlertCircle, ArrowLeft, AlertTriangle, ExternalLink, Ticket } from "lucide-react"
5
+ import { Button } from "@/components/ui/button"
6
+ import { DeviceStatusBadge } from "@/components/devices/device-badges"
7
+ import { DeviceAvailabilityBanner } from "@/components/devices/device-availability-banner"
8
+ import { TicketStatusBadge } from "@/components/tickets/ticket-badges"
9
+ import Link from "next/link"
10
+
11
+ export default async function ScanResolverPage({ params }: { params: Promise<{ code: string }> }) {
12
+ const { code } = await params
13
+ return <ScanResolverContent code={code} />
14
+ }
15
+
16
+ async function ScanResolverContent({ code }: { code: string }) {
17
+ const result = await resolveDeviceScan(code)
18
+
19
+ if (result?.success && result.data) {
20
+ const device = result.data
21
+ const ticketsResult = await getDeviceActiveTickets(device.deviceId)
22
+ const activeTickets = (ticketsResult?.success && Array.isArray(ticketsResult.data)) ? ticketsResult.data : []
23
+
24
+ return (
25
+ <div className="max-w-lg mx-auto space-y-5 pb-8">
26
+ {/* Navigation link */}
27
+ <Link
28
+ href="/scan"
29
+ className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground transition-colors"
30
+ >
31
+ <ArrowLeft className="w-4 h-4 mr-1.5" />
32
+ Back to Scanner
33
+ </Link>
34
+
35
+ {/* 1. Clinical Availability & Safety Banner */}
36
+ <DeviceAvailabilityBanner
37
+ status={device.currentStatus}
38
+ limitationsNote={device.statusLimitationsNote}
39
+ />
40
+
41
+ {/* 2. Device Identity Summary Card */}
42
+ <Card className="shadow-xs">
43
+ <CardHeader className="pb-3">
44
+ <div className="flex items-start justify-between gap-3">
45
+ <div>
46
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
47
+ {device.hospitalName || "Hospital"}
48
+ </p>
49
+ <CardTitle className="text-xl font-bold mt-0.5">{device.deviceName}</CardTitle>
50
+ </div>
51
+ <DeviceStatusBadge status={device.currentStatus} />
52
+ </div>
53
+ </CardHeader>
54
+ <CardContent className="space-y-3 pt-0 text-sm">
55
+ <div className="flex justify-between py-1.5 border-b text-xs">
56
+ <span className="text-muted-foreground">Asset Number</span>
57
+ <span className="font-mono font-semibold">{device.assetNumber}</span>
58
+ </div>
59
+ <div className="flex justify-between py-1.5 border-b text-xs">
60
+ <span className="text-muted-foreground">Internal Code</span>
61
+ <span className="font-mono">{device.internalCode}</span>
62
+ </div>
63
+ <div className="flex items-start justify-between py-1.5 border-b text-xs">
64
+ <span className="text-muted-foreground">Location</span>
65
+ <span className="text-right font-medium">
66
+ {device.departmentName ? `${device.departmentName} — ` : ""}
67
+ {device.locationName || "General Area"}
68
+ </span>
69
+ </div>
70
+ </CardContent>
71
+
72
+ <CardFooter className="flex flex-col gap-2.5 pt-2">
73
+ <Button
74
+ asChild
75
+ className="w-full bg-amber-600 hover:bg-amber-700 text-white font-semibold py-5"
76
+ >
77
+ <Link href={`/tickets/create?deviceId=${device.deviceId}&source=qr_scan`}>
78
+ <AlertTriangle className="w-4 h-4 mr-2" />
79
+ Report a Problem / Request Maintenance
80
+ </Link>
81
+ </Button>
82
+
83
+ <Button asChild variant="outline" className="w-full py-5">
84
+ <Link href={`/devices/${device.deviceId}`}>
85
+ <ExternalLink className="w-4 h-4 mr-2" />
86
+ View Full Device Profile & History
87
+ </Link>
88
+ </Button>
89
+ </CardFooter>
90
+ </Card>
91
+
92
+ {/* 3. Active Tickets Alert Card */}
93
+ {activeTickets.length > 0 && (
94
+ <Card className="border-amber-200 dark:border-amber-900/40 bg-amber-50/40 dark:bg-amber-950/10">
95
+ <CardHeader className="pb-3">
96
+ <CardTitle className="text-sm font-semibold text-amber-900 dark:text-amber-300 flex items-center gap-2">
97
+ <Ticket className="w-4 h-4 text-amber-600" />
98
+ Active Service Ticket in Progress ({activeTickets.length})
99
+ </CardTitle>
100
+ </CardHeader>
101
+ <CardContent className="space-y-3 pt-0">
102
+ {activeTickets.map((t: any) => (
103
+ <div
104
+ key={t.id}
105
+ className="p-3 bg-white dark:bg-card rounded-md border text-xs space-y-1.5"
106
+ >
107
+ <div className="flex items-center justify-between">
108
+ <span className="font-mono font-medium text-primary">{t.ticketNumber}</span>
109
+ <TicketStatusBadge status={t.statusCode} />
110
+ </div>
111
+ <p className="font-medium text-foreground text-sm">{t.title}</p>
112
+ <p className="text-muted-foreground line-clamp-1">{t.description}</p>
113
+ <div className="pt-1">
114
+ <Link
115
+ href={`/tickets/${t.id}`}
116
+ className="text-primary hover:underline font-medium inline-flex items-center gap-1"
117
+ >
118
+ Track this ticket & add updates →
119
+ </Link>
120
+ </div>
121
+ </div>
122
+ ))}
123
+ </CardContent>
124
+ </Card>
125
+ )}
126
+ </div>
127
+ )
128
+ }
129
+
130
+ // Error state for unknown or revoked QR code
131
+ return (
132
+ <div className="flex items-center justify-center min-h-[60vh] max-w-md mx-auto">
133
+ <Card className="w-full text-center">
134
+ <CardContent className="pt-8 pb-6 space-y-4">
135
+ <AlertCircle className="w-12 h-12 text-destructive mx-auto" />
136
+ <h2 className="text-lg font-semibold">QR Code Not Recognized</h2>
137
+ <p className="text-muted-foreground text-sm leading-relaxed px-4">
138
+ {result?.code === "INACTIVE"
139
+ ? "This QR label has been revoked or replaced with a new label. Please check the current label affixed to the device."
140
+ : "The scanned QR code is not associated with any active device in the BEMMS registry."}
141
+ </p>
142
+ <div className="flex flex-col sm:flex-row gap-2 justify-center pt-2">
143
+ <Button asChild variant="outline" className="w-full sm:w-auto">
144
+ <Link href="/scan">Scan Another</Link>
145
+ </Button>
146
+ <Button asChild className="w-full sm:w-auto">
147
+ <Link href="/devices">Browse Device Inventory</Link>
148
+ </Button>
149
+ </div>
150
+ </CardContent>
151
+ </Card>
152
+ </div>
153
+ )
154
+ }
@@ -0,0 +1,73 @@
1
+ "use client"
2
+
3
+ import { PageHeader } from "@/components/shared/page-header"
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
5
+ import { Button } from "@/components/ui/button"
6
+ import { Input } from "@/components/ui/input"
7
+ import { Camera, Search, QrCode } from "lucide-react"
8
+ import { useRouter } from "next/navigation"
9
+ import { useState } from "react"
10
+ import { QrCameraScanner } from "@/components/devices/qr-camera-scanner"
11
+
12
+ export default function ScanPage() {
13
+ const router = useRouter()
14
+ const [manualInput, setManualInput] = useState("")
15
+
16
+ const handleManualSearch = (e: React.FormEvent) => {
17
+ e.preventDefault()
18
+ if (manualInput.trim()) {
19
+ // If it looks like a QR reference (starts with dv_), go to scan resolver
20
+ if (manualInput.startsWith("dv_")) {
21
+ router.push(`/scan/${manualInput.trim()}`)
22
+ } else {
23
+ // Otherwise search by asset number in devices list
24
+ router.push(`/devices?search=${encodeURIComponent(manualInput.trim())}`)
25
+ }
26
+ }
27
+ }
28
+
29
+ return (
30
+ <div className="space-y-6 max-w-lg mx-auto">
31
+ <PageHeader
32
+ title="Scan Device QR"
33
+ description="Scan a QR code or enter an asset number to find a device"
34
+ />
35
+
36
+ <Card>
37
+ <CardHeader className="pb-3">
38
+ <CardTitle className="flex items-center gap-2 text-base">
39
+ <Camera className="w-5 h-5 text-primary" />
40
+ Live Camera Scanner
41
+ </CardTitle>
42
+ </CardHeader>
43
+ <CardContent>
44
+ <QrCameraScanner />
45
+ </CardContent>
46
+ </Card>
47
+
48
+ <Card>
49
+ <CardHeader>
50
+ <CardTitle className="flex items-center gap-2">
51
+ <Search className="w-5 h-5" />
52
+ Manual Lookup
53
+ </CardTitle>
54
+ </CardHeader>
55
+ <CardContent>
56
+ <form onSubmit={handleManualSearch} className="flex gap-2">
57
+ <Input
58
+ value={manualInput}
59
+ onChange={(e) => setManualInput(e.target.value)}
60
+ placeholder="Enter asset number or QR reference (dv_xxx)..."
61
+ className="flex-1"
62
+ />
63
+ <Button type="submit">
64
+ <Search className="w-4 h-4 mr-2" />
65
+ Find
66
+ </Button>
67
+ </form>
68
+ </CardContent>
69
+ </Card>
70
+ </div>
71
+ )
72
+ }
73
+
@@ -0,0 +1,42 @@
1
+ import { requireAuth } from '@/lib/auth/rbac';
2
+ import { getEngineersTeam, getDepartmentCoverageMatrix } from '@/lib/actions/engineers';
3
+ import { TeamPageClient } from '@/components/team/team-page-client';
4
+ import { Metadata } from 'next';
5
+
6
+ export const dynamic = 'force-dynamic';
7
+
8
+ export const metadata: Metadata = {
9
+ title: 'Engineering Team & Duty Schedule | BEMMS',
10
+ description: 'Hospital biomedical engineering roster, on-call schedules, and department coverage.',
11
+ };
12
+
13
+ export default async function TeamPage() {
14
+ await requireAuth();
15
+
16
+ const [teamRes, coverageRes] = await Promise.all([
17
+ getEngineersTeam(),
18
+ getDepartmentCoverageMatrix(),
19
+ ]);
20
+
21
+ const members = teamRes.data || [];
22
+ const stats = teamRes.stats || {
23
+ totalEngineers: members.length,
24
+ onDutyToday: members.filter((m) => m.dutyStatus === 'on_duty').length,
25
+ onCallToday: members.filter((m) => m.dutyStatus === 'on_call').length,
26
+ inMaintenanceToday: members.filter((m) => m.dutyStatus === 'in_maintenance').length,
27
+ offDutyToday: members.filter((m) => m.dutyStatus === 'off_duty').length,
28
+ totalActiveWorkOrders: 0,
29
+ };
30
+ const coverage = coverageRes.departments || [];
31
+
32
+ return (
33
+ <div className="space-y-6">
34
+ <TeamPageClient
35
+ initialMembers={members}
36
+ stats={stats}
37
+ coverage={coverage}
38
+ />
39
+ </div>
40
+ );
41
+ }
42
+