898 lines
42 KiB
TypeScript
898 lines
42 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
|
|
import { useAssetDetails, useAssetMutations } from '../hooks/useAsset';
|
|
import { FaArrowLeft, FaSave, FaEdit, FaQrcode } from 'react-icons/fa';
|
|
import type { CreateAssetData } from '../services/assetService';
|
|
|
|
import LinkField from '../components/LinkField';
|
|
|
|
|
|
const AssetDetail: React.FC = () => {
|
|
const { assetName } = useParams<{ assetName: string }>();
|
|
const navigate = useNavigate();
|
|
const [searchParams] = useSearchParams();
|
|
const duplicateFromAsset = searchParams.get('duplicate');
|
|
|
|
const isNewAsset = assetName === 'new';
|
|
const isDuplicating = isNewAsset && !!duplicateFromAsset;
|
|
|
|
// If duplicating, fetch the source asset
|
|
const { asset, loading, error } = useAssetDetails(
|
|
isDuplicating ? duplicateFromAsset : (isNewAsset ? null : assetName || null)
|
|
);
|
|
const { createAsset, updateAsset, loading: saving } = useAssetMutations();
|
|
|
|
const [isEditing, setIsEditing] = useState(isNewAsset);
|
|
const [formData, setFormData] = useState<CreateAssetData>({
|
|
asset_name: '',
|
|
company: '',
|
|
custom_serial_number: '',
|
|
location: '',
|
|
custom_manufacturer: '',
|
|
department: '',
|
|
custom_asset_type: '',
|
|
custom_manufacturing_year: '',
|
|
custom_model: '',
|
|
custom_class: '',
|
|
custom_device_status: '',
|
|
custom_down_time: 0,
|
|
asset_owner_company: '',
|
|
custom_up_time: 0,
|
|
custom_modality: '',
|
|
custom_attach_image: '',
|
|
custom_site_contractor: '',
|
|
custom_total_amount: 0
|
|
});
|
|
|
|
// Load asset data for editing or duplicating
|
|
useEffect(() => {
|
|
if (asset) {
|
|
setFormData({
|
|
asset_name: isDuplicating ? `${asset.asset_name} (Copy)` : (asset.asset_name || ''),
|
|
company: asset.company || '',
|
|
custom_serial_number: isDuplicating ? '' : (asset.custom_serial_number || ''), // Clear serial number for duplicates
|
|
location: asset.location || '',
|
|
custom_manufacturer: asset.custom_manufacturer || '',
|
|
department: asset.department || '',
|
|
custom_asset_type: asset.custom_asset_type || '',
|
|
custom_manufacturing_year: asset.custom_manufacturing_year || '',
|
|
custom_model: asset.custom_model || '',
|
|
custom_class: asset.custom_class || '',
|
|
custom_device_status: asset.custom_device_status || '',
|
|
custom_down_time: asset.custom_down_time || 0,
|
|
asset_owner_company: asset.asset_owner_company || '',
|
|
custom_up_time: asset.custom_up_time || 0,
|
|
custom_modality: asset.custom_modality || '',
|
|
custom_attach_image: asset.custom_attach_image || '',
|
|
custom_site_contractor: asset.custom_site_contractor || '',
|
|
custom_total_amount: asset.custom_total_amount || 0
|
|
});
|
|
}
|
|
}, [asset, isDuplicating]);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
|
const { name, value } = e.target;
|
|
setFormData(prev => ({
|
|
...prev,
|
|
[name]: value
|
|
}));
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
// Validate required fields
|
|
if (!formData.asset_name) {
|
|
alert('Please enter an Asset Name');
|
|
return;
|
|
}
|
|
|
|
if (!formData.custom_asset_type) {
|
|
alert('Please select a Category');
|
|
return;
|
|
}
|
|
|
|
// Show console log for debugging
|
|
console.log('Submitting asset data:', formData);
|
|
|
|
try {
|
|
if (isNewAsset || isDuplicating) {
|
|
const newAsset = await createAsset(formData);
|
|
const successMessage = isDuplicating
|
|
? 'Asset duplicated successfully!'
|
|
: 'Asset created successfully!';
|
|
alert(successMessage);
|
|
navigate(`/assets/${newAsset.name}`);
|
|
} else if (assetName) {
|
|
await updateAsset(assetName, formData);
|
|
alert('Asset updated successfully!');
|
|
setIsEditing(false);
|
|
}
|
|
} catch (err) {
|
|
console.error('Asset save error:', err);
|
|
|
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
|
|
// Check if it's an API deployment issue
|
|
if (errorMessage.includes('404') || errorMessage.includes('not found') ||
|
|
errorMessage.includes('has no attribute') || errorMessage.includes('417')) {
|
|
alert(
|
|
'⚠️ Asset API Not Deployed\n\n' +
|
|
'The Asset API endpoint (asset_api.py) is not deployed on your Frappe server yet.\n\n' +
|
|
'To fix this:\n' +
|
|
'1. SSH into your Frappe server\n' +
|
|
'2. Navigate to: frappe-bench/apps/asset_lite/asset_lite/api/\n' +
|
|
'3. Create the file: asset_api.py\n' +
|
|
'4. Copy the content from frappe_asset_api.py in this project\n' +
|
|
'5. Restart Frappe: bench restart\n\n' +
|
|
'Error: ' + errorMessage
|
|
);
|
|
} else {
|
|
alert('Failed to save asset:\n\n' + errorMessage);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-screen bg-gray-50 dark:bg-gray-900">
|
|
<div className="text-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto"></div>
|
|
<p className="mt-4 text-gray-600 dark:text-gray-400">Loading asset details...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error && !isNewAsset && !isDuplicating) {
|
|
return (
|
|
<div className="p-6 bg-gray-50 dark:bg-gray-900 min-h-screen">
|
|
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
|
|
<p className="text-red-600 dark:text-red-400">Error: {error}</p>
|
|
<button
|
|
onClick={() => navigate('/assets')}
|
|
className="mt-2 text-red-700 dark:text-red-400 underline hover:text-red-800 dark:hover:text-red-300"
|
|
>
|
|
Back to assets list
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Show error for duplicate if source asset not found
|
|
if (error && isDuplicating) {
|
|
return (
|
|
<div className="p-6 bg-gray-50 dark:bg-gray-900 min-h-screen">
|
|
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
|
|
<h3 className="text-lg font-semibold text-yellow-800 dark:text-yellow-300 mb-2">
|
|
Source Asset Not Found
|
|
</h3>
|
|
<p className="text-yellow-700 dark:text-yellow-400">
|
|
The asset you're trying to duplicate could not be found. It may have been deleted or you may not have permission to access it.
|
|
</p>
|
|
<div className="mt-4 flex gap-3">
|
|
<button
|
|
onClick={() => navigate('/assets/new')}
|
|
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
|
>
|
|
Create New Asset
|
|
</button>
|
|
<button
|
|
onClick={() => navigate('/assets')}
|
|
className="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors"
|
|
>
|
|
Back to Assets List
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-6">
|
|
{/* Header */}
|
|
<div className="mb-6 flex justify-between items-center">
|
|
<div className="flex items-center gap-4">
|
|
<button
|
|
onClick={() => navigate('/assets')}
|
|
className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 flex items-center gap-2"
|
|
>
|
|
<FaArrowLeft />
|
|
<span className="text-gray-900 dark:text-white">
|
|
{isDuplicating ? 'Duplicate Asset' : (isNewAsset ? 'New Asset Details' : 'Asset Details')}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
{!isNewAsset && !isEditing && (
|
|
<button
|
|
onClick={() => setIsEditing(true)}
|
|
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg flex items-center gap-2"
|
|
>
|
|
<FaEdit />
|
|
Edit
|
|
</button>
|
|
)}
|
|
{isEditing && (
|
|
<>
|
|
<button
|
|
onClick={() => {
|
|
if (isNewAsset) {
|
|
navigate('/assets');
|
|
} else {
|
|
setIsEditing(false);
|
|
}
|
|
}}
|
|
className="bg-gray-300 hover:bg-gray-400 text-gray-700 px-6 py-2 rounded-lg"
|
|
disabled={saving}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={handleSubmit}
|
|
disabled={saving}
|
|
className="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg flex items-center gap-2 disabled:opacity-50"
|
|
>
|
|
<FaSave />
|
|
{saving ? 'Saving...' : 'Save Changes'}
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Left Column - Asset Information & Technical Specs & Location */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Asset Information */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Asset Information</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Asset Name <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="asset_name"
|
|
value={formData.asset_name}
|
|
onChange={handleChange}
|
|
placeholder="e.g. Laptop Model X"
|
|
required
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
{/* <div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Category <span className="text-red-500">*</span>
|
|
</label>
|
|
<select
|
|
name="custom_asset_type"
|
|
value={formData.custom_asset_type}
|
|
onChange={handleChange}
|
|
required
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select category</option>
|
|
<option value="Medical Equipment">Medical Equipment</option>
|
|
<option value="Office Equipment">Office Equipment</option>
|
|
<option value="IT Equipment">IT Equipment</option>
|
|
<option value="Furniture">Furniture</option>
|
|
</select>
|
|
</div> */}
|
|
|
|
<LinkField
|
|
label="Category"
|
|
doctype="Asset Type"
|
|
value={formData.custom_asset_type || ''}
|
|
onChange={(val) => setFormData({ ...formData, custom_asset_type: val })}
|
|
/>
|
|
|
|
|
|
{/* <div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Modality <span className="text-red-500">*</span>
|
|
</label>
|
|
<select
|
|
name="custom_modality"
|
|
value={formData.custom_modality}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select modality</option>
|
|
<option value="X-Ray">X-Ray</option>
|
|
<option value="CT">CT Scan</option>
|
|
<option value="MRI">MRI</option>
|
|
<option value="Ultrasound">Ultrasound</option>
|
|
<option value="Other">Other</option>
|
|
</select>
|
|
</div> */}
|
|
<LinkField
|
|
label="Modality"
|
|
doctype="Modality"
|
|
value={formData.custom_modality || ''}
|
|
onChange={(val) => setFormData({ ...formData, custom_modality: val })}
|
|
/>
|
|
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Class <span className="text-red-500">*</span>
|
|
</label>
|
|
<select
|
|
name="custom_class"
|
|
value={formData.custom_class}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select class</option>
|
|
<option value="Class A">Class A</option>
|
|
<option value="Class B">Class B</option>
|
|
<option value="Class C">Class C</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="md:col-span-2">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Asset ID <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={isNewAsset || isDuplicating ? 'Auto-generated' : asset?.name}
|
|
disabled
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
{isDuplicating && (
|
|
<p className="mt-1 text-xs text-blue-600 dark:text-blue-400">
|
|
💡 Duplicating from: {duplicateFromAsset}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Technical Specs */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Technical Specs</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Serial No.
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="custom_serial_number"
|
|
value={formData.custom_serial_number}
|
|
onChange={handleChange}
|
|
placeholder="e.g. SN-12345"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
System ID
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="e.g. SYS-755"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Serial No.2
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="e.g. SR-V021-A"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
{/* <div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Manufacturer
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="custom_manufacturer"
|
|
value={formData.custom_manufacturer}
|
|
onChange={handleChange}
|
|
placeholder="Manufacturer name"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div> */}
|
|
|
|
<LinkField
|
|
label="Manufacturer"
|
|
doctype="Manufacturer"
|
|
value={formData.manufacturer || ''}
|
|
onChange={(val) => setFormData({ ...formData, manufacturer: val })}
|
|
/>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Model
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="custom_model"
|
|
value={formData.custom_model}
|
|
onChange={handleChange}
|
|
placeholder="Model number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Model Number
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="Model number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Location */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Location</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{/* <div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Company
|
|
</label>
|
|
<select
|
|
name="company"
|
|
value={formData.company}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select company</option>
|
|
<option value="ABC Hospital">ABC Hospital</option>
|
|
<option value="XYZ Clinic">XYZ Clinic</option>
|
|
</select>
|
|
</div> */}
|
|
|
|
<LinkField
|
|
label="Hospital"
|
|
doctype="Company"
|
|
value={formData.company || ''}
|
|
onChange={(val) => setFormData({ ...formData, company: val })}
|
|
/>
|
|
|
|
{/* <div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Department
|
|
</label>
|
|
<select
|
|
name="department"
|
|
value={formData.department}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select department</option>
|
|
<option value="Radiology">Radiology</option>
|
|
<option value="Cardiology">Cardiology</option>
|
|
<option value="IT">IT</option>
|
|
</select>
|
|
</div> */}
|
|
|
|
<LinkField
|
|
label="Department"
|
|
doctype="Department"
|
|
value={formData.department || ''}
|
|
onChange={(val) => setFormData({ ...formData, department: val })}
|
|
/>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Building
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="location"
|
|
value={formData.location}
|
|
onChange={handleChange}
|
|
placeholder="Building name"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Area/Unit
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="Area or unit"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Room Number
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="e.g. Room 001-002"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Assigned To
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="Person or department"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Coverage */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Coverage</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Site Contractor
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="custom_site_contractor"
|
|
value={formData.custom_site_contractor}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Contract Number
|
|
</label>
|
|
<input
|
|
type="text"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Subcontractor
|
|
</label>
|
|
<input
|
|
type="text"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Service Agreement
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Service Coverage
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Start Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
End Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Total Amount
|
|
</label>
|
|
<input
|
|
type="number"
|
|
name="custom_total_amount"
|
|
value={formData.custom_total_amount}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div className="md:col-span-3">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Comments
|
|
</label>
|
|
<textarea
|
|
rows={2}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Acquisition Details */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Acquisition Details</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Purchase Order Number
|
|
</label>
|
|
<input
|
|
type="text"
|
|
placeholder="PO number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Supplier/Vendor
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Gross Purchase Amount
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Price</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Purchase Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Installation Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Available For Use Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Financial Details */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">Financial Details</h2>
|
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
|
The depreciation method is an accounting method used to allocate the cost of a tangible asset over its useful life.
|
|
</p>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Depreciation Method
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Straight Line</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Depreciation Rate (%)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Current Value
|
|
</label>
|
|
<input
|
|
type="number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div className="md:col-span-3">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Annual Rate
|
|
</label>
|
|
<input
|
|
type="number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div className="md:col-span-3">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Current Value
|
|
</label>
|
|
<input
|
|
type="number"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* End-of-Life Details */}
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">End-of-Life Details</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Expected End-of-Life Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Disposal Method
|
|
</label>
|
|
<select
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Recycling</option>
|
|
<option value="Donation">Donation</option>
|
|
<option value="Sale">Sale</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Column - More Details */}
|
|
<div className="space-y-6">
|
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h2 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">More Details</h2>
|
|
|
|
<div className="mb-4">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Condition
|
|
</label>
|
|
<select
|
|
name="custom_device_status"
|
|
value={formData.custom_device_status}
|
|
onChange={handleChange}
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
>
|
|
<option value="">Select status</option>
|
|
<option value="Operational">Operational</option>
|
|
<option value="Under Maintenance">Under Maintenance</option>
|
|
<option value="Decommissioned">Decommissioned</option>
|
|
</select>
|
|
</div>
|
|
|
|
{/* QR Code */}
|
|
<div className="flex justify-center my-6">
|
|
<div className="border-2 border-gray-300 dark:border-gray-600 p-4 rounded-lg">
|
|
<FaQrcode size={120} className="text-gray-400 dark:text-gray-500" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-4">
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Description
|
|
</label>
|
|
<textarea
|
|
rows={4}
|
|
placeholder="Brief description of the asset"
|
|
disabled={!isEditing}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AssetDetail;
|
|
|