@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,180 @@
1
+ 'use client';
2
+
3
+ import { Card } from '@/components/ui/card';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { Button } from '@/components/ui/button';
6
+ import {
7
+ Phone,
8
+ PhoneCall,
9
+ Wrench,
10
+ CheckCircle2,
11
+ Moon,
12
+ ChevronRight,
13
+ Building2,
14
+ } from 'lucide-react';
15
+ import { EngineerTeamMember } from '@/lib/actions/engineers';
16
+ import { EngineerDutyStatus } from '@/lib/db/schema/engineer-profiles';
17
+
18
+ interface EngineerCardProps {
19
+ engineer: EngineerTeamMember;
20
+ onClick?: (engineer: EngineerTeamMember) => void;
21
+ onEdit?: (engineer: EngineerTeamMember) => void;
22
+ }
23
+
24
+ export function EngineerCard({ engineer, onClick, onEdit }: EngineerCardProps) {
25
+ const getStatusBadge = (status: EngineerDutyStatus) => {
26
+ switch (status) {
27
+ case 'on_duty':
28
+ return (
29
+ <Badge className="bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/30 gap-1 font-medium whitespace-nowrap text-[11px] h-5 px-1.5 shrink-0">
30
+ <span className="relative flex h-1.5 w-1.5">
31
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
32
+ <span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-500"></span>
33
+ </span>
34
+ On Duty
35
+ </Badge>
36
+ );
37
+ case 'on_call':
38
+ return (
39
+ <Badge className="bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/30 gap-1 font-medium whitespace-nowrap text-[11px] h-5 px-1.5 shrink-0">
40
+ <PhoneCall className="w-2.5 h-2.5 text-amber-500 animate-pulse" />
41
+ On Call
42
+ </Badge>
43
+ );
44
+ case 'in_maintenance':
45
+ return (
46
+ <Badge className="bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/30 gap-1 font-medium whitespace-nowrap text-[11px] h-5 px-1.5 shrink-0">
47
+ <Wrench className="w-2.5 h-2.5 text-blue-500" />
48
+ Maintenance
49
+ </Badge>
50
+ );
51
+ case 'off_duty':
52
+ return (
53
+ <Badge variant="outline" className="text-muted-foreground gap-1 font-medium whitespace-nowrap text-[11px] h-5 px-1.5 shrink-0">
54
+ <Moon className="w-2.5 h-2.5" />
55
+ Off Duty
56
+ </Badge>
57
+ );
58
+ }
59
+ };
60
+
61
+ const cleanName = (engineer.fullName || '').replace(/^Eng\.\s*/i, '');
62
+ const initials =
63
+ cleanName
64
+ .split(/\s+/)
65
+ .filter(Boolean)
66
+ .map((n) => n[0])
67
+ .join('')
68
+ .slice(0, 2)
69
+ .toUpperCase() || 'BE';
70
+
71
+ const statusColor =
72
+ engineer.dutyStatus === 'on_duty'
73
+ ? 'bg-emerald-500'
74
+ : engineer.dutyStatus === 'on_call'
75
+ ? 'bg-amber-500'
76
+ : engineer.dutyStatus === 'in_maintenance'
77
+ ? 'bg-blue-500'
78
+ : 'bg-muted-foreground/30';
79
+
80
+ return (
81
+ <Card
82
+ onClick={() => onClick?.(engineer)}
83
+ className="group relative flex flex-col justify-between border-border/70 hover:border-primary/50 hover:shadow-md transition-all cursor-pointer overflow-hidden bg-card active:scale-[0.99] select-none p-3.5 gap-3"
84
+ >
85
+ {/* Top Accent Strip */}
86
+ <div className={`absolute top-0 left-0 right-0 h-1 ${statusColor}`} />
87
+
88
+ {/* Main Header Row */}
89
+ <div className="flex items-start justify-between gap-2.5">
90
+ <div className="flex items-center gap-2.5 min-w-0 flex-1">
91
+ {/* Avatar with status indicator */}
92
+ <div className="relative shrink-0">
93
+ <div className="w-10 h-10 rounded-full bg-primary/10 text-primary font-bold text-xs flex items-center justify-center border border-primary/20">
94
+ {initials}
95
+ </div>
96
+ <span
97
+ className={`absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full ring-2 ring-background ${statusColor}`}
98
+ />
99
+ </div>
100
+
101
+ {/* Name & Title */}
102
+ <div className="min-w-0 flex-1">
103
+ <h3
104
+ className="font-semibold text-sm leading-tight text-foreground truncate group-hover:text-primary transition-colors"
105
+ title={engineer.fullName}
106
+ >
107
+ {engineer.fullName}
108
+ </h3>
109
+ <p className="text-[11px] text-muted-foreground truncate mt-0.5">
110
+ {engineer.jobTitle}
111
+ </p>
112
+ </div>
113
+ </div>
114
+
115
+ {/* Status Badge */}
116
+ {getStatusBadge(engineer.dutyStatus)}
117
+ </div>
118
+
119
+ {/* Middle: Specialization or Department chip */}
120
+ <div className="flex items-center gap-1.5 flex-wrap min-w-0 text-[11px]">
121
+ {engineer.specialization ? (
122
+ <span className="inline-flex items-center text-muted-foreground bg-muted/50 px-2 py-0.5 rounded border border-border/40 truncate max-w-full">
123
+ {engineer.specialization}
124
+ </span>
125
+ ) : engineer.coveredDepartments.length > 0 ? (
126
+ <span className="inline-flex items-center gap-1 text-muted-foreground bg-muted/50 px-2 py-0.5 rounded border border-border/40 truncate max-w-full">
127
+ <Building2 className="w-3 h-3 text-primary shrink-0" />
128
+ <span className="truncate">{engineer.coveredDepartments[0].name}</span>
129
+ {engineer.coveredDepartments.length > 1 && (
130
+ <span className="text-foreground/70 font-medium">+{engineer.coveredDepartments.length - 1}</span>
131
+ )}
132
+ </span>
133
+ ) : (
134
+ <span className="text-muted-foreground italic text-[11px]">Biomedical Team Pool</span>
135
+ )}
136
+ </div>
137
+
138
+ {/* Bottom Summary & Quick Action */}
139
+ <div className="flex items-center justify-between text-[11px] text-muted-foreground pt-2 border-t border-border/50">
140
+ <div className="flex items-center gap-2.5">
141
+ <span className="flex items-center gap-1" title="Assigned Devices">
142
+ <CheckCircle2 className="w-3 h-3 text-emerald-600" />
143
+ <strong className="text-foreground">{engineer.assignedDevicesCount}</strong> dev
144
+ </span>
145
+ <span className="flex items-center gap-1" title="Active Tasks & Tickets">
146
+ <Wrench className="w-3 h-3 text-blue-600" />
147
+ <strong className="text-foreground">{engineer.activeTicketsCount + engineer.activeMaintenanceCount}</strong> tasks
148
+ </span>
149
+ {engineer.extension && (
150
+ <span className="hidden sm:inline-flex items-center gap-0.5 font-mono text-[10px] bg-muted/60 px-1 py-0.2 rounded text-foreground font-medium">
151
+ Ext:{engineer.extension}
152
+ </span>
153
+ )}
154
+ </div>
155
+
156
+ <div className="flex items-center gap-1.5 shrink-0">
157
+ {engineer.mobileNumber && (
158
+ <Button
159
+ asChild
160
+ variant="ghost"
161
+ size="icon"
162
+ className="h-6 w-6 text-muted-foreground hover:text-emerald-600 hover:bg-emerald-50 dark:hover:bg-emerald-950/40"
163
+ onClick={(e) => e.stopPropagation()}
164
+ title={`Call ${engineer.mobileNumber}`}
165
+ >
166
+ <a href={`tel:${engineer.mobileNumber}`}>
167
+ <Phone className="w-3 h-3" />
168
+ </a>
169
+ </Button>
170
+ )}
171
+
172
+ <span className="text-[11px] text-primary font-medium flex items-center gap-0.5 group-hover:translate-x-0.5 transition-transform">
173
+ Details
174
+ <ChevronRight className="w-3 h-3" />
175
+ </span>
176
+ </div>
177
+ </div>
178
+ </Card>
179
+ );
180
+ }
@@ -0,0 +1,432 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ DialogDescription,
10
+ DialogFooter,
11
+ } from '@/components/ui/dialog';
12
+ import { Badge } from '@/components/ui/badge';
13
+ import { Button } from '@/components/ui/button';
14
+ import {
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuItem,
18
+ DropdownMenuTrigger,
19
+ } from '@/components/ui/dropdown-menu';
20
+ import {
21
+ Phone,
22
+ Mail,
23
+ PhoneCall,
24
+ Clock,
25
+ Calendar,
26
+ Building2,
27
+ Wrench,
28
+ CheckCircle2,
29
+ Moon,
30
+ ChevronDown,
31
+ Edit,
32
+ ExternalLink,
33
+ Shield,
34
+ Radio,
35
+ FileText,
36
+ } from 'lucide-react';
37
+ import { EngineerTeamMember, updateEngineerDutyStatus } from '@/lib/actions/engineers';
38
+ import { EngineerDutyStatus } from '@/lib/db/schema/engineer-profiles';
39
+ import { toast } from 'sonner';
40
+ import Link from 'next/link';
41
+
42
+ interface EngineerDetailsDialogProps {
43
+ engineer: EngineerTeamMember | null;
44
+ open: boolean;
45
+ onOpenChange: (open: boolean) => void;
46
+ onEdit?: (engineer: EngineerTeamMember) => void;
47
+ onStatusUpdated?: () => void;
48
+ }
49
+
50
+ export function EngineerDetailsDialog({
51
+ engineer,
52
+ open,
53
+ onOpenChange,
54
+ onEdit,
55
+ onStatusUpdated,
56
+ }: EngineerDetailsDialogProps) {
57
+ const [currentStatus, setCurrentStatus] = useState<EngineerDutyStatus>(engineer?.dutyStatus || 'off_duty');
58
+ const [isUpdatingStatus, setIsUpdatingStatus] = useState(false);
59
+
60
+ useEffect(() => {
61
+ if (engineer?.dutyStatus) {
62
+ setCurrentStatus(engineer.dutyStatus);
63
+ }
64
+ }, [engineer?.dutyStatus]);
65
+
66
+ if (!engineer) return null;
67
+
68
+ const handleStatusChange = async (newStatus: EngineerDutyStatus) => {
69
+ if (!engineer) return;
70
+ setIsUpdatingStatus(true);
71
+ try {
72
+ const res = await updateEngineerDutyStatus(engineer.id, newStatus);
73
+ if (res.success) {
74
+ setCurrentStatus(newStatus);
75
+ toast.success(`Duty status updated to ${formatStatus(newStatus)}`);
76
+ onStatusUpdated?.();
77
+ } else {
78
+ toast.error(res.error || 'Failed to update duty status');
79
+ }
80
+ } catch {
81
+ toast.error('An unexpected error occurred');
82
+ } finally {
83
+ setIsUpdatingStatus(false);
84
+ }
85
+ };
86
+
87
+ const formatStatus = (status: EngineerDutyStatus) => {
88
+ switch (status) {
89
+ case 'on_duty':
90
+ return 'On Duty';
91
+ case 'on_call':
92
+ return 'On Call';
93
+ case 'in_maintenance':
94
+ return 'In Maintenance';
95
+ case 'off_duty':
96
+ return 'Off Duty';
97
+ }
98
+ };
99
+
100
+ const getStatusBadge = (status: EngineerDutyStatus) => {
101
+ switch (status) {
102
+ case 'on_duty':
103
+ return (
104
+ <Badge className="bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/30 gap-1.5 font-medium whitespace-nowrap text-xs h-6 px-2.5">
105
+ <span className="relative flex h-2 w-2">
106
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
107
+ <span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
108
+ </span>
109
+ On Duty (In Hospital)
110
+ </Badge>
111
+ );
112
+ case 'on_call':
113
+ return (
114
+ <Badge className="bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/30 gap-1.5 font-medium whitespace-nowrap text-xs h-6 px-2.5">
115
+ <PhoneCall className="w-3.5 h-3.5 text-amber-500 animate-pulse" />
116
+ On Call (Emergency)
117
+ </Badge>
118
+ );
119
+ case 'in_maintenance':
120
+ return (
121
+ <Badge className="bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/30 gap-1.5 font-medium whitespace-nowrap text-xs h-6 px-2.5">
122
+ <Wrench className="w-3.5 h-3.5 text-blue-500" />
123
+ In Active Maintenance
124
+ </Badge>
125
+ );
126
+ case 'off_duty':
127
+ return (
128
+ <Badge variant="outline" className="text-muted-foreground gap-1.5 font-medium whitespace-nowrap text-xs h-6 px-2.5">
129
+ <Moon className="w-3.5 h-3.5" />
130
+ Off Duty / Rest
131
+ </Badge>
132
+ );
133
+ }
134
+ };
135
+
136
+ const cleanName = (engineer.fullName || '').replace(/^Eng\.\s*/i, '');
137
+ const initials =
138
+ cleanName
139
+ .split(/\s+/)
140
+ .filter(Boolean)
141
+ .map((n) => n[0])
142
+ .join('')
143
+ .slice(0, 2)
144
+ .toUpperCase() || 'BE';
145
+
146
+ const ALL_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
147
+
148
+ return (
149
+ <Dialog open={open} onOpenChange={onOpenChange}>
150
+ <DialogContent className="max-w-xl max-h-[90vh] overflow-y-auto p-0 gap-0">
151
+ {/* Top Status Accent Bar */}
152
+ <div
153
+ className={`h-2 w-full ${
154
+ currentStatus === 'on_duty'
155
+ ? 'bg-emerald-500'
156
+ : currentStatus === 'on_call'
157
+ ? 'bg-amber-500'
158
+ : currentStatus === 'in_maintenance'
159
+ ? 'bg-blue-500'
160
+ : 'bg-muted-foreground/30'
161
+ }`}
162
+ />
163
+
164
+ {/* Dialog Header */}
165
+ <div className="p-6 pb-4 border-b bg-card">
166
+ <div className="flex items-start justify-between gap-4">
167
+ <div className="flex items-start gap-3.5 min-w-0">
168
+ <div className="w-14 h-14 rounded-full bg-primary/10 text-primary font-bold text-lg flex items-center justify-center border-2 border-primary/20 shrink-0 shadow-sm">
169
+ {initials}
170
+ </div>
171
+ <div className="min-w-0">
172
+ <div className="flex items-center gap-2 flex-wrap">
173
+ <h2 className="text-lg font-bold text-foreground leading-tight">
174
+ {engineer.fullName}
175
+ </h2>
176
+ </div>
177
+ <p className="text-xs font-medium text-primary mt-0.5">{engineer.jobTitle}</p>
178
+ {engineer.specialization && (
179
+ <Badge variant="secondary" className="text-[11px] font-normal mt-1.5 px-2 py-0.5">
180
+ {engineer.specialization}
181
+ </Badge>
182
+ )}
183
+ </div>
184
+ </div>
185
+
186
+ {/* Duty Status Selector & Edit */}
187
+ <div className="flex flex-col items-end gap-1.5 shrink-0">
188
+ <DropdownMenu>
189
+ <DropdownMenuTrigger asChild>
190
+ <Button variant="ghost" size="sm" className="h-7 px-2 text-xs gap-1 hover:bg-muted/80 cursor-pointer border" disabled={isUpdatingStatus}>
191
+ {getStatusBadge(currentStatus)}
192
+ <ChevronDown className="h-3 w-3 text-muted-foreground ml-1" />
193
+ </Button>
194
+ </DropdownMenuTrigger>
195
+ <DropdownMenuContent align="end" className="w-52 text-xs">
196
+ <DropdownMenuItem onClick={() => handleStatusChange('on_duty')} className="gap-2 cursor-pointer">
197
+ <span className="w-2 h-2 rounded-full bg-emerald-500" />
198
+ On Duty (In Hospital)
199
+ </DropdownMenuItem>
200
+ <DropdownMenuItem onClick={() => handleStatusChange('on_call')} className="gap-2 cursor-pointer">
201
+ <span className="w-2 h-2 rounded-full bg-amber-500" />
202
+ On Call (Emergency)
203
+ </DropdownMenuItem>
204
+ <DropdownMenuItem onClick={() => handleStatusChange('in_maintenance')} className="gap-2 cursor-pointer">
205
+ <span className="w-2 h-2 rounded-full bg-blue-500" />
206
+ In Active Maintenance
207
+ </DropdownMenuItem>
208
+ <DropdownMenuItem onClick={() => handleStatusChange('off_duty')} className="gap-2 cursor-pointer">
209
+ <span className="w-2 h-2 rounded-full bg-slate-400" />
210
+ Off Duty
211
+ </DropdownMenuItem>
212
+ </DropdownMenuContent>
213
+ </DropdownMenu>
214
+
215
+ {onEdit && (
216
+ <Button
217
+ variant="outline"
218
+ size="sm"
219
+ className="h-7 text-xs gap-1.5 text-muted-foreground hover:text-foreground"
220
+ onClick={() => {
221
+ onOpenChange(false);
222
+ onEdit(engineer);
223
+ }}
224
+ >
225
+ <Edit className="h-3.5 w-3.5" />
226
+ Edit Schedule
227
+ </Button>
228
+ )}
229
+ </div>
230
+ </div>
231
+ </div>
232
+
233
+ {/* Content Body */}
234
+ <div className="p-6 space-y-5 text-xs">
235
+ {/* 1. Contact Information Row */}
236
+ <div>
237
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2.5">
238
+ Contact & Direct Channels
239
+ </h4>
240
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-2.5">
241
+ {/* Mobile */}
242
+ <div className="p-3 rounded-lg bg-muted/30 border border-border/50 flex flex-col justify-between">
243
+ <span className="text-[11px] text-muted-foreground flex items-center gap-1.5 mb-1">
244
+ <Phone className="w-3.5 h-3.5 text-primary" />
245
+ Direct Mobile
246
+ </span>
247
+ {engineer.mobileNumber ? (
248
+ <div className="flex items-center justify-between gap-1 mt-1">
249
+ <span className="font-semibold text-foreground text-xs">{engineer.mobileNumber}</span>
250
+ <Button asChild size="sm" variant="ghost" className="h-6 px-1.5 text-xs text-primary">
251
+ <a href={`tel:${engineer.mobileNumber}`}>Call</a>
252
+ </Button>
253
+ </div>
254
+ ) : (
255
+ <span className="text-muted-foreground italic text-xs">Not provided</span>
256
+ )}
257
+ </div>
258
+
259
+ {/* Extension */}
260
+ <div className="p-3 rounded-lg bg-muted/30 border border-border/50 flex flex-col justify-between">
261
+ <span className="text-[11px] text-muted-foreground flex items-center gap-1.5 mb-1">
262
+ <PhoneCall className="w-3.5 h-3.5 text-primary" />
263
+ Hospital Extension
264
+ </span>
265
+ <div className="mt-1">
266
+ {engineer.extension ? (
267
+ <span className="font-bold text-foreground text-sm font-mono">{engineer.extension}</span>
268
+ ) : (
269
+ <span className="text-muted-foreground italic text-xs">No extension</span>
270
+ )}
271
+ </div>
272
+ </div>
273
+
274
+ {/* Email */}
275
+ <div className="p-3 rounded-lg bg-muted/30 border border-border/50 flex flex-col justify-between">
276
+ <span className="text-[11px] text-muted-foreground flex items-center gap-1.5 mb-1">
277
+ <Mail className="w-3.5 h-3.5 text-primary" />
278
+ Email Address
279
+ </span>
280
+ <div className="flex items-center justify-between gap-1 mt-1">
281
+ <span className="font-medium text-foreground text-xs truncate" title={engineer.email}>
282
+ {engineer.email}
283
+ </span>
284
+ <Button asChild size="sm" variant="ghost" className="h-6 px-1.5 text-xs text-primary">
285
+ <a href={`mailto:${engineer.email}`}>Email</a>
286
+ </Button>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </div>
291
+
292
+ {/* 2. Workload & Responsibilities Stats */}
293
+ <div>
294
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2.5">
295
+ Assigned Workload & Responsibility
296
+ </h4>
297
+ <div className="grid grid-cols-3 gap-2.5 text-center">
298
+ <div className="p-3 rounded-lg bg-emerald-500/10 border border-emerald-500/20">
299
+ <p className="text-xl font-bold text-emerald-700 dark:text-emerald-400">
300
+ {engineer.assignedDevicesCount}
301
+ </p>
302
+ <p className="text-[11px] text-muted-foreground mt-0.5 flex items-center justify-center gap-1">
303
+ <CheckCircle2 className="w-3 h-3 text-emerald-600" />
304
+ Assigned Devices
305
+ </p>
306
+ </div>
307
+
308
+ <div className="p-3 rounded-lg bg-blue-500/10 border border-blue-500/20">
309
+ <p className="text-xl font-bold text-blue-700 dark:text-blue-400">
310
+ {engineer.activeTicketsCount}
311
+ </p>
312
+ <p className="text-[11px] text-muted-foreground mt-0.5 flex items-center justify-center gap-1">
313
+ <Wrench className="w-3 h-3 text-blue-600" />
314
+ Active Tickets
315
+ </p>
316
+ </div>
317
+
318
+ <div className="p-3 rounded-lg bg-amber-500/10 border border-amber-500/20">
319
+ <p className="text-xl font-bold text-amber-700 dark:text-amber-400">
320
+ {engineer.activeMaintenanceCount}
321
+ </p>
322
+ <p className="text-[11px] text-muted-foreground mt-0.5 flex items-center justify-center gap-1">
323
+ <Clock className="w-3 h-3 text-amber-600" />
324
+ PM Tasks
325
+ </p>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ {/* 3. Shift Schedule & Working Days */}
331
+ <div>
332
+ <div className="flex items-center justify-between mb-2">
333
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
334
+ Working Days & Shift Schedule
335
+ </h4>
336
+ <Badge variant="outline" className="text-[11px] gap-1 font-mono">
337
+ <Clock className="w-3 h-3 text-primary" />
338
+ {engineer.shiftHours || '08:00 AM - 04:00 PM'}
339
+ </Badge>
340
+ </div>
341
+
342
+ <div className="grid grid-cols-7 gap-1.5 p-3 rounded-lg bg-muted/30 border border-border/50">
343
+ {ALL_DAYS.map((day) => {
344
+ const isWorking = engineer.workingDays.includes(day);
345
+ const isOnCall = engineer.onCallDays.includes(day);
346
+ return (
347
+ <div
348
+ key={day}
349
+ className={`py-2 px-1 text-center rounded-md border text-xs flex flex-col items-center justify-between min-h-[52px] ${
350
+ isWorking
351
+ ? 'bg-primary/10 border-primary/30 text-primary font-bold shadow-xs'
352
+ : isOnCall
353
+ ? 'bg-amber-500/15 border-amber-500/30 text-amber-700 dark:text-amber-400 font-bold'
354
+ : 'bg-background/60 border-border/40 text-muted-foreground/50'
355
+ }`}
356
+ >
357
+ <span className="text-[11px]">{day}</span>
358
+ <span className="text-[10px] mt-0.5 font-normal">
359
+ {isWorking ? '🟢 Shift' : isOnCall ? '📞 Call' : 'Off'}
360
+ </span>
361
+ </div>
362
+ );
363
+ })}
364
+ </div>
365
+ </div>
366
+
367
+ {/* 4. Department Coverage */}
368
+ <div>
369
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2 flex items-center justify-between">
370
+ <span>Department Coverage ({engineer.coveredDepartments.length})</span>
371
+ {engineer.hospitalName && (
372
+ <span className="text-muted-foreground font-normal normal-case">{engineer.hospitalName}</span>
373
+ )}
374
+ </h4>
375
+ <div className="flex flex-wrap gap-1.5 p-3 rounded-lg bg-muted/30 border border-border/50">
376
+ {engineer.coveredDepartments.length > 0 ? (
377
+ engineer.coveredDepartments.map((dept) => {
378
+ const label = dept.name.includes(dept.code) ? dept.name : `${dept.name} (${dept.code})`;
379
+ return (
380
+ <Badge
381
+ key={dept.id}
382
+ variant="secondary"
383
+ className="text-xs px-2.5 py-1 font-normal flex items-center gap-1.5"
384
+ >
385
+ <Building2 className="w-3 h-3 text-primary" />
386
+ {label}
387
+ </Badge>
388
+ );
389
+ })
390
+ ) : (
391
+ <span className="text-muted-foreground italic text-xs">
392
+ Assigned to hospital general biomedical pool
393
+ </span>
394
+ )}
395
+ </div>
396
+ </div>
397
+
398
+ {/* 5. Notes & Qualifications */}
399
+ {engineer.notes && (
400
+ <div>
401
+ <h4 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1.5 flex items-center gap-1.5">
402
+ <FileText className="w-3.5 h-3.5 text-primary" />
403
+ Notes & Clinical Qualifications
404
+ </h4>
405
+ <p className="text-xs text-foreground bg-muted/30 p-3 rounded-lg border border-border/50 leading-relaxed whitespace-pre-wrap">
406
+ {engineer.notes}
407
+ </p>
408
+ </div>
409
+ )}
410
+ </div>
411
+
412
+ {/* Footer Actions */}
413
+ <DialogFooter className="p-4 border-t bg-muted/20 flex flex-row justify-between items-center sm:justify-between">
414
+ <Button variant="outline" size="sm" onClick={() => onOpenChange(false)}>
415
+ Close
416
+ </Button>
417
+
418
+ <div className="flex items-center gap-2">
419
+ {engineer.mobileNumber && (
420
+ <Button asChild size="sm" className="gap-1.5">
421
+ <a href={`tel:${engineer.mobileNumber}`}>
422
+ <Phone className="w-3.5 h-3.5" />
423
+ Call Engineer
424
+ </a>
425
+ </Button>
426
+ )}
427
+ </div>
428
+ </DialogFooter>
429
+ </DialogContent>
430
+ </Dialog>
431
+ );
432
+ }