From c0edd7cd87f5afb984cd3d4ceabb01ca5ee6c956 Mon Sep 17 00:00:00 2001 From: snowrugar-beep Date: Sat, 30 May 2026 20:01:44 +0000 Subject: [PATCH] feat: implement QR code display and download for assets Closes #780 --- frontend/app/(dashboard)/assets/[id]/page.tsx | 788 +----------------- .../opsce/features/assets/AssetQRCode.tsx | 161 ++++ frontend/package-lock.json | 2 +- frontend/package.json | 2 +- 4 files changed, 164 insertions(+), 789 deletions(-) create mode 100644 frontend/opsce/features/assets/AssetQRCode.tsx diff --git a/frontend/app/(dashboard)/assets/[id]/page.tsx b/frontend/app/(dashboard)/assets/[id]/page.tsx index 3333bbbf..4890b274 100644 --- a/frontend/app/(dashboard)/assets/[id]/page.tsx +++ b/frontend/app/(dashboard)/assets/[id]/page.tsx @@ -1,787 +1 @@ -// frontend/app/(dashboard)/assets/[id]/page.tsx -"use client"; - -import { useState, useEffect } from "react"; -import { useParams, useRouter } from "next/navigation"; -import { - ArrowLeft, - Clock, - FileText, - Hash, - Wrench, - FolderOpen, - StickyNote, - Pencil, - Trash2, - ArrowRightLeft, - RefreshCw, - CheckCircle, - Upload, - Plus, - Printer, - QrCode, -} from "lucide-react"; -import { format } from "date-fns"; -import { Button } from "@/components/ui/button"; -import { StatusBadge } from "@/components/assets/status-badge"; -import { ConditionBadge } from "@/components/assets/condition-badge"; -import { ConfirmDialog } from "@/components/ui/confirm-dialog"; -import { - useAsset, - useAssetHistory, - useAssetDocuments, - useMaintenanceRecords, - useAssetNotes, - useDeleteAsset, - useUploadDocument, - useDeleteDocument, - useCreateMaintenanceRecord, - useUpdateMaintenanceStatus, - useCreateNote, - useDeleteNote, -} from "@/lib/query/hooks/useAsset"; -import type { MaintenanceType } from "@/lib/query/types/asset"; - -type Tab = "overview" | "history" | "maintenance" | "documents" | "notes"; - -// ── Skeleton ──────────────────────────────────────────────────────────────── -function Skeleton({ className }: { className?: string }) { - return ( -
- ); -} - -// ── DetailRow ──────────────────────────────────────────────────────────────── -function DetailRow({ - label, - value, - fallback = "—", -}: { - label: string; - value?: string | null; - fallback?: string; -}) { - return ( -
-
{label}
-
{value || fallback}
-
- ); -} - -// ── ActionBadge ────────────────────────────────────────────────────────────── -const actionColors: Record = { - CREATED: "bg-green-100 text-green-700", - UPDATED: "bg-blue-100 text-blue-700", - STATUS_CHANGED: "bg-yellow-100 text-yellow-700", - TRANSFERRED: "bg-purple-100 text-purple-700", - MAINTENANCE: "bg-orange-100 text-orange-700", - NOTE_ADDED: "bg-gray-100 text-gray-600", - DOCUMENT_UPLOADED: "bg-teal-100 text-teal-700", -}; - -function ActionBadge({ action }: { action: string }) { - const cls = actionColors[action] ?? "bg-gray-100 text-gray-600"; - return ( - - {action.replace(/_/g, " ")} - - ); -} - -// ── MaintenanceStatusBadge ─────────────────────────────────────────────────── -const maintenanceStatusColors: Record = { - SCHEDULED: "bg-blue-100 text-blue-700", - IN_PROGRESS: "bg-yellow-100 text-yellow-700", - COMPLETED: "bg-green-100 text-green-700", - CANCELLED: "bg-gray-100 text-gray-500", -}; - -function MaintenanceStatusBadge({ status }: { status: string }) { - const cls = maintenanceStatusColors[status] ?? "bg-gray-100 text-gray-500"; - return ( - - {status.replace(/_/g, " ")} - - ); -} - -// ── ScheduleMaintenanceModal ───────────────────────────────────────────────── -function ScheduleMaintenanceModal({ - assetId, - onClose, -}: { - assetId: string; - onClose: () => void; -}) { - const [form, setForm] = useState({ - type: "PREVENTIVE" as MaintenanceType, - description: "", - scheduledDate: "", - notes: "", - }); - const { mutate, isPending } = useCreateMaintenanceRecord(assetId, { - onSuccess: onClose, - }); - - return ( -
-
-
-

- Schedule Maintenance -

-
-
- - -
-
- - setForm({ ...form, description: e.target.value })} - /> -
-
- - setForm({ ...form, scheduledDate: e.target.value })} - /> -
-
- -