@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,579 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Badge } from "@/components/ui/badge"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
import {
|
|
7
|
+
Shield,
|
|
8
|
+
Wrench,
|
|
9
|
+
Search,
|
|
10
|
+
Eye,
|
|
11
|
+
Ruler,
|
|
12
|
+
Zap,
|
|
13
|
+
Activity,
|
|
14
|
+
PackagePlus,
|
|
15
|
+
Sliders,
|
|
16
|
+
Archive,
|
|
17
|
+
Ban,
|
|
18
|
+
Calendar,
|
|
19
|
+
Clock,
|
|
20
|
+
AlertCircle,
|
|
21
|
+
AlertTriangle,
|
|
22
|
+
CheckCircle2,
|
|
23
|
+
XCircle,
|
|
24
|
+
MinusCircle,
|
|
25
|
+
ExternalLink,
|
|
26
|
+
FileEdit,
|
|
27
|
+
UserCheck,
|
|
28
|
+
RotateCcw,
|
|
29
|
+
CheckCheck,
|
|
30
|
+
HelpCircle,
|
|
31
|
+
Minus,
|
|
32
|
+
ArrowUp,
|
|
33
|
+
ArrowDown,
|
|
34
|
+
Sparkles,
|
|
35
|
+
} from "lucide-react"
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Format string with underscore to capitalized readable words
|
|
39
|
+
*/
|
|
40
|
+
function formatLabel(value?: string | null): string {
|
|
41
|
+
if (!value) return "Unknown"
|
|
42
|
+
return value
|
|
43
|
+
.replace(/_/g, " ")
|
|
44
|
+
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// 1. MaintenanceStatusBadge
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
interface MaintenanceStatusConfig {
|
|
51
|
+
label: string
|
|
52
|
+
className: string
|
|
53
|
+
icon: React.ComponentType<{ className?: string }>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const maintenanceStatusConfig: Record<string, MaintenanceStatusConfig> = {
|
|
57
|
+
draft: {
|
|
58
|
+
label: "Draft",
|
|
59
|
+
className: "bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-700",
|
|
60
|
+
icon: FileEdit,
|
|
61
|
+
},
|
|
62
|
+
assigned: {
|
|
63
|
+
label: "Assigned",
|
|
64
|
+
className: "bg-blue-100 text-blue-800 border-blue-300 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
65
|
+
icon: UserCheck,
|
|
66
|
+
},
|
|
67
|
+
in_progress: {
|
|
68
|
+
label: "In Progress",
|
|
69
|
+
className: "bg-indigo-100 text-indigo-800 border-indigo-300 dark:bg-indigo-950 dark:text-indigo-300 dark:border-indigo-800",
|
|
70
|
+
icon: Activity,
|
|
71
|
+
},
|
|
72
|
+
waiting_dependency: {
|
|
73
|
+
label: "Waiting Dependency",
|
|
74
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
75
|
+
icon: Clock,
|
|
76
|
+
},
|
|
77
|
+
work_complete: {
|
|
78
|
+
label: "Work Complete",
|
|
79
|
+
className: "bg-cyan-100 text-cyan-800 border-cyan-300 dark:bg-cyan-950 dark:text-cyan-300 dark:border-cyan-800",
|
|
80
|
+
icon: CheckCheck,
|
|
81
|
+
},
|
|
82
|
+
awaiting_review: {
|
|
83
|
+
label: "Awaiting Review",
|
|
84
|
+
className: "bg-purple-100 text-purple-800 border-purple-300 dark:bg-purple-950 dark:text-purple-300 dark:border-purple-800",
|
|
85
|
+
icon: Eye,
|
|
86
|
+
},
|
|
87
|
+
returned_for_rework: {
|
|
88
|
+
label: "Returned for Rework",
|
|
89
|
+
className: "bg-orange-100 text-orange-800 border-orange-300 dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",
|
|
90
|
+
icon: RotateCcw,
|
|
91
|
+
},
|
|
92
|
+
awaiting_release: {
|
|
93
|
+
label: "Awaiting Release",
|
|
94
|
+
className: "bg-violet-100 text-violet-800 border-violet-300 dark:bg-violet-950 dark:text-violet-300 dark:border-violet-800",
|
|
95
|
+
icon: Sparkles,
|
|
96
|
+
},
|
|
97
|
+
closed: {
|
|
98
|
+
label: "Closed",
|
|
99
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
100
|
+
icon: CheckCircle2,
|
|
101
|
+
},
|
|
102
|
+
cancelled: {
|
|
103
|
+
label: "Cancelled",
|
|
104
|
+
className: "bg-rose-100 text-rose-800 border-rose-300 line-through dark:bg-rose-950 dark:text-rose-300 dark:border-rose-800",
|
|
105
|
+
icon: XCircle,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface MaintenanceStatusBadgeProps {
|
|
110
|
+
status?: string | null
|
|
111
|
+
className?: string
|
|
112
|
+
showIcon?: boolean
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function MaintenanceStatusBadge({
|
|
116
|
+
status,
|
|
117
|
+
className,
|
|
118
|
+
showIcon = false,
|
|
119
|
+
}: MaintenanceStatusBadgeProps) {
|
|
120
|
+
const normalized = status ? status.toLowerCase() : ""
|
|
121
|
+
const config = maintenanceStatusConfig[normalized] || {
|
|
122
|
+
label: formatLabel(status),
|
|
123
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
124
|
+
icon: HelpCircle,
|
|
125
|
+
}
|
|
126
|
+
const Icon = config.icon
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Badge
|
|
130
|
+
variant="outline"
|
|
131
|
+
className={cn("inline-flex items-center gap-1.5 font-medium text-xs", config.className, className)}
|
|
132
|
+
>
|
|
133
|
+
{showIcon && <Icon className="h-3 w-3 shrink-0" />}
|
|
134
|
+
<span>{config.label}</span>
|
|
135
|
+
</Badge>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// 2. MaintenanceTypeBadge
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
interface MaintenanceTypeConfig {
|
|
143
|
+
label: string
|
|
144
|
+
className: string
|
|
145
|
+
icon: React.ComponentType<{ className?: string }>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const maintenanceTypeConfig: Record<string, MaintenanceTypeConfig> = {
|
|
149
|
+
preventive_maintenance: {
|
|
150
|
+
label: "Preventive Maintenance",
|
|
151
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
152
|
+
icon: Shield,
|
|
153
|
+
},
|
|
154
|
+
corrective_maintenance: {
|
|
155
|
+
label: "Corrective Maintenance",
|
|
156
|
+
className: "bg-orange-100 text-orange-800 border-orange-300 dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",
|
|
157
|
+
icon: Wrench,
|
|
158
|
+
},
|
|
159
|
+
troubleshooting: {
|
|
160
|
+
label: "Troubleshooting",
|
|
161
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
162
|
+
icon: Search,
|
|
163
|
+
},
|
|
164
|
+
inspection: {
|
|
165
|
+
label: "Inspection",
|
|
166
|
+
className: "bg-blue-100 text-blue-800 border-blue-300 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
167
|
+
icon: Eye,
|
|
168
|
+
},
|
|
169
|
+
calibration: {
|
|
170
|
+
label: "Calibration",
|
|
171
|
+
className: "bg-purple-100 text-purple-800 border-purple-300 dark:bg-purple-950 dark:text-purple-300 dark:border-purple-800",
|
|
172
|
+
icon: Ruler,
|
|
173
|
+
},
|
|
174
|
+
electrical_safety_testing: {
|
|
175
|
+
label: "Electrical Safety Testing",
|
|
176
|
+
className: "bg-yellow-100 text-yellow-800 border-yellow-300 dark:bg-yellow-950 dark:text-yellow-300 dark:border-yellow-800",
|
|
177
|
+
icon: Zap,
|
|
178
|
+
},
|
|
179
|
+
performance_testing: {
|
|
180
|
+
label: "Performance Testing",
|
|
181
|
+
className: "bg-teal-100 text-teal-800 border-teal-300 dark:bg-teal-950 dark:text-teal-300 dark:border-teal-800",
|
|
182
|
+
icon: Activity,
|
|
183
|
+
},
|
|
184
|
+
installation_commissioning: {
|
|
185
|
+
label: "Installation / Commissioning",
|
|
186
|
+
className: "bg-sky-100 text-sky-800 border-sky-300 dark:bg-sky-950 dark:text-sky-300 dark:border-sky-800",
|
|
187
|
+
icon: PackagePlus,
|
|
188
|
+
},
|
|
189
|
+
software_configuration: {
|
|
190
|
+
label: "Software Configuration",
|
|
191
|
+
className: "bg-indigo-100 text-indigo-800 border-indigo-300 dark:bg-indigo-950 dark:text-indigo-300 dark:border-indigo-800",
|
|
192
|
+
icon: Sliders,
|
|
193
|
+
},
|
|
194
|
+
decommissioning: {
|
|
195
|
+
label: "Decommissioning",
|
|
196
|
+
className: "bg-rose-100 text-rose-800 border-rose-300 dark:bg-rose-950 dark:text-rose-300 dark:border-rose-800",
|
|
197
|
+
icon: Ban,
|
|
198
|
+
},
|
|
199
|
+
other: {
|
|
200
|
+
label: "Other Maintenance",
|
|
201
|
+
className: "bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-700",
|
|
202
|
+
icon: Wrench,
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface MaintenanceTypeBadgeProps {
|
|
207
|
+
type?: string | null
|
|
208
|
+
className?: string
|
|
209
|
+
showIcon?: boolean
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function MaintenanceTypeBadge({
|
|
213
|
+
type,
|
|
214
|
+
className,
|
|
215
|
+
showIcon = true,
|
|
216
|
+
}: MaintenanceTypeBadgeProps) {
|
|
217
|
+
const normalized = type ? type.toLowerCase() : ""
|
|
218
|
+
const config = maintenanceTypeConfig[normalized] || {
|
|
219
|
+
label: formatLabel(type),
|
|
220
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
221
|
+
icon: Wrench,
|
|
222
|
+
}
|
|
223
|
+
const Icon = config.icon
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<Badge
|
|
227
|
+
variant="outline"
|
|
228
|
+
className={cn("inline-flex items-center gap-1.5 font-medium text-xs", config.className, className)}
|
|
229
|
+
>
|
|
230
|
+
{showIcon && <Icon className="h-3 w-3 shrink-0" />}
|
|
231
|
+
<span>{config.label}</span>
|
|
232
|
+
</Badge>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// 3. DueStateBadge
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
interface DueStateConfig {
|
|
240
|
+
label: string
|
|
241
|
+
className: string
|
|
242
|
+
icon: React.ComponentType<{ className?: string }>
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const dueStateConfig: Record<string, DueStateConfig> = {
|
|
246
|
+
scheduled: {
|
|
247
|
+
label: "Scheduled",
|
|
248
|
+
className: "bg-blue-100 text-blue-800 border-blue-300 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
249
|
+
icon: Calendar,
|
|
250
|
+
},
|
|
251
|
+
due_soon: {
|
|
252
|
+
label: "Due Soon",
|
|
253
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
254
|
+
icon: Clock,
|
|
255
|
+
},
|
|
256
|
+
due_today: {
|
|
257
|
+
label: "Due Today",
|
|
258
|
+
className: "bg-orange-100 text-orange-800 border-orange-300 font-semibold dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",
|
|
259
|
+
icon: AlertCircle,
|
|
260
|
+
},
|
|
261
|
+
overdue: {
|
|
262
|
+
label: "Overdue",
|
|
263
|
+
className: "bg-red-100 text-red-800 border-red-400 animate-pulse font-bold dark:bg-red-950 dark:text-red-300 dark:border-red-800",
|
|
264
|
+
icon: AlertTriangle,
|
|
265
|
+
},
|
|
266
|
+
completed: {
|
|
267
|
+
label: "Completed",
|
|
268
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
269
|
+
icon: CheckCircle2,
|
|
270
|
+
},
|
|
271
|
+
deferred: {
|
|
272
|
+
label: "Deferred",
|
|
273
|
+
className: "bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-700",
|
|
274
|
+
icon: Clock,
|
|
275
|
+
},
|
|
276
|
+
cancelled: {
|
|
277
|
+
label: "Cancelled",
|
|
278
|
+
className: "bg-gray-100 text-gray-500 border-gray-300 line-through dark:bg-gray-800 dark:text-gray-400 dark:border-gray-700",
|
|
279
|
+
icon: XCircle,
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface DueStateBadgeProps {
|
|
284
|
+
state?: string | null
|
|
285
|
+
dueState?: string | null
|
|
286
|
+
className?: string
|
|
287
|
+
showIcon?: boolean
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function DueStateBadge({
|
|
291
|
+
state,
|
|
292
|
+
dueState,
|
|
293
|
+
className,
|
|
294
|
+
showIcon = true,
|
|
295
|
+
}: DueStateBadgeProps) {
|
|
296
|
+
const target = state || dueState
|
|
297
|
+
const normalized = target ? target.toLowerCase() : ""
|
|
298
|
+
const config = dueStateConfig[normalized] || {
|
|
299
|
+
label: formatLabel(target),
|
|
300
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
301
|
+
icon: Calendar,
|
|
302
|
+
}
|
|
303
|
+
const Icon = config.icon
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<Badge
|
|
307
|
+
variant="outline"
|
|
308
|
+
className={cn("inline-flex items-center gap-1.5 font-medium text-xs", config.className, className)}
|
|
309
|
+
>
|
|
310
|
+
{showIcon && <Icon className="h-3 w-3 shrink-0" />}
|
|
311
|
+
<span>{config.label}</span>
|
|
312
|
+
</Badge>
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
// 4. PriorityBadge
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
interface PriorityConfig {
|
|
320
|
+
label: string
|
|
321
|
+
className: string
|
|
322
|
+
icon?: React.ComponentType<{ className?: string }>
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const priorityConfig: Record<string, PriorityConfig> = {
|
|
326
|
+
p1_critical: {
|
|
327
|
+
label: "P1 Critical",
|
|
328
|
+
className: "bg-red-100 text-red-800 border-red-300 font-bold dark:bg-red-950 dark:text-red-300 dark:border-red-800",
|
|
329
|
+
icon: AlertTriangle,
|
|
330
|
+
},
|
|
331
|
+
critical: {
|
|
332
|
+
label: "Critical",
|
|
333
|
+
className: "bg-red-100 text-red-800 border-red-300 font-bold dark:bg-red-950 dark:text-red-300 dark:border-red-800",
|
|
334
|
+
icon: AlertTriangle,
|
|
335
|
+
},
|
|
336
|
+
p2_high: {
|
|
337
|
+
label: "P2 High",
|
|
338
|
+
className: "bg-orange-100 text-orange-800 border-orange-300 font-semibold dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",
|
|
339
|
+
icon: ArrowUp,
|
|
340
|
+
},
|
|
341
|
+
high: {
|
|
342
|
+
label: "High",
|
|
343
|
+
className: "bg-orange-100 text-orange-800 border-orange-300 font-semibold dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800",
|
|
344
|
+
icon: ArrowUp,
|
|
345
|
+
},
|
|
346
|
+
p3_normal: {
|
|
347
|
+
label: "P3 Normal",
|
|
348
|
+
className: "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
349
|
+
icon: Minus,
|
|
350
|
+
},
|
|
351
|
+
medium: {
|
|
352
|
+
label: "Medium",
|
|
353
|
+
className: "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
354
|
+
icon: Minus,
|
|
355
|
+
},
|
|
356
|
+
normal: {
|
|
357
|
+
label: "Normal",
|
|
358
|
+
className: "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800",
|
|
359
|
+
icon: Minus,
|
|
360
|
+
},
|
|
361
|
+
p4_low: {
|
|
362
|
+
label: "P4 Low",
|
|
363
|
+
className: "bg-gray-50 text-gray-600 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700",
|
|
364
|
+
icon: ArrowDown,
|
|
365
|
+
},
|
|
366
|
+
low: {
|
|
367
|
+
label: "Low",
|
|
368
|
+
className: "bg-gray-50 text-gray-600 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700",
|
|
369
|
+
icon: ArrowDown,
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface PriorityBadgeProps {
|
|
374
|
+
priority?: string | null
|
|
375
|
+
level?: string | null
|
|
376
|
+
className?: string
|
|
377
|
+
showIcon?: boolean
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function PriorityBadge({
|
|
381
|
+
priority,
|
|
382
|
+
level,
|
|
383
|
+
className,
|
|
384
|
+
showIcon = false,
|
|
385
|
+
}: PriorityBadgeProps) {
|
|
386
|
+
const target = priority || level
|
|
387
|
+
const normalized = target ? target.toLowerCase() : ""
|
|
388
|
+
const config = priorityConfig[normalized] || {
|
|
389
|
+
label: formatLabel(target),
|
|
390
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
391
|
+
}
|
|
392
|
+
const Icon = config.icon
|
|
393
|
+
|
|
394
|
+
return (
|
|
395
|
+
<Badge
|
|
396
|
+
variant="outline"
|
|
397
|
+
className={cn("inline-flex items-center gap-1 font-medium text-xs", config.className, className)}
|
|
398
|
+
>
|
|
399
|
+
{showIcon && Icon && <Icon className="h-3 w-3 shrink-0" />}
|
|
400
|
+
<span>{config.label}</span>
|
|
401
|
+
</Badge>
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// 5. ChecklistResultBadge
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
interface ChecklistResultConfig {
|
|
409
|
+
label: string
|
|
410
|
+
className: string
|
|
411
|
+
icon: React.ComponentType<{ className?: string }>
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const checklistResultConfig: Record<string, ChecklistResultConfig> = {
|
|
415
|
+
passed: {
|
|
416
|
+
label: "Passed",
|
|
417
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
418
|
+
icon: CheckCircle2,
|
|
419
|
+
},
|
|
420
|
+
failed: {
|
|
421
|
+
label: "Failed",
|
|
422
|
+
className: "bg-red-100 text-red-800 border-red-300 font-semibold dark:bg-red-950 dark:text-red-300 dark:border-red-800",
|
|
423
|
+
icon: XCircle,
|
|
424
|
+
},
|
|
425
|
+
not_applicable: {
|
|
426
|
+
label: "Not Applicable",
|
|
427
|
+
className: "bg-gray-100 text-gray-600 border-gray-300 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700",
|
|
428
|
+
icon: MinusCircle,
|
|
429
|
+
},
|
|
430
|
+
requires_follow_up: {
|
|
431
|
+
label: "Requires Follow-up",
|
|
432
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 font-semibold dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
433
|
+
icon: AlertTriangle,
|
|
434
|
+
},
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export interface ChecklistResultBadgeProps {
|
|
438
|
+
result?: string | null
|
|
439
|
+
status?: string | null
|
|
440
|
+
className?: string
|
|
441
|
+
showIcon?: boolean
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function ChecklistResultBadge({
|
|
445
|
+
result,
|
|
446
|
+
status,
|
|
447
|
+
className,
|
|
448
|
+
showIcon = true,
|
|
449
|
+
}: ChecklistResultBadgeProps) {
|
|
450
|
+
const target = result || status
|
|
451
|
+
const normalized = target ? target.toLowerCase() : ""
|
|
452
|
+
const config = checklistResultConfig[normalized] || {
|
|
453
|
+
label: formatLabel(target),
|
|
454
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
455
|
+
icon: HelpCircle,
|
|
456
|
+
}
|
|
457
|
+
const Icon = config.icon
|
|
458
|
+
|
|
459
|
+
return (
|
|
460
|
+
<Badge
|
|
461
|
+
variant="outline"
|
|
462
|
+
className={cn("inline-flex items-center gap-1.5 font-medium text-xs", config.className, className)}
|
|
463
|
+
>
|
|
464
|
+
{showIcon && <Icon className="h-3 w-3 shrink-0" />}
|
|
465
|
+
<span>{config.label}</span>
|
|
466
|
+
</Badge>
|
|
467
|
+
)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ---------------------------------------------------------------------------
|
|
471
|
+
// 6. FinalResultBadge
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
interface FinalResultConfig {
|
|
474
|
+
label: string
|
|
475
|
+
className: string
|
|
476
|
+
icon: React.ComponentType<{ className?: string }>
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const finalResultConfig: Record<string, FinalResultConfig> = {
|
|
480
|
+
passed: {
|
|
481
|
+
label: "Passed",
|
|
482
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
483
|
+
icon: CheckCircle2,
|
|
484
|
+
},
|
|
485
|
+
passed_with_limitations: {
|
|
486
|
+
label: "Passed with Limitations",
|
|
487
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 font-medium dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
488
|
+
icon: AlertCircle,
|
|
489
|
+
},
|
|
490
|
+
failed: {
|
|
491
|
+
label: "Failed",
|
|
492
|
+
className: "bg-red-100 text-red-800 border-red-300 font-semibold dark:bg-red-950 dark:text-red-300 dark:border-red-800",
|
|
493
|
+
icon: XCircle,
|
|
494
|
+
},
|
|
495
|
+
no_fault_found: {
|
|
496
|
+
label: "No Fault Found",
|
|
497
|
+
className: "bg-sky-100 text-sky-800 border-sky-300 dark:bg-sky-950 dark:text-sky-300 dark:border-sky-800",
|
|
498
|
+
icon: Search,
|
|
499
|
+
},
|
|
500
|
+
decommission_recommended: {
|
|
501
|
+
label: "Decommission Recommended",
|
|
502
|
+
className: "bg-rose-100 text-rose-800 border-rose-300 font-semibold dark:bg-rose-950 dark:text-rose-300 dark:border-rose-800",
|
|
503
|
+
icon: Ban,
|
|
504
|
+
},
|
|
505
|
+
requires_external_service: {
|
|
506
|
+
label: "Requires External Service",
|
|
507
|
+
className: "bg-indigo-100 text-indigo-800 border-indigo-300 dark:bg-indigo-950 dark:text-indigo-300 dark:border-indigo-800",
|
|
508
|
+
icon: ExternalLink,
|
|
509
|
+
},
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export interface FinalResultBadgeProps {
|
|
513
|
+
result?: string | null
|
|
514
|
+
className?: string
|
|
515
|
+
showIcon?: boolean
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export function FinalResultBadge({
|
|
519
|
+
result,
|
|
520
|
+
className,
|
|
521
|
+
showIcon = true,
|
|
522
|
+
}: FinalResultBadgeProps) {
|
|
523
|
+
const normalized = result ? result.toLowerCase() : ""
|
|
524
|
+
const config = finalResultConfig[normalized] || {
|
|
525
|
+
label: formatLabel(result),
|
|
526
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
527
|
+
icon: HelpCircle,
|
|
528
|
+
}
|
|
529
|
+
const Icon = config.icon
|
|
530
|
+
|
|
531
|
+
return (
|
|
532
|
+
<Badge
|
|
533
|
+
variant="outline"
|
|
534
|
+
className={cn("inline-flex items-center gap-1.5 font-medium text-xs", config.className, className)}
|
|
535
|
+
>
|
|
536
|
+
{showIcon && <Icon className="h-3 w-3 shrink-0" />}
|
|
537
|
+
<span>{config.label}</span>
|
|
538
|
+
</Badge>
|
|
539
|
+
)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ---------------------------------------------------------------------------
|
|
543
|
+
// 7. MaintenancePlanStatusBadge (Helper)
|
|
544
|
+
// ---------------------------------------------------------------------------
|
|
545
|
+
const planStatusConfig: Record<string, { label: string; className: string }> = {
|
|
546
|
+
active: {
|
|
547
|
+
label: "Active",
|
|
548
|
+
className: "bg-emerald-100 text-emerald-800 border-emerald-300 dark:bg-emerald-950 dark:text-emerald-300 dark:border-emerald-800",
|
|
549
|
+
},
|
|
550
|
+
paused: {
|
|
551
|
+
label: "Paused",
|
|
552
|
+
className: "bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-800",
|
|
553
|
+
},
|
|
554
|
+
archived: {
|
|
555
|
+
label: "Archived",
|
|
556
|
+
className: "bg-slate-100 text-slate-700 border-slate-300 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-700",
|
|
557
|
+
},
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export function MaintenancePlanStatusBadge({
|
|
561
|
+
status,
|
|
562
|
+
className,
|
|
563
|
+
}: {
|
|
564
|
+
status?: string | null
|
|
565
|
+
className?: string
|
|
566
|
+
}) {
|
|
567
|
+
const normalized = status ? status.toLowerCase() : ""
|
|
568
|
+
const config = planStatusConfig[normalized] || {
|
|
569
|
+
label: formatLabel(status),
|
|
570
|
+
className: "bg-muted text-muted-foreground border-border",
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return (
|
|
574
|
+
<Badge variant="outline" className={cn("font-medium text-xs", config.className, className)}>
|
|
575
|
+
{config.label}
|
|
576
|
+
</Badge>
|
|
577
|
+
)
|
|
578
|
+
}
|
|
579
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Link from "next/link"
|
|
4
|
+
import { usePathname } from "next/navigation"
|
|
5
|
+
import {
|
|
6
|
+
Wrench,
|
|
7
|
+
ClipboardList,
|
|
8
|
+
Calendar,
|
|
9
|
+
CheckSquare,
|
|
10
|
+
Plus,
|
|
11
|
+
} from "lucide-react"
|
|
12
|
+
import { cn } from "@/lib/utils"
|
|
13
|
+
import { Button } from "@/components/ui/button"
|
|
14
|
+
|
|
15
|
+
interface MaintenanceNavProps {
|
|
16
|
+
className?: string
|
|
17
|
+
showNewTaskButton?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const TABS = [
|
|
21
|
+
{
|
|
22
|
+
href: "/maintenance",
|
|
23
|
+
label: "Overview",
|
|
24
|
+
icon: Wrench,
|
|
25
|
+
exact: true,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
href: "/maintenance/tasks",
|
|
29
|
+
label: "Work Orders",
|
|
30
|
+
icon: ClipboardList,
|
|
31
|
+
exact: false,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
href: "/maintenance/plans",
|
|
35
|
+
label: "PM Plans",
|
|
36
|
+
icon: Calendar,
|
|
37
|
+
exact: false,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
href: "/maintenance/checklists",
|
|
41
|
+
label: "Checklists",
|
|
42
|
+
icon: CheckSquare,
|
|
43
|
+
exact: false,
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
export function MaintenanceNav({
|
|
48
|
+
className,
|
|
49
|
+
showNewTaskButton = true,
|
|
50
|
+
}: MaintenanceNavProps) {
|
|
51
|
+
const pathname = usePathname()
|
|
52
|
+
|
|
53
|
+
const isTabActive = (href: string, exact: boolean) => {
|
|
54
|
+
if (exact) {
|
|
55
|
+
return pathname === href
|
|
56
|
+
}
|
|
57
|
+
return pathname === href || pathname.startsWith(href + "/")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
className={cn(
|
|
63
|
+
"flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-border pb-3 mb-6",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
{/* Navigation Tabs */}
|
|
68
|
+
<nav
|
|
69
|
+
aria-label="Maintenance navigation"
|
|
70
|
+
className="flex items-center gap-1 overflow-x-auto no-scrollbar py-0.5"
|
|
71
|
+
>
|
|
72
|
+
{TABS.map((tab) => {
|
|
73
|
+
const active = isTabActive(tab.href, tab.exact)
|
|
74
|
+
const Icon = tab.icon
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Link
|
|
78
|
+
key={tab.href}
|
|
79
|
+
href={tab.href}
|
|
80
|
+
className={cn(
|
|
81
|
+
"flex items-center gap-2 px-3.5 py-2 rounded-lg text-sm font-medium transition-all duration-150 shrink-0 select-none",
|
|
82
|
+
active
|
|
83
|
+
? "bg-primary text-primary-foreground shadow-sm"
|
|
84
|
+
: "text-muted-foreground hover:text-foreground hover:bg-muted/60"
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
<Icon className={cn("w-4 h-4", active ? "text-primary-foreground" : "text-muted-foreground")} />
|
|
88
|
+
<span>{tab.label}</span>
|
|
89
|
+
</Link>
|
|
90
|
+
)
|
|
91
|
+
})}
|
|
92
|
+
</nav>
|
|
93
|
+
|
|
94
|
+
{/* Action button */}
|
|
95
|
+
{showNewTaskButton && (
|
|
96
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
97
|
+
<Link href="/maintenance/tasks/new">
|
|
98
|
+
<Button size="sm" className="gap-1.5 shadow-sm">
|
|
99
|
+
<Plus className="w-4 h-4" />
|
|
100
|
+
<span>New Task</span>
|
|
101
|
+
</Button>
|
|
102
|
+
</Link>
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|