@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.
- package/LICENSE +22 -0
- package/README.md +203 -2
- package/drizzle.config.ts +11 -0
- package/next.config.ts +51 -0
- package/package.json +99 -3
- package/postcss.config.mjs +7 -0
- package/public/hospital_demo_devices.csv +32 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/icons/icon-maskable-512.png +0 -0
- package/public/manifest.webmanifest +31 -0
- package/public/sw.js +177 -0
- package/src/app/(auth)/layout.tsx +20 -0
- package/src/app/(auth)/login/page.tsx +135 -0
- package/src/app/(dashboard)/admin/audit-logs/page.tsx +178 -0
- package/src/app/(dashboard)/admin/data/page.tsx +37 -0
- package/src/app/(dashboard)/admin/departments/[deptId]/edit/page.tsx +206 -0
- package/src/app/(dashboard)/admin/departments/[deptId]/page.tsx +71 -0
- package/src/app/(dashboard)/admin/departments/new/page.tsx +6 -0
- package/src/app/(dashboard)/admin/departments/page.tsx +6 -0
- package/src/app/(dashboard)/admin/general/page.tsx +374 -0
- package/src/app/(dashboard)/admin/hospitals/[hospitalId]/edit/page.tsx +304 -0
- package/src/app/(dashboard)/admin/hospitals/[hospitalId]/page.tsx +94 -0
- package/src/app/(dashboard)/admin/hospitals/new/page.tsx +302 -0
- package/src/app/(dashboard)/admin/hospitals/page.tsx +6 -0
- package/src/app/(dashboard)/admin/layout.tsx +49 -0
- package/src/app/(dashboard)/admin/organizations/[orgId]/edit/page.tsx +172 -0
- package/src/app/(dashboard)/admin/organizations/[orgId]/page.tsx +82 -0
- package/src/app/(dashboard)/admin/organizations/new/page.tsx +150 -0
- package/src/app/(dashboard)/admin/organizations/page.tsx +6 -0
- package/src/app/(dashboard)/admin/page.tsx +258 -0
- package/src/app/(dashboard)/admin/roles/page.tsx +153 -0
- package/src/app/(dashboard)/admin/settings/page.tsx +69 -0
- package/src/app/(dashboard)/admin/users/[userId]/edit/page.tsx +153 -0
- package/src/app/(dashboard)/admin/users/[userId]/page.tsx +317 -0
- package/src/app/(dashboard)/admin/users/invite/page.tsx +3 -0
- package/src/app/(dashboard)/admin/users/new/page.tsx +5 -0
- package/src/app/(dashboard)/admin/users/page.tsx +23 -0
- package/src/app/(dashboard)/dashboard/page.tsx +462 -0
- package/src/app/(dashboard)/departments/[deptId]/page.tsx +39 -0
- package/src/app/(dashboard)/departments/new/page.tsx +330 -0
- package/src/app/(dashboard)/departments/page.tsx +52 -0
- package/src/app/(dashboard)/devices/[id]/edit/page.tsx +131 -0
- package/src/app/(dashboard)/devices/[id]/page.tsx +408 -0
- package/src/app/(dashboard)/devices/[id]/qr/page.tsx +54 -0
- package/src/app/(dashboard)/devices/new/page.tsx +382 -0
- package/src/app/(dashboard)/devices/page.tsx +80 -0
- package/src/app/(dashboard)/engineering-team/page.tsx +41 -0
- package/src/app/(dashboard)/layout.tsx +43 -0
- package/src/app/(dashboard)/maintenance/checklists/page.tsx +57 -0
- package/src/app/(dashboard)/maintenance/page.tsx +231 -0
- package/src/app/(dashboard)/maintenance/plans/page.tsx +142 -0
- package/src/app/(dashboard)/maintenance/tasks/[id]/page.tsx +997 -0
- package/src/app/(dashboard)/maintenance/tasks/page.tsx +119 -0
- package/src/app/(dashboard)/more/page.tsx +310 -0
- package/src/app/(dashboard)/my-department/page.tsx +151 -0
- package/src/app/(dashboard)/notifications/notification-list.tsx +103 -0
- package/src/app/(dashboard)/notifications/page.tsx +59 -0
- package/src/app/(dashboard)/profile/page.tsx +27 -0
- package/src/app/(dashboard)/reports/costs/page.tsx +127 -0
- package/src/app/(dashboard)/reports/history/page.tsx +105 -0
- package/src/app/(dashboard)/reports/inventory/page.tsx +116 -0
- package/src/app/(dashboard)/reports/maintenance/page.tsx +136 -0
- package/src/app/(dashboard)/reports/page.tsx +120 -0
- package/src/app/(dashboard)/reports/tickets/page.tsx +139 -0
- package/src/app/(dashboard)/reports/workload/page.tsx +135 -0
- package/src/app/(dashboard)/rounds/[id]/page.tsx +197 -0
- package/src/app/(dashboard)/rounds/history/page.tsx +82 -0
- package/src/app/(dashboard)/rounds/page.tsx +107 -0
- package/src/app/(dashboard)/scan/[code]/page.tsx +154 -0
- package/src/app/(dashboard)/scan/page.tsx +73 -0
- package/src/app/(dashboard)/team/page.tsx +42 -0
- package/src/app/(dashboard)/tickets/[id]/page.tsx +465 -0
- package/src/app/(dashboard)/tickets/create/page.tsx +722 -0
- package/src/app/(dashboard)/tickets/page.tsx +55 -0
- package/src/app/(dashboard)/tickets/triage/page.tsx +161 -0
- package/src/app/api/admin/data/export/route.ts +316 -0
- package/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/src/app/api/health/route.ts +46 -0
- package/src/app/api/notifications/sse/route.ts +91 -0
- package/src/app/api/notifications/subscribe/route.ts +54 -0
- package/src/app/api/notifications/vapid-key/route.ts +7 -0
- package/src/app/api/reports/export/route.ts +102 -0
- package/src/app/api/tickets/[id]/attachments/route.ts +129 -0
- package/src/app/api/tickets/[id]/live-sync/route.ts +52 -0
- package/src/app/error.tsx +54 -0
- package/src/app/globals.css +89 -0
- package/src/app/layout.tsx +59 -0
- package/src/app/not-found.tsx +32 -0
- package/src/app/page.tsx +5 -0
- package/src/components/admin/add-department-user-dialog.tsx +759 -0
- package/src/components/admin/add-engineer-dialog.tsx +773 -0
- package/src/components/admin/add-user-type-dialog.tsx +89 -0
- package/src/components/admin/audit-log-export.tsx +113 -0
- package/src/components/admin/audit-log-filters.tsx +255 -0
- package/src/components/admin/audit-log-table.tsx +383 -0
- package/src/components/admin/data-management/danger-zone.tsx +301 -0
- package/src/components/admin/data-management/data-management-tabs.tsx +61 -0
- package/src/components/admin/data-management/export-center.tsx +301 -0
- package/src/components/admin/data-management/import-wizard.tsx +558 -0
- package/src/components/admin/settings-forms/features-form.tsx +115 -0
- package/src/components/admin/settings-forms/health-panel.tsx +182 -0
- package/src/components/admin/settings-forms/localization-form.tsx +203 -0
- package/src/components/admin/settings-forms/retention-form.tsx +164 -0
- package/src/components/admin/settings-forms/security-form.tsx +186 -0
- package/src/components/admin/system-settings-tabs.tsx +88 -0
- package/src/components/admin/user-role-dialog.tsx +293 -0
- package/src/components/admin/user-status-dialog.tsx +166 -0
- package/src/components/admin/users-page-client.tsx +514 -0
- package/src/components/dashboard/action-buttons.tsx +93 -0
- package/src/components/dashboard/admin-alerts.tsx +86 -0
- package/src/components/dashboard/availability-alerts.tsx +65 -0
- package/src/components/dashboard/maintenance-due-list.tsx +87 -0
- package/src/components/dashboard/round-widget.tsx +110 -0
- package/src/components/dashboard/stat-card.tsx +38 -0
- package/src/components/dashboard/ticket-list-compact.tsx +86 -0
- package/src/components/departments/department-cards-view.tsx +244 -0
- package/src/components/departments/department-detail-view.tsx +467 -0
- package/src/components/departments/department-visuals.tsx +190 -0
- package/src/components/departments/my-department-view.tsx +333 -0
- package/src/components/devices/change-status-dialog.tsx +146 -0
- package/src/components/devices/device-action-buttons.tsx +125 -0
- package/src/components/devices/device-availability-banner.tsx +59 -0
- package/src/components/devices/device-badges.tsx +55 -0
- package/src/components/devices/device-documents-tab.tsx +93 -0
- package/src/components/devices/device-maintenance-tab.tsx +113 -0
- package/src/components/devices/device-mini-qr.tsx +109 -0
- package/src/components/devices/device-qr-card.tsx +187 -0
- package/src/components/devices/device-signatures-tab.tsx +607 -0
- package/src/components/devices/device-tickets-tab.tsx +139 -0
- package/src/components/devices/device-timelines.tsx +124 -0
- package/src/components/devices/qr-camera-scanner.tsx +167 -0
- package/src/components/devices/qr-code-display.tsx +139 -0
- package/src/components/devices/quick-add-category-dialog.tsx +135 -0
- package/src/components/devices/quick-add-manufacturer-dialog.tsx +212 -0
- package/src/components/devices/transfer-device-dialog.tsx +190 -0
- package/src/components/layout/header.tsx +80 -0
- package/src/components/layout/mobile-nav.tsx +91 -0
- package/src/components/layout/more-item-badge.tsx +15 -0
- package/src/components/layout/nav-badge.tsx +72 -0
- package/src/components/layout/sidebar.tsx +297 -0
- package/src/components/maintenance/maintenance-badges.tsx +579 -0
- package/src/components/maintenance/maintenance-nav.tsx +107 -0
- package/src/components/maintenance/parts-costs-manager.tsx +909 -0
- package/src/components/maintenance/signature-card.tsx +413 -0
- package/src/components/maintenance/task-action-bar.tsx +955 -0
- package/src/components/notifications/notification-bell.tsx +367 -0
- package/src/components/notifications/notification-item.tsx +93 -0
- package/src/components/notifications/notification-preferences.tsx +182 -0
- package/src/components/profile/user-profile-view.tsx +626 -0
- package/src/components/providers/nav-counts-provider.tsx +235 -0
- package/src/components/providers/session-provider.tsx +7 -0
- package/src/components/providers/theme-provider.tsx +124 -0
- package/src/components/pwa/install-prompt.tsx +82 -0
- package/src/components/pwa/sw-register.tsx +43 -0
- package/src/components/reports/export-button.tsx +62 -0
- package/src/components/reports/filter-chips.tsx +47 -0
- package/src/components/reports/summary-cards.tsx +40 -0
- package/src/components/reports/transparency-header.tsx +54 -0
- package/src/components/rounds/department-visit-sheet.tsx +199 -0
- package/src/components/rounds/mention-textarea.tsx +187 -0
- package/src/components/rounds/round-calendar.tsx +233 -0
- package/src/components/rounds/round-department-list.tsx +281 -0
- package/src/components/rounds/round-history-client.tsx +218 -0
- package/src/components/rounds/start-round-card.tsx +184 -0
- package/src/components/shared/confirm-dialog.tsx +67 -0
- package/src/components/shared/data-table.tsx +104 -0
- package/src/components/shared/page-header.tsx +20 -0
- package/src/components/shared/sign-out-button.tsx +52 -0
- package/src/components/shared/status-badge.tsx +42 -0
- package/src/components/shared/table-search.tsx +55 -0
- package/src/components/signatures/sign-action-dialog.tsx +253 -0
- package/src/components/signatures/signature-history.tsx +121 -0
- package/src/components/signatures/signature-status-panel.tsx +164 -0
- package/src/components/team/department-coverage.tsx +141 -0
- package/src/components/team/edit-engineer-dialog.tsx +242 -0
- package/src/components/team/engineer-card.tsx +180 -0
- package/src/components/team/engineer-details-dialog.tsx +432 -0
- package/src/components/team/schedule-calendar.tsx +204 -0
- package/src/components/team/staff-engineer-team-view.tsx +368 -0
- package/src/components/team/team-header-stats.tsx +88 -0
- package/src/components/team/team-page-client.tsx +216 -0
- package/src/components/theme/theme-selector-card.tsx +63 -0
- package/src/components/theme/theme-toggle.tsx +65 -0
- package/src/components/tickets/staff-status-label.tsx +58 -0
- package/src/components/tickets/staff-ticket-card.tsx +109 -0
- package/src/components/tickets/staff-ticket-progress-tracker.tsx +405 -0
- package/src/components/tickets/staff-tickets-view.tsx +182 -0
- package/src/components/tickets/ticket-action-buttons.tsx +873 -0
- package/src/components/tickets/ticket-auto-refresh.tsx +197 -0
- package/src/components/tickets/ticket-badges.tsx +43 -0
- package/src/components/tickets/ticket-card.tsx +197 -0
- package/src/components/tickets/ticket-cards-view.tsx +395 -0
- package/src/components/tickets/ticket-comment-section.tsx +684 -0
- package/src/components/tickets/ticket-dispatch-status.tsx +184 -0
- package/src/components/tickets/ticket-list-auto-refresh.tsx +55 -0
- package/src/components/tickets/ticket-reporter-profile.tsx +235 -0
- package/src/components/tickets/ticket-signatures-panel.tsx +32 -0
- package/src/components/tickets/ticket-timeline.tsx +131 -0
- package/src/components/tickets/ticket-triage-panel.tsx +124 -0
- package/src/components/tickets/tickets-nav.tsx +107 -0
- package/src/components/ui/avatar.tsx +43 -0
- package/src/components/ui/badge.tsx +34 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +77 -0
- package/src/components/ui/checkbox.tsx +31 -0
- package/src/components/ui/dialog.tsx +123 -0
- package/src/components/ui/dropdown-menu.tsx +126 -0
- package/src/components/ui/form.tsx +179 -0
- package/src/components/ui/input.tsx +24 -0
- package/src/components/ui/label.tsx +27 -0
- package/src/components/ui/popover.tsx +34 -0
- package/src/components/ui/progress.tsx +36 -0
- package/src/components/ui/scroll-area.tsx +47 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +131 -0
- package/src/components/ui/switch.tsx +30 -0
- package/src/components/ui/table.tsx +121 -0
- package/src/components/ui/tabs.tsx +62 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/tooltip.tsx +27 -0
- package/src/lib/actions/__tests__/devices.test.ts +176 -0
- package/src/lib/actions/__tests__/signatures.test.ts +303 -0
- package/src/lib/actions/__tests__/tickets.test.ts +243 -0
- package/src/lib/actions/audit-logs.ts +298 -0
- package/src/lib/actions/checklists.ts +429 -0
- package/src/lib/actions/dashboard.ts +358 -0
- package/src/lib/actions/data-management.ts +723 -0
- package/src/lib/actions/departments.ts +369 -0
- package/src/lib/actions/device-categories.ts +117 -0
- package/src/lib/actions/devices.ts +748 -0
- package/src/lib/actions/engineer-rounds.ts +973 -0
- package/src/lib/actions/engineers.ts +285 -0
- package/src/lib/actions/hospitals.ts +148 -0
- package/src/lib/actions/locations.ts +115 -0
- package/src/lib/actions/maintenance-plans.ts +316 -0
- package/src/lib/actions/maintenance-tasks.ts +415 -0
- package/src/lib/actions/manufacturers.ts +132 -0
- package/src/lib/actions/nav-counts.ts +144 -0
- package/src/lib/actions/notifications.ts +136 -0
- package/src/lib/actions/organizations.ts +99 -0
- package/src/lib/actions/qr.ts +147 -0
- package/src/lib/actions/report-history.ts +77 -0
- package/src/lib/actions/reports.ts +192 -0
- package/src/lib/actions/roles.ts +32 -0
- package/src/lib/actions/signature-auth.ts +377 -0
- package/src/lib/actions/signatures.ts +532 -0
- package/src/lib/actions/system-settings.ts +274 -0
- package/src/lib/actions/tickets.ts +1445 -0
- package/src/lib/actions/users.ts +740 -0
- package/src/lib/audit/index.ts +103 -0
- package/src/lib/auth/__tests__/rbac.test.ts +33 -0
- package/src/lib/auth/index.ts +338 -0
- package/src/lib/auth/password.ts +10 -0
- package/src/lib/auth/rbac.ts +103 -0
- package/src/lib/auth/types.ts +57 -0
- package/src/lib/constants/departments.ts +16 -0
- package/src/lib/constants/manufacturers.ts +32 -0
- package/src/lib/db/index.ts +23 -0
- package/src/lib/db/schema/audit-logs.ts +42 -0
- package/src/lib/db/schema/departments.ts +39 -0
- package/src/lib/db/schema/device-categories.ts +38 -0
- package/src/lib/db/schema/devices.ts +195 -0
- package/src/lib/db/schema/engineer-profiles.ts +63 -0
- package/src/lib/db/schema/engineer-rounds.ts +115 -0
- package/src/lib/db/schema/enums.ts +148 -0
- package/src/lib/db/schema/hospitals.ts +34 -0
- package/src/lib/db/schema/index.ts +24 -0
- package/src/lib/db/schema/locations.ts +46 -0
- package/src/lib/db/schema/maintenance-plans.ts +99 -0
- package/src/lib/db/schema/maintenance-records.ts +161 -0
- package/src/lib/db/schema/maintenance-tasks.ts +196 -0
- package/src/lib/db/schema/manufacturers.ts +60 -0
- package/src/lib/db/schema/notifications.ts +48 -0
- package/src/lib/db/schema/organizations.ts +24 -0
- package/src/lib/db/schema/report-history.ts +28 -0
- package/src/lib/db/schema/roles.ts +59 -0
- package/src/lib/db/schema/sessions.ts +46 -0
- package/src/lib/db/schema/signature-events.ts +48 -0
- package/src/lib/db/schema/signature-policies.ts +60 -0
- package/src/lib/db/schema/signatures.ts +87 -0
- package/src/lib/db/schema/tickets.ts +169 -0
- package/src/lib/db/schema/user-assignments.ts +85 -0
- package/src/lib/db/schema/users.ts +39 -0
- package/src/lib/db/seed.ts +131 -0
- package/src/lib/logger/__tests__/logger.test.ts +154 -0
- package/src/lib/logger/formatters.ts +159 -0
- package/src/lib/logger/index.ts +184 -0
- package/src/lib/logger/types.ts +48 -0
- package/src/lib/notifications/create.ts +71 -0
- package/src/lib/notifications/dispatcher.ts +490 -0
- package/src/lib/notifications/index.ts +7 -0
- package/src/lib/notifications/push-sender.ts +128 -0
- package/src/lib/notifications/push.ts +64 -0
- package/src/lib/notifications/triggers.ts +166 -0
- package/src/lib/utils/__tests__/crypto.test.ts +30 -0
- package/src/lib/utils/__tests__/export.test.ts +55 -0
- package/src/lib/utils/__tests__/nav-badge.test.ts +147 -0
- package/src/lib/utils/__tests__/staff-status.test.ts +50 -0
- package/src/lib/utils/attestations.ts +19 -0
- package/src/lib/utils/crypto.ts +18 -0
- package/src/lib/utils/csv-parser.ts +181 -0
- package/src/lib/utils/export.ts +45 -0
- package/src/lib/utils/nav-badge.ts +225 -0
- package/src/lib/utils/sound.ts +152 -0
- package/src/lib/utils/staff-status.ts +115 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validators/__tests__/auth.test.ts +127 -0
- package/src/lib/validators/__tests__/data-management.test.ts +182 -0
- package/src/lib/validators/__tests__/devices-profile.test.ts +129 -0
- package/src/lib/validators/__tests__/devices.test.ts +98 -0
- package/src/lib/validators/__tests__/engineer-rounds.test.ts +170 -0
- package/src/lib/validators/__tests__/maintenance.test.ts +178 -0
- package/src/lib/validators/__tests__/signatures.test.ts +154 -0
- package/src/lib/validators/__tests__/system-settings.test.ts +119 -0
- package/src/lib/validators/__tests__/tickets.test.ts +176 -0
- package/src/lib/validators/__tests__/users.test.ts +92 -0
- package/src/lib/validators/auth.ts +32 -0
- package/src/lib/validators/data-management.ts +67 -0
- package/src/lib/validators/departments.ts +14 -0
- package/src/lib/validators/devices.ts +94 -0
- package/src/lib/validators/engineer-rounds.ts +70 -0
- package/src/lib/validators/hospitals.ts +17 -0
- package/src/lib/validators/locations.ts +15 -0
- package/src/lib/validators/maintenance.ts +211 -0
- package/src/lib/validators/organizations.ts +11 -0
- package/src/lib/validators/reports.ts +31 -0
- package/src/lib/validators/signatures.ts +49 -0
- package/src/lib/validators/system-settings.ts +55 -0
- package/src/lib/validators/tickets.ts +125 -0
- package/src/lib/validators/users.ts +43 -0
- package/src/middleware.ts +45 -0
- package/tailwind.config.ts +76 -0
- 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'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
|
+
|