From c8931592862e6b4b7d3009c341292dad4f84349a Mon Sep 17 00:00:00 2001 From: Mercy Duru Date: Sun, 28 Jun 2026 00:41:28 +0100 Subject: [PATCH 1/3] Build Asset Disposal, contract management, implement email notification, add pdf and excel to export --- frontend/app/(dashboard)/asset-notes/page.tsx | 120 ++- frontend/app/(dashboard)/assets-bulk/page.tsx | 154 +++- frontend/app/(dashboard)/assets/[id]/page.tsx | 820 ++++++++++++------ .../app/(dashboard)/assets/disposals/page.tsx | 262 ++++++ .../app/(dashboard)/check-in-out/page.tsx | 126 ++- frontend/app/(dashboard)/cmdb/page.tsx | 117 ++- frontend/app/(dashboard)/contracts/page.tsx | 322 +++++++ frontend/app/(dashboard)/departments/page.tsx | 197 +++-- frontend/app/(dashboard)/developer/page.tsx | 154 +++- frontend/app/(dashboard)/import/page.tsx | 177 +++- .../(dashboard)/maintenance-calendar/page.tsx | 116 ++- .../app/(dashboard)/my-dashboard/page.tsx | 83 +- .../(dashboard)/notifications-live/page.tsx | 86 +- .../app/(dashboard)/print-labels/page.tsx | 132 ++- frontend/app/(dashboard)/rbac/page.tsx | 93 +- .../(dashboard)/settings/appearance/page.tsx | 67 +- .../(dashboard)/software-licenses/page.tsx | 102 ++- frontend/app/(dashboard)/stocktake/page.tsx | 139 ++- frontend/app/(dashboard)/work-orders/page.tsx | 116 ++- frontend/app/app/booking/page.tsx | 122 ++- frontend/app/app/locations/page.tsx | 106 ++- frontend/app/app/vendors/page.tsx | 128 ++- .../contracts/contract-detail-drawer.tsx | 316 +++++++ .../contracts/create-contract-modal.tsx | 265 ++++++ .../components/contracts/status-badge.tsx | 26 + frontend/components/contracts/type-badge.tsx | 27 + frontend/components/layout/sidebar.tsx | 11 +- frontend/lib/api/assets.ts | 154 +++- frontend/lib/api/contracts.ts | 69 ++ frontend/lib/query/hooks/useAsset.ts | 238 ++++- frontend/lib/query/hooks/useAssets.ts | 90 +- frontend/lib/query/hooks/useContracts.ts | 75 ++ frontend/lib/query/index.ts | 24 +- frontend/lib/query/keys.ts | 54 +- frontend/lib/query/types/asset.ts | 83 +- frontend/lib/query/types/contract.ts | 97 +++ 36 files changed, 4389 insertions(+), 879 deletions(-) create mode 100644 frontend/app/(dashboard)/assets/disposals/page.tsx create mode 100644 frontend/app/(dashboard)/contracts/page.tsx create mode 100644 frontend/components/contracts/contract-detail-drawer.tsx create mode 100644 frontend/components/contracts/create-contract-modal.tsx create mode 100644 frontend/components/contracts/status-badge.tsx create mode 100644 frontend/components/contracts/type-badge.tsx create mode 100644 frontend/lib/api/contracts.ts create mode 100644 frontend/lib/query/hooks/useContracts.ts create mode 100644 frontend/lib/query/types/contract.ts diff --git a/frontend/app/(dashboard)/asset-notes/page.tsx b/frontend/app/(dashboard)/asset-notes/page.tsx index efadc244d..5d74e10bb 100644 --- a/frontend/app/(dashboard)/asset-notes/page.tsx +++ b/frontend/app/(dashboard)/asset-notes/page.tsx @@ -1,40 +1,58 @@ -'use client'; +"use client"; -import { useState, useRef } from 'react'; -import { Send } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { useState, useRef } from "react"; +import { Send } from "lucide-react"; +import { Button } from "@/components/ui/button"; -const USERS = ['alice.m', 'bob.k', 'carol.s', 'dave.t', 'emma.w']; +const USERS = ["alice.m", "bob.k", "carol.s", "dave.t", "emma.w"]; -interface Note { id: string; author: string; text: string; timestamp: string } +interface Note { + id: string; + author: string; + text: string; + timestamp: string; +} const MOCK_NOTES: Note[] = [ - { id:'1', author:'Alice M.', text:'Sent for repair. @bob.k please follow up with vendor.', timestamp:'2024-01-20T10:00:00Z' }, - { id:'2', author:'Bob K.', text:'Will check @carol.s for parts availability.', timestamp:'2024-01-20T10:30:00Z' }, + { + id: "1", + author: "Alice M.", + text: "Sent for repair. @bob.k please follow up with vendor.", + timestamp: "2024-01-20T10:00:00Z", + }, + { + id: "2", + author: "Bob K.", + text: "Will check @carol.s for parts availability.", + timestamp: "2024-01-20T10:30:00Z", + }, ]; export default function AssetNotesPage() { const [notes, setNotes] = useState(MOCK_NOTES); - const [text, setText] = useState(''); + const [text, setText] = useState(""); const [suggestions, setSuggestions] = useState([]); const [mentionStart, setMentionStart] = useState(-1); const inputRef = useRef(null); const handleChange = (val: string) => { setText(val); - const atIdx = val.lastIndexOf('@'); - if (atIdx >= 0 && atIdx === val.length - 1 - (val.slice(atIdx + 1).length) + atIdx) { + const atIdx = val.lastIndexOf("@"); + if ( + atIdx >= 0 && + atIdx === val.length - 1 - val.slice(atIdx + 1).length + atIdx + ) { const fragment = val.slice(atIdx + 1); - const matches = USERS.filter(u => u.startsWith(fragment)); + const matches = USERS.filter((u) => u.startsWith(fragment)); setSuggestions(matches); setMentionStart(atIdx); - } else if (val.slice(val.lastIndexOf('@') + 1).includes(' ')) { + } else if (val.slice(val.lastIndexOf("@") + 1).includes(" ")) { setSuggestions([]); } }; const insertMention = (user: string) => { - const newText = text.slice(0, mentionStart) + @ ; + const newText = text.slice(0, mentionStart) + `@${user} `; setText(newText); setSuggestions([]); inputRef.current?.focus(); @@ -42,36 +60,82 @@ export default function AssetNotesPage() { const addNote = () => { if (!text.trim()) return; - setNotes(prev => [...prev, { id: Date.now().toString(), author:'You', text, timestamp: new Date().toISOString() }]); - setText(''); + setNotes((prev) => [ + ...prev, + { + id: Date.now().toString(), + author: "You", + text, + timestamp: new Date().toISOString(), + }, + ]); + setText(""); }; - const renderText = (t: string) => t.replace(/@(\w+\.\w+)/g, '@'); + const renderText = (t: string) => + t.replace( + /@(\w+\.\w+)/g, + '@', + ); return (
-

Asset Notes

@mention support with user autocomplete

+
+

Asset Notes

+

+ @mention support with user autocomplete +

+
- {notes.map(n => ( -
+ {notes.map((n) => ( +
- {n.author} - {new Date(n.timestamp).toLocaleString()} + + {n.author} + + + {new Date(n.timestamp).toLocaleString()} +
-

+

))}
-