@omersx/bemms 0.0.0-stage → 0.1.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,231 @@
|
|
|
1
|
+
import { listMaintenanceTasks } from "@/lib/actions/maintenance-tasks"
|
|
2
|
+
import { PageHeader } from "@/components/shared/page-header"
|
|
3
|
+
import { DataTable } from "@/components/shared/data-table"
|
|
4
|
+
import { MaintenanceNav } from "@/components/maintenance/maintenance-nav"
|
|
5
|
+
import {
|
|
6
|
+
MaintenanceStatusBadge,
|
|
7
|
+
MaintenanceTypeBadge,
|
|
8
|
+
} from "@/components/maintenance/maintenance-badges"
|
|
9
|
+
import { Button } from "@/components/ui/button"
|
|
10
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
11
|
+
import {
|
|
12
|
+
Activity,
|
|
13
|
+
AlertTriangle,
|
|
14
|
+
Calendar,
|
|
15
|
+
CheckCircle2,
|
|
16
|
+
ClipboardCheck,
|
|
17
|
+
Clock,
|
|
18
|
+
Plus,
|
|
19
|
+
ArrowRight,
|
|
20
|
+
} from "lucide-react"
|
|
21
|
+
import Link from "next/link"
|
|
22
|
+
|
|
23
|
+
export default async function MaintenanceDashboardPage() {
|
|
24
|
+
const result = await listMaintenanceTasks({ page: 1, pageSize: 50 })
|
|
25
|
+
const tasks = result?.success && Array.isArray(result.data) ? result.data : []
|
|
26
|
+
|
|
27
|
+
// Calculate KPI metrics from maintenance tasks
|
|
28
|
+
const inProgressCount = tasks.filter(
|
|
29
|
+
(t: any) => t.statusCode === "in_progress"
|
|
30
|
+
).length
|
|
31
|
+
|
|
32
|
+
const awaitingReviewCount = tasks.filter(
|
|
33
|
+
(t: any) =>
|
|
34
|
+
t.statusCode === "awaiting_review" ||
|
|
35
|
+
t.statusCode === "work_complete" ||
|
|
36
|
+
t.statusCode === "awaiting_release"
|
|
37
|
+
).length
|
|
38
|
+
|
|
39
|
+
const now = new Date()
|
|
40
|
+
const sevenDaysFromNow = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
|
41
|
+
|
|
42
|
+
const dueSoonOrOverdueCount = tasks.filter((t: any) => {
|
|
43
|
+
if (t.statusCode === "closed" || t.statusCode === "cancelled") return false
|
|
44
|
+
if (!t.dueDate) return false
|
|
45
|
+
const due = new Date(t.dueDate)
|
|
46
|
+
return due <= sevenDaysFromNow
|
|
47
|
+
}).length
|
|
48
|
+
|
|
49
|
+
const closedThisMonthCount = tasks.filter((t: any) => {
|
|
50
|
+
if (t.statusCode !== "closed") return false
|
|
51
|
+
const taskDate = t.completedAt ? new Date(t.completedAt) : new Date(t.updatedAt)
|
|
52
|
+
return (
|
|
53
|
+
taskDate.getMonth() === now.getMonth() &&
|
|
54
|
+
taskDate.getFullYear() === now.getFullYear()
|
|
55
|
+
)
|
|
56
|
+
}).length
|
|
57
|
+
|
|
58
|
+
const columns = [
|
|
59
|
+
{
|
|
60
|
+
key: "taskNumber",
|
|
61
|
+
header: "Task #",
|
|
62
|
+
render: (item: any) => (
|
|
63
|
+
<Link
|
|
64
|
+
href={`/maintenance/tasks/${item.id}`}
|
|
65
|
+
className="text-primary hover:underline font-mono text-sm font-medium"
|
|
66
|
+
>
|
|
67
|
+
{item.taskNumber}
|
|
68
|
+
</Link>
|
|
69
|
+
),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "title",
|
|
73
|
+
header: "Title",
|
|
74
|
+
render: (item: any) => (
|
|
75
|
+
<Link
|
|
76
|
+
href={`/maintenance/tasks/${item.id}`}
|
|
77
|
+
className="font-medium hover:underline line-clamp-1"
|
|
78
|
+
>
|
|
79
|
+
{item.title}
|
|
80
|
+
</Link>
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: "device",
|
|
85
|
+
header: "Device",
|
|
86
|
+
render: (item: any) => (
|
|
87
|
+
<div>
|
|
88
|
+
<span className="text-sm font-medium">
|
|
89
|
+
{item.device?.name || "No device"}
|
|
90
|
+
</span>
|
|
91
|
+
{item.device?.assetNumber && (
|
|
92
|
+
<span className="text-xs text-muted-foreground block">
|
|
93
|
+
Asset: {item.device.assetNumber}
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "maintenanceType",
|
|
101
|
+
header: "Type",
|
|
102
|
+
render: (item: any) => (
|
|
103
|
+
<MaintenanceTypeBadge type={item.maintenanceType} />
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
key: "statusCode",
|
|
108
|
+
header: "Status",
|
|
109
|
+
render: (item: any) => (
|
|
110
|
+
<MaintenanceStatusBadge status={item.statusCode} showIcon />
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
key: "assignee",
|
|
115
|
+
header: "Assignee",
|
|
116
|
+
render: (item: any) => (
|
|
117
|
+
<span className="text-sm">
|
|
118
|
+
{item.assignedEngineer?.fullName || "Unassigned"}
|
|
119
|
+
</span>
|
|
120
|
+
),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
key: "dueDate",
|
|
124
|
+
header: "Due Date",
|
|
125
|
+
render: (item: any) => (
|
|
126
|
+
<span className="text-xs text-muted-foreground">
|
|
127
|
+
{item.dueDate ? new Date(item.dueDate).toLocaleDateString() : "—"}
|
|
128
|
+
</span>
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="space-y-6">
|
|
135
|
+
<PageHeader
|
|
136
|
+
title="Maintenance"
|
|
137
|
+
description="Manage maintenance tasks, PM schedules, and work orders"
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<MaintenanceNav />
|
|
141
|
+
|
|
142
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
143
|
+
<Card>
|
|
144
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
145
|
+
<CardTitle className="text-sm font-medium">
|
|
146
|
+
Tasks In Progress
|
|
147
|
+
</CardTitle>
|
|
148
|
+
<Activity className="h-4 w-4 text-indigo-500" />
|
|
149
|
+
</CardHeader>
|
|
150
|
+
<CardContent>
|
|
151
|
+
<div className="text-2xl font-bold">{inProgressCount}</div>
|
|
152
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
153
|
+
Active maintenance work
|
|
154
|
+
</p>
|
|
155
|
+
</CardContent>
|
|
156
|
+
</Card>
|
|
157
|
+
|
|
158
|
+
<Card>
|
|
159
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
160
|
+
<CardTitle className="text-sm font-medium">
|
|
161
|
+
Awaiting Review
|
|
162
|
+
</CardTitle>
|
|
163
|
+
<Clock className="h-4 w-4 text-purple-500" />
|
|
164
|
+
</CardHeader>
|
|
165
|
+
<CardContent>
|
|
166
|
+
<div className="text-2xl font-bold">{awaitingReviewCount}</div>
|
|
167
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
168
|
+
Pending review / sign-off
|
|
169
|
+
</p>
|
|
170
|
+
</CardContent>
|
|
171
|
+
</Card>
|
|
172
|
+
|
|
173
|
+
<Card>
|
|
174
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
175
|
+
<CardTitle className="text-sm font-medium">
|
|
176
|
+
Due Soon / Overdue
|
|
177
|
+
</CardTitle>
|
|
178
|
+
<AlertTriangle className="h-4 w-4 text-amber-500" />
|
|
179
|
+
</CardHeader>
|
|
180
|
+
<CardContent>
|
|
181
|
+
<div className="text-2xl font-bold">{dueSoonOrOverdueCount}</div>
|
|
182
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
183
|
+
Due within 7 days or past due
|
|
184
|
+
</p>
|
|
185
|
+
</CardContent>
|
|
186
|
+
</Card>
|
|
187
|
+
|
|
188
|
+
<Card>
|
|
189
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
190
|
+
<CardTitle className="text-sm font-medium">
|
|
191
|
+
Closed This Month
|
|
192
|
+
</CardTitle>
|
|
193
|
+
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
|
|
194
|
+
</CardHeader>
|
|
195
|
+
<CardContent>
|
|
196
|
+
<div className="text-2xl font-bold">{closedThisMonthCount}</div>
|
|
197
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
198
|
+
Completed work orders
|
|
199
|
+
</p>
|
|
200
|
+
</CardContent>
|
|
201
|
+
</Card>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div className="space-y-4">
|
|
205
|
+
<div className="flex items-center justify-between">
|
|
206
|
+
<div>
|
|
207
|
+
<h2 className="text-lg font-semibold tracking-tight">Recent Tasks</h2>
|
|
208
|
+
<p className="text-sm text-muted-foreground">
|
|
209
|
+
Latest maintenance requests and scheduled work
|
|
210
|
+
</p>
|
|
211
|
+
</div>
|
|
212
|
+
<Link href="/maintenance/tasks">
|
|
213
|
+
<Button variant="ghost" size="sm">
|
|
214
|
+
View All Tasks
|
|
215
|
+
<ArrowRight className="w-4 h-4 ml-1" />
|
|
216
|
+
</Button>
|
|
217
|
+
</Link>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<DataTable
|
|
221
|
+
data={tasks.slice(0, 10)}
|
|
222
|
+
columns={columns}
|
|
223
|
+
searchable={true}
|
|
224
|
+
searchKey="title"
|
|
225
|
+
searchPlaceholder="Search maintenance tasks..."
|
|
226
|
+
/>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { PageHeader } from "@/components/shared/page-header"
|
|
2
|
+
import { MaintenanceNav } from "@/components/maintenance/maintenance-nav"
|
|
3
|
+
import {
|
|
4
|
+
MaintenancePlanStatusBadge,
|
|
5
|
+
DueStateBadge,
|
|
6
|
+
} from "@/components/maintenance/maintenance-badges"
|
|
7
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
8
|
+
import { Button } from "@/components/ui/button"
|
|
9
|
+
import {
|
|
10
|
+
Calendar,
|
|
11
|
+
Clock,
|
|
12
|
+
Info,
|
|
13
|
+
Monitor,
|
|
14
|
+
Plus,
|
|
15
|
+
RotateCw,
|
|
16
|
+
User,
|
|
17
|
+
Wrench,
|
|
18
|
+
} from "lucide-react"
|
|
19
|
+
import Link from "next/link"
|
|
20
|
+
|
|
21
|
+
export default async function MaintenancePlansPage() {
|
|
22
|
+
// Sample structure demonstration for PM plans until listMaintenancePlans with device joins is available
|
|
23
|
+
const samplePlans = [
|
|
24
|
+
{
|
|
25
|
+
id: "pm-plan-1",
|
|
26
|
+
title: "Quarterly Defibrillator Safety & Calibration",
|
|
27
|
+
deviceName: "Zoll R Series Defibrillator",
|
|
28
|
+
deviceAsset: "ASSET-2024-001",
|
|
29
|
+
department: "Emergency Department",
|
|
30
|
+
frequency: "Every 3 Months",
|
|
31
|
+
nextDueDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toLocaleDateString(),
|
|
32
|
+
status: "active",
|
|
33
|
+
dueState: "scheduled",
|
|
34
|
+
assignedEngineer: "Alex Taylor (Lead Biomed)",
|
|
35
|
+
calculationMethod: "fixed_calendar",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "pm-plan-2",
|
|
39
|
+
title: "Semi-Annual Anesthesia Machine PM & Vaporizer Check",
|
|
40
|
+
deviceName: "Mindray A7 Anesthesia System",
|
|
41
|
+
deviceAsset: "ASSET-2024-042",
|
|
42
|
+
department: "Surgical Suite 3",
|
|
43
|
+
frequency: "Every 6 Months",
|
|
44
|
+
nextDueDate: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toLocaleDateString(),
|
|
45
|
+
status: "active",
|
|
46
|
+
dueState: "due_soon",
|
|
47
|
+
assignedEngineer: "Sarah Jenkins (Senior CE)",
|
|
48
|
+
calculationMethod: "completion_based",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "pm-plan-3",
|
|
52
|
+
title: "Annual Infusion Pump Electrical Safety Testing",
|
|
53
|
+
deviceName: "Alaris 8100 Infusion Pump",
|
|
54
|
+
deviceAsset: "ASSET-2024-108",
|
|
55
|
+
department: "ICU Ward B",
|
|
56
|
+
frequency: "Every 12 Months",
|
|
57
|
+
nextDueDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toLocaleDateString(),
|
|
58
|
+
status: "active",
|
|
59
|
+
dueState: "overdue",
|
|
60
|
+
assignedEngineer: "David Chen (Biomed Tech II)",
|
|
61
|
+
calculationMethod: "fixed_calendar",
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className="space-y-6">
|
|
67
|
+
<PageHeader
|
|
68
|
+
title="Preventive Maintenance Plans"
|
|
69
|
+
description="Configure automated recurring PM schedules, calibration cycles, and maintenance routines"
|
|
70
|
+
>
|
|
71
|
+
<Link href="/maintenance/plans/new">
|
|
72
|
+
<Button>
|
|
73
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
74
|
+
New Plan
|
|
75
|
+
</Button>
|
|
76
|
+
</Link>
|
|
77
|
+
</PageHeader>
|
|
78
|
+
|
|
79
|
+
<MaintenanceNav />
|
|
80
|
+
|
|
81
|
+
<Card className="border-blue-200 bg-blue-50/50 dark:border-blue-900/50 dark:bg-blue-950/20">
|
|
82
|
+
<CardContent className="flex items-start gap-4 p-4">
|
|
83
|
+
<Info className="h-5 w-5 text-blue-600 dark:text-blue-400 mt-0.5 shrink-0" />
|
|
84
|
+
<div className="space-y-1">
|
|
85
|
+
<h3 className="text-sm font-semibold text-blue-900 dark:text-blue-200">
|
|
86
|
+
Database Synchronization Active
|
|
87
|
+
</h3>
|
|
88
|
+
<p className="text-xs text-blue-800 dark:text-blue-300">
|
|
89
|
+
PM Plans will be loaded from the database. Automated background schedules trigger task creation according to specified lead time days and recurrence calculations.
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</CardContent>
|
|
93
|
+
</Card>
|
|
94
|
+
|
|
95
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
96
|
+
{samplePlans.map((plan) => (
|
|
97
|
+
<Card key={plan.id} className="flex flex-col justify-between hover:shadow-md transition-shadow">
|
|
98
|
+
<CardHeader className="pb-3">
|
|
99
|
+
<div className="flex items-start justify-between gap-2">
|
|
100
|
+
<CardTitle className="text-base font-semibold line-clamp-2">
|
|
101
|
+
{plan.title}
|
|
102
|
+
</CardTitle>
|
|
103
|
+
<MaintenancePlanStatusBadge status={plan.status} />
|
|
104
|
+
</div>
|
|
105
|
+
<CardDescription className="flex items-center gap-1.5 text-xs">
|
|
106
|
+
<Monitor className="w-3.5 h-3.5" />
|
|
107
|
+
<span>{plan.deviceName}</span>
|
|
108
|
+
<span className="text-muted-foreground font-mono">({plan.deviceAsset})</span>
|
|
109
|
+
</CardDescription>
|
|
110
|
+
</CardHeader>
|
|
111
|
+
|
|
112
|
+
<CardContent className="space-y-3 text-sm flex-1">
|
|
113
|
+
<div className="grid grid-cols-2 gap-2 text-xs pt-2 border-t border-border">
|
|
114
|
+
<div className="space-y-1">
|
|
115
|
+
<span className="text-muted-foreground flex items-center gap-1">
|
|
116
|
+
<RotateCw className="w-3 h-3" /> Frequency
|
|
117
|
+
</span>
|
|
118
|
+
<span className="font-medium text-foreground">{plan.frequency}</span>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="space-y-1">
|
|
121
|
+
<span className="text-muted-foreground flex items-center gap-1">
|
|
122
|
+
<Calendar className="w-3 h-3" /> Next Due Date
|
|
123
|
+
</span>
|
|
124
|
+
<span className="font-medium text-foreground">{plan.nextDueDate}</span>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="flex items-center justify-between pt-2 border-t border-border text-xs">
|
|
129
|
+
<div className="flex items-center gap-1 text-muted-foreground">
|
|
130
|
+
<User className="w-3.5 h-3.5" />
|
|
131
|
+
<span className="truncate max-w-[140px]">{plan.assignedEngineer}</span>
|
|
132
|
+
</div>
|
|
133
|
+
<DueStateBadge dueState={plan.dueState} showIcon />
|
|
134
|
+
</div>
|
|
135
|
+
</CardContent>
|
|
136
|
+
</Card>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|