diff --git a/frontend/app/(dashboard)/assets/[id]/page.tsx b/frontend/app/(dashboard)/assets/[id]/page.tsx index 3de49715..c53d409d 100644 --- a/frontend/app/(dashboard)/assets/[id]/page.tsx +++ b/frontend/app/(dashboard)/assets/[id]/page.tsx @@ -8,6 +8,9 @@ import { Button } from "@/components/ui/button"; import { StatusBadge } from "@/components/assets/status-badge"; import { ConditionBadge } from "@/components/assets/condition-badge"; import { useAsset, useAssetHistory } from "@/lib/query/hooks/useAsset"; +import { useAuthStore } from "@/store/auth.store"; +import { TransferAssetDialog } from "@/components/assets/transfer-dialog"; +import { MoveHorizontal } from "lucide-react"; type Tab = "overview" | "history" | "documents"; @@ -15,6 +18,8 @@ export default function AssetDetailPage() { const { id } = useParams<{ id: string }>(); const router = useRouter(); const [tab, setTab] = useState("overview"); + const [isTransferOpen, setIsTransferOpen] = useState(false); + const { user } = useAuthStore(); const { data: asset, isLoading } = useAsset(id); const { data: history = [] } = useAssetHistory(id); @@ -73,6 +78,16 @@ export default function AssetDetailPage() { + + {(user?.role === 'ADMIN' || user?.role === 'MANAGER') && ( + + )} @@ -82,11 +97,10 @@ export default function AssetDetailPage() { + + + + +
+

+ {activeTab} ({items?.length || 0}) +

+ +
+ + {isAdding && ( +
+
+
+ + setFormData({ ...formData, name: e.target.value })} + className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all" + /> + {activeTab === 'departments' ? createDept.isError && ( +

+ {createDept.error?.message} +

+ ) : createCat.isError && ( +

+ {createCat.error?.message} +

+ )} +
+
+ + setFormData({ ...formData, description: e.target.value })} + className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all" + /> +
+
+
+ + +
+
+ )} + + {isLoading ? ( +
+ +

Loading {activeTab}...

+
+ ) : ( +
+ {items?.map((item: any) => ( +
+
+

{item.name}

+ +
+

+ {item.description || 'No description provided.'} +

+
+ Asset Count + {item.assetCount || 0} +
+
+ ))} + {!isLoading && items?.length === 0 && ( +
+ +

No {activeTab} found. Create one to get started.

+
+ )} +
+ )} + + ); +} diff --git a/frontend/components/assets/transfer-dialog.tsx b/frontend/components/assets/transfer-dialog.tsx new file mode 100644 index 00000000..0df1fe35 --- /dev/null +++ b/frontend/components/assets/transfer-dialog.tsx @@ -0,0 +1,141 @@ +'use client'; + +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { X, Loader2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { useDepartments, useUsers, useTransferAsset } from '@/lib/query/hooks/useAsset'; +import { toast } from 'react-toastify'; + +const schema = z.object({ + departmentId: z.string().min(1, 'Department is required'), + assignedToId: z.string().optional(), + location: z.string().optional(), + notes: z.string().optional(), +}); + +type FormValues = z.infer; + +interface Props { + assetId: string; + assetName: string; + onClose: () => void; + onSuccess?: () => void; +} + +export function TransferAssetDialog({ assetId, assetName, onClose, onSuccess }: Props) { + const { data: departments = [] } = useDepartments(); + const { data: users = [] } = useUsers(); + const transferAsset = useTransferAsset(assetId); + + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: zodResolver(schema), + }); + + const onSubmit = async (values: FormValues) => { + try { + await transferAsset.mutateAsync({ + departmentId: values.departmentId, + assignedToId: values.assignedToId || undefined, + location: values.location || undefined, + notes: values.notes || undefined, + }); + toast.success('Asset transferred successfully'); + onSuccess?.(); + onClose(); + } catch (err: any) { + const message = err.message || 'Failed to transfer asset.'; + setError('root', { message }); + toast.error(message); + } + }; + + return ( +
+ {/* Backdrop */} +
+ + {/* Modal */} +
+
+

Transfer Asset: {assetName}

+ +
+ +
+ {/* Department */} +
+ + + {errors.departmentId &&

{errors.departmentId.message}

} +
+ + {/* Assigned To */} +
+ + +
+ + + +
+ +