@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,61 @@
1
+ 'use client';
2
+
3
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
4
+ import { UploadCloud, Download, AlertTriangle } from 'lucide-react';
5
+ import { ImportWizard } from './import-wizard';
6
+ import { ExportCenter } from './export-center';
7
+ import { DangerZone } from './danger-zone';
8
+
9
+ interface DataManagementTabsProps {
10
+ summary: {
11
+ devices: number;
12
+ departments: number;
13
+ categories: number;
14
+ manufacturers: number;
15
+ tickets: number;
16
+ auditLogs: number;
17
+ };
18
+ }
19
+
20
+ export function DataManagementTabs({ summary }: DataManagementTabsProps) {
21
+ return (
22
+ <div className="space-y-6">
23
+ <div>
24
+ <h1 className="text-2xl font-bold tracking-tight">Export & Import Center</h1>
25
+ <p className="text-sm text-muted-foreground">
26
+ Bulk import medical equipment and departments, export datasets in Excel or CSV, and manage data lifecycle.
27
+ </p>
28
+ </div>
29
+
30
+ <Tabs defaultValue="import" className="space-y-6">
31
+ <TabsList className="grid grid-cols-3 w-full max-w-md h-auto p-1 gap-1">
32
+ <TabsTrigger value="import" className="flex items-center gap-2 py-2">
33
+ <UploadCloud className="h-4 w-4" />
34
+ <span>Import</span>
35
+ </TabsTrigger>
36
+ <TabsTrigger value="export" className="flex items-center gap-2 py-2">
37
+ <Download className="h-4 w-4" />
38
+ <span>Export</span>
39
+ </TabsTrigger>
40
+ <TabsTrigger value="danger" className="flex items-center gap-2 py-2 text-destructive data-[state=active]:text-destructive">
41
+ <AlertTriangle className="h-4 w-4" />
42
+ <span>Danger Zone</span>
43
+ </TabsTrigger>
44
+ </TabsList>
45
+
46
+ <TabsContent value="import" className="space-y-4">
47
+ <ImportWizard />
48
+ </TabsContent>
49
+
50
+ <TabsContent value="export" className="space-y-4">
51
+ <ExportCenter summary={summary} />
52
+ </TabsContent>
53
+
54
+ <TabsContent value="danger" className="space-y-4">
55
+ <DangerZone />
56
+ </TabsContent>
57
+ </Tabs>
58
+ </div>
59
+ );
60
+ }
61
+
@@ -0,0 +1,301 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Badge } from '@/components/ui/badge';
7
+ import { Checkbox } from '@/components/ui/checkbox';
8
+ import { Label } from '@/components/ui/label';
9
+ import { Download, FileSpreadsheet, FileText, FileJson, CheckSquare, Layers, ShieldCheck, Wrench, Building2, HardDrive, Route } from 'lucide-react';
10
+ import { toast } from 'sonner';
11
+
12
+ interface ExportCenterProps {
13
+ summary: {
14
+ devices: number;
15
+ departments: number;
16
+ categories: number;
17
+ manufacturers: number;
18
+ tickets: number;
19
+ auditLogs: number;
20
+ rounds?: number;
21
+ };
22
+ }
23
+
24
+ export function ExportCenter({ summary }: ExportCenterProps) {
25
+ const [selectedEntities, setSelectedEntities] = useState<string[]>(['devices', 'departments']);
26
+ const [format, setFormat] = useState<'xlsx' | 'csv' | 'json'>('xlsx');
27
+ const [downloading, setDownloading] = useState(false);
28
+
29
+ const toggleEntity = (entity: string) => {
30
+ setSelectedEntities((prev) =>
31
+ prev.includes(entity) ? prev.filter((e) => e !== entity) : [...prev, entity]
32
+ );
33
+ };
34
+
35
+ const handleSelectAll = () => {
36
+ setSelectedEntities(['devices', 'departments', 'catalogs', 'tickets', 'audit_logs', 'rounds']);
37
+ };
38
+
39
+ const handleClearAll = () => {
40
+ setSelectedEntities([]);
41
+ };
42
+
43
+ const handleExport = () => {
44
+ if (selectedEntities.length === 0) {
45
+ toast.error('Please select at least one dataset to export');
46
+ return;
47
+ }
48
+
49
+ setDownloading(true);
50
+ const query = new URLSearchParams({
51
+ entities: selectedEntities.join(','),
52
+ format,
53
+ });
54
+
55
+ const exportUrl = `/api/admin/data/export?${query.toString()}`;
56
+ const link = document.createElement('a');
57
+ link.href = exportUrl;
58
+ link.target = '_blank';
59
+ document.body.appendChild(link);
60
+ link.click();
61
+ document.body.removeChild(link);
62
+
63
+ setTimeout(() => {
64
+ setDownloading(false);
65
+ toast.success('Export started! Check your browser downloads.');
66
+ }, 1200);
67
+ };
68
+
69
+ return (
70
+ <div className="space-y-6">
71
+ <Card>
72
+ <CardHeader>
73
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
74
+ <div>
75
+ <CardTitle className="text-xl flex items-center gap-2">
76
+ <HardDrive className="h-5 w-5 text-primary" />
77
+ Data Export & Backup Center
78
+ </CardTitle>
79
+ <CardDescription>
80
+ Export medical equipment records, hospital structures, and maintenance histories for spreadsheets or migrations.
81
+ </CardDescription>
82
+ </div>
83
+ <div className="flex items-center gap-2 shrink-0">
84
+ <Button variant="ghost" size="sm" onClick={handleSelectAll} className="text-xs">
85
+ Select All
86
+ </Button>
87
+ <Button variant="ghost" size="sm" onClick={handleClearAll} className="text-xs text-muted-foreground">
88
+ Clear
89
+ </Button>
90
+ </div>
91
+ </div>
92
+ </CardHeader>
93
+
94
+ <CardContent className="space-y-6">
95
+ {/* Datasets Selection Grid */}
96
+ <div className="space-y-3">
97
+ <Label className="text-sm font-semibold">Select Datasets to Include</Label>
98
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
99
+ {/* Medical Devices */}
100
+ <div
101
+ onClick={() => toggleEntity('devices')}
102
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors ${
103
+ selectedEntities.includes('devices') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
104
+ }`}
105
+ >
106
+ <div className="flex items-center gap-3">
107
+ <Checkbox checked={selectedEntities.includes('devices')} />
108
+ <div>
109
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
110
+ <Layers className="h-4 w-4 text-primary" />
111
+ Medical Equipment & Devices
112
+ </h5>
113
+ <p className="text-xs text-muted-foreground">
114
+ Asset tags, serial numbers, locations, status & criticality
115
+ </p>
116
+ </div>
117
+ </div>
118
+ <Badge variant="secondary">{summary.devices} items</Badge>
119
+ </div>
120
+
121
+ {/* Departments */}
122
+ <div
123
+ onClick={() => toggleEntity('departments')}
124
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors ${
125
+ selectedEntities.includes('departments') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
126
+ }`}
127
+ >
128
+ <div className="flex items-center gap-3">
129
+ <Checkbox checked={selectedEntities.includes('departments')} />
130
+ <div>
131
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
132
+ <Building2 className="h-4 w-4 text-primary" />
133
+ Hospitals & Departments
134
+ </h5>
135
+ <p className="text-xs text-muted-foreground">
136
+ Campus units, clinical departments, manager scopes
137
+ </p>
138
+ </div>
139
+ </div>
140
+ <Badge variant="secondary">{summary.departments} units</Badge>
141
+ </div>
142
+
143
+ {/* Master Catalogs */}
144
+ <div
145
+ onClick={() => toggleEntity('catalogs')}
146
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors ${
147
+ selectedEntities.includes('catalogs') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
148
+ }`}
149
+ >
150
+ <div className="flex items-center gap-3">
151
+ <Checkbox checked={selectedEntities.includes('catalogs')} />
152
+ <div>
153
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
154
+ <CheckSquare className="h-4 w-4 text-primary" />
155
+ Reference Catalogs
156
+ </h5>
157
+ <p className="text-xs text-muted-foreground">
158
+ Approved manufacturers and device categories
159
+ </p>
160
+ </div>
161
+ </div>
162
+ <Badge variant="secondary">
163
+ {summary.categories + summary.manufacturers} records
164
+ </Badge>
165
+ </div>
166
+
167
+ {/* Tickets */}
168
+ <div
169
+ onClick={() => toggleEntity('tickets')}
170
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors ${
171
+ selectedEntities.includes('tickets') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
172
+ }`}
173
+ >
174
+ <div className="flex items-center gap-3">
175
+ <Checkbox checked={selectedEntities.includes('tickets')} />
176
+ <div>
177
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
178
+ <Wrench className="h-4 w-4 text-primary" />
179
+ Helpdesk Tickets & Work
180
+ </h5>
181
+ <p className="text-xs text-muted-foreground">
182
+ Incident logs, triage notes, and resolution history
183
+ </p>
184
+ </div>
185
+ </div>
186
+ <Badge variant="secondary">{summary.tickets} tickets</Badge>
187
+ </div>
188
+
189
+ {/* Engineer's Daily Rounds */}
190
+ <div
191
+ onClick={() => toggleEntity('rounds')}
192
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors ${
193
+ selectedEntities.includes('rounds') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
194
+ }`}
195
+ >
196
+ <div className="flex items-center gap-3">
197
+ <Checkbox checked={selectedEntities.includes('rounds')} />
198
+ <div>
199
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
200
+ <Route className="h-4 w-4 text-primary" />
201
+ Engineer&apos;s Daily Rounds
202
+ </h5>
203
+ <p className="text-xs text-muted-foreground">
204
+ Daily department inspections, electronic signatures, and @device notes
205
+ </p>
206
+ </div>
207
+ </div>
208
+ <Badge variant="secondary">{summary.rounds || 0} rounds</Badge>
209
+ </div>
210
+
211
+ {/* Audit Logs */}
212
+ <div
213
+ onClick={() => toggleEntity('audit_logs')}
214
+ className={`p-4 rounded-lg border flex items-center justify-between cursor-pointer transition-colors md:col-span-2 ${
215
+ selectedEntities.includes('audit_logs') ? 'border-primary bg-primary/5' : 'hover:bg-muted/40'
216
+ }`}
217
+ >
218
+ <div className="flex items-center gap-3">
219
+ <Checkbox checked={selectedEntities.includes('audit_logs')} />
220
+ <div>
221
+ <h5 className="font-medium text-sm flex items-center gap-1.5">
222
+ <ShieldCheck className="h-4 w-4 text-primary" />
223
+ 21 CFR Part 11 Audit Trail
224
+ </h5>
225
+ <p className="text-xs text-muted-foreground">
226
+ Immutable record of access, signatures, and configuration changes
227
+ </p>
228
+ </div>
229
+ </div>
230
+ <Badge variant="secondary">{summary.auditLogs} logs</Badge>
231
+ </div>
232
+ </div>
233
+ </div>
234
+
235
+ {/* Format Selection */}
236
+ <div className="space-y-3 pt-4 border-t">
237
+ <Label className="text-sm font-semibold">Choose Export Format</Label>
238
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
239
+ <div
240
+ onClick={() => setFormat('xlsx')}
241
+ className={`p-4 rounded-lg border flex items-center gap-3 cursor-pointer transition-colors ${
242
+ format === 'xlsx' ? 'border-primary bg-primary/5 ring-1 ring-primary' : 'hover:bg-muted/40'
243
+ }`}
244
+ >
245
+ <FileSpreadsheet className="h-6 w-6 text-emerald-600 shrink-0" />
246
+ <div>
247
+ <h6 className="font-semibold text-sm">Microsoft Excel (.xlsx)</h6>
248
+ <p className="text-xs text-muted-foreground">
249
+ Native multi-sheet workbook with columns and styling.
250
+ </p>
251
+ </div>
252
+ </div>
253
+
254
+ <div
255
+ onClick={() => setFormat('csv')}
256
+ className={`p-4 rounded-lg border flex items-center gap-3 cursor-pointer transition-colors ${
257
+ format === 'csv' ? 'border-primary bg-primary/5 ring-1 ring-primary' : 'hover:bg-muted/40'
258
+ }`}
259
+ >
260
+ <FileText className="h-6 w-6 text-blue-600 shrink-0" />
261
+ <div>
262
+ <h6 className="font-semibold text-sm">Standard CSV (.csv)</h6>
263
+ <p className="text-xs text-muted-foreground">
264
+ Includes UTF-8 BOM for full English & Arabic character support.
265
+ </p>
266
+ </div>
267
+ </div>
268
+
269
+ <div
270
+ onClick={() => setFormat('json')}
271
+ className={`p-4 rounded-lg border flex items-center gap-3 cursor-pointer transition-colors ${
272
+ format === 'json' ? 'border-primary bg-primary/5 ring-1 ring-primary' : 'hover:bg-muted/40'
273
+ }`}
274
+ >
275
+ <FileJson className="h-6 w-6 text-indigo-600 shrink-0" />
276
+ <div>
277
+ <h6 className="font-semibold text-sm">Structured JSON (.json)</h6>
278
+ <p className="text-xs text-muted-foreground">
279
+ Complete nested data format for developer backups and migrations.
280
+ </p>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </CardContent>
286
+
287
+ <CardFooter className="flex justify-end border-t pt-4">
288
+ <Button
289
+ onClick={handleExport}
290
+ disabled={downloading || selectedEntities.length === 0}
291
+ className="gap-2"
292
+ >
293
+ <Download className="h-4 w-4" />
294
+ {downloading ? 'Preparing File...' : `Download ${format.toUpperCase()} Export`}
295
+ </Button>
296
+ </CardFooter>
297
+ </Card>
298
+ </div>
299
+ );
300
+ }
301
+