Lated Updated for QR code
This commit is contained in:
parent
18ed88bff0
commit
c11db48dbf
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import apiService from '../services/apiService'; // ✅ your ApiService
|
||||
import apiService from '../services/apiService';
|
||||
|
||||
interface LinkFieldProps {
|
||||
label: string;
|
||||
@ -8,6 +8,7 @@ interface LinkFieldProps {
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
filters?: Record<string, any>;
|
||||
}
|
||||
|
||||
const LinkField: React.FC<LinkFieldProps> = ({
|
||||
@ -17,29 +18,43 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
onChange,
|
||||
placeholder,
|
||||
disabled = false,
|
||||
filters = {},
|
||||
}) => {
|
||||
const [searchResults, setSearchResults] = useState<{ value: string; description?: string }[]>([]);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Fetch link options from ERPNext
|
||||
// Fetch link options from ERPNext with filters
|
||||
const searchLink = async (text: string = '') => {
|
||||
try {
|
||||
const params = new URLSearchParams({ doctype, txt: text });
|
||||
const params = new URLSearchParams({
|
||||
doctype,
|
||||
txt: text,
|
||||
});
|
||||
|
||||
// Add filters if provided
|
||||
if (filters && Object.keys(filters).length > 0) {
|
||||
// Convert filters to JSON string for Frappe API
|
||||
params.append('filters', JSON.stringify(filters));
|
||||
}
|
||||
|
||||
const response = await apiService.apiCall<{ value: string; description?: string }[]>(
|
||||
`/api/method/frappe.desk.search.search_link?${params.toString()}`
|
||||
);
|
||||
setSearchResults(response || []);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching ${doctype} links:`, error);
|
||||
setSearchResults([]);
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch default options when dropdown opens
|
||||
// Fetch default options when dropdown opens or filters change
|
||||
useEffect(() => {
|
||||
if (isDropdownOpen) searchLink('');
|
||||
}, [isDropdownOpen]);
|
||||
if (isDropdownOpen) {
|
||||
searchLink(searchText || '');
|
||||
}
|
||||
}, [isDropdownOpen, filters]); // Re-fetch when filters change
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
@ -54,7 +69,9 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="relative w-full mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{label}</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
{label}
|
||||
</label>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
@ -62,47 +79,8 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
placeholder={placeholder || `Select ${label}`}
|
||||
disabled={disabled}
|
||||
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`}
|
||||
onFocus={() => !disabled && setDropdownOpen(true)}
|
||||
onChange={(e) => {
|
||||
const text = e.target.value;
|
||||
setSearchText(text);
|
||||
searchLink(text);
|
||||
onChange(text);
|
||||
}}
|
||||
/>
|
||||
|
||||
{isDropdownOpen && searchResults.length > 0 && !disabled && (
|
||||
<ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md mt-1 max-h-48 overflow-auto w-full shadow-lg">
|
||||
{searchResults.map((item, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
onClick={() => {
|
||||
onChange(item.value);
|
||||
setDropdownOpen(false);
|
||||
}}
|
||||
className={`px-3 py-2 cursor-pointer
|
||||
text-gray-900 dark:text-gray-100
|
||||
hover:bg-blue-500 dark:hover:bg-blue-600
|
||||
${value === item.value ? 'bg-blue-50 dark:bg-blue-700 font-semibold' : ''}`}
|
||||
>
|
||||
{item.value}
|
||||
{item.description && (
|
||||
<span className="text-gray-600 dark:text-gray-300 text-xs ml-2">{item.description}</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
|
||||
{/* <input
|
||||
type="text"
|
||||
value={value}
|
||||
placeholder={placeholder || `Select ${label}`}
|
||||
disabled={disabled}
|
||||
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`}
|
||||
onFocus={() => !disabled && setDropdownOpen(true)}
|
||||
onChange={(e) => {
|
||||
const text = e.target.value;
|
||||
@ -110,17 +88,11 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
searchLink(text);
|
||||
onChange(text);
|
||||
}}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500
|
||||
disabled:bg-gray-100 dark:disabled:bg-gray-700
|
||||
border-gray-300 dark:border-gray-600
|
||||
bg-white dark:bg-gray-700 text-gray-900 dark:text-white`}
|
||||
/>
|
||||
|
||||
{isDropdownOpen && searchResults.length > 0 && (
|
||||
<ul
|
||||
className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md mt-1 max-h-48 overflow-auto w-full shadow-lg"
|
||||
>
|
||||
{isDropdownOpen && searchResults.length > 0 && !disabled && (
|
||||
<ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md mt-1 max-h-48 overflow-auto w-full shadow-lg">
|
||||
{searchResults.map((item, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
@ -128,22 +100,137 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
onChange(item.value);
|
||||
setDropdownOpen(false);
|
||||
}}
|
||||
className={`px-3 py-2 hover:bg-blue-100 dark:hover:bg-gray-700 cursor-pointer
|
||||
${value === item.value ? 'bg-blue-50 dark:bg-gray-600 font-semibold' : ''}`}
|
||||
className={`px-3 py-2 cursor-pointer
|
||||
text-gray-900 dark:text-gray-100
|
||||
hover:bg-blue-500 dark:hover:bg-blue-600 hover:text-white
|
||||
${value === item.value ? 'bg-blue-50 dark:bg-blue-700 font-semibold' : ''}`}
|
||||
>
|
||||
<div>{item.value}</div>
|
||||
{item.value}
|
||||
{item.description && (
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs">{item.description}</div>
|
||||
<span className="text-gray-600 dark:text-gray-300 text-xs ml-2">
|
||||
{item.description}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)} */}
|
||||
|
||||
|
||||
)}
|
||||
|
||||
{/* Show message when no results found */}
|
||||
{isDropdownOpen && searchResults.length === 0 && !disabled && (
|
||||
<div className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md mt-1 w-full shadow-lg p-3 text-center text-gray-500 dark:text-gray-400 text-sm">
|
||||
No results found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinkField;
|
||||
|
||||
|
||||
// import React, { useState, useEffect, useRef } from 'react';
|
||||
// import apiService from '../services/apiService'; // ✅ your ApiService
|
||||
|
||||
// interface LinkFieldProps {
|
||||
// label: string;
|
||||
// doctype: string;
|
||||
// value: string;
|
||||
// onChange: (value: string) => void;
|
||||
// placeholder?: string;
|
||||
// disabled?: boolean;
|
||||
// filters?: Record<string, any>
|
||||
// }
|
||||
|
||||
// const LinkField: React.FC<LinkFieldProps> = ({
|
||||
// label,
|
||||
// doctype,
|
||||
// value,
|
||||
// onChange,
|
||||
// placeholder,
|
||||
// disabled = false,
|
||||
// }) => {
|
||||
// const [searchResults, setSearchResults] = useState<{ value: string; description?: string }[]>([]);
|
||||
// const [searchText, setSearchText] = useState('');
|
||||
// const [isDropdownOpen, setDropdownOpen] = useState(false);
|
||||
// const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// // Fetch link options from ERPNext
|
||||
// const searchLink = async (text: string = '') => {
|
||||
// try {
|
||||
// const params = new URLSearchParams({ doctype, txt: text });
|
||||
// const response = await apiService.apiCall<{ value: string; description?: string }[]>(
|
||||
// `/api/method/frappe.desk.search.search_link?${params.toString()}`
|
||||
// );
|
||||
// setSearchResults(response || []);
|
||||
// } catch (error) {
|
||||
// console.error(`Error fetching ${doctype} links:`, error);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // Fetch default options when dropdown opens
|
||||
// useEffect(() => {
|
||||
// if (isDropdownOpen) searchLink('');
|
||||
// }, [isDropdownOpen]);
|
||||
|
||||
// // Close dropdown when clicking outside
|
||||
// useEffect(() => {
|
||||
// const handleClickOutside = (event: MouseEvent) => {
|
||||
// if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||
// setDropdownOpen(false);
|
||||
// }
|
||||
// };
|
||||
// document.addEventListener('mousedown', handleClickOutside);
|
||||
// return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
// }, []);
|
||||
|
||||
// return (
|
||||
// <div ref={containerRef} className="relative w-full mb-4">
|
||||
// <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{label}</label>
|
||||
|
||||
// <input
|
||||
// type="text"
|
||||
// value={value}
|
||||
// placeholder={placeholder || `Select ${label}`}
|
||||
// disabled={disabled}
|
||||
// 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`}
|
||||
// onFocus={() => !disabled && setDropdownOpen(true)}
|
||||
// onChange={(e) => {
|
||||
// const text = e.target.value;
|
||||
// setSearchText(text);
|
||||
// searchLink(text);
|
||||
// onChange(text);
|
||||
// }}
|
||||
// />
|
||||
|
||||
// {isDropdownOpen && searchResults.length > 0 && !disabled && (
|
||||
// <ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
// rounded-md mt-1 max-h-48 overflow-auto w-full shadow-lg">
|
||||
// {searchResults.map((item, idx) => (
|
||||
// <li
|
||||
// key={idx}
|
||||
// onClick={() => {
|
||||
// onChange(item.value);
|
||||
// setDropdownOpen(false);
|
||||
// }}
|
||||
// className={`px-3 py-2 cursor-pointer
|
||||
// text-gray-900 dark:text-gray-100
|
||||
// hover:bg-blue-500 dark:hover:bg-blue-600
|
||||
// ${value === item.value ? 'bg-blue-50 dark:bg-blue-700 font-semibold' : ''}`}
|
||||
// >
|
||||
// {item.value}
|
||||
// {item.description && (
|
||||
// <span className="text-gray-600 dark:text-gray-300 text-xs ml-2">{item.description}</span>
|
||||
// )}
|
||||
// </li>
|
||||
// ))}
|
||||
// </ul>
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default LinkField;
|
||||
|
||||
@ -5,6 +5,7 @@ import { FaArrowLeft, FaSave, FaEdit, FaQrcode } from 'react-icons/fa';
|
||||
import type { CreateAssetData } from '../services/assetService';
|
||||
|
||||
import LinkField from '../components/LinkField';
|
||||
import apiService from '../services/apiService'; // ✅ your ApiService
|
||||
|
||||
|
||||
const AssetDetail: React.FC = () => {
|
||||
@ -21,8 +22,12 @@ const AssetDetail: React.FC = () => {
|
||||
isDuplicating ? duplicateFromAsset : (isNewAsset ? null : assetName || null)
|
||||
);
|
||||
const { createAsset, updateAsset, loading: saving } = useAssetMutations();
|
||||
|
||||
|
||||
const [isEditing, setIsEditing] = useState(isNewAsset);
|
||||
|
||||
const [userSiteName, setUserSiteName] = useState('');
|
||||
const [departmentFilters, setDepartmentFilters] = useState<Record<string, any>>({});
|
||||
|
||||
const [formData, setFormData] = useState<CreateAssetData>({
|
||||
asset_name: '',
|
||||
company: '',
|
||||
@ -41,9 +46,53 @@ const AssetDetail: React.FC = () => {
|
||||
custom_modality: '',
|
||||
custom_attach_image: '',
|
||||
custom_site_contractor: '',
|
||||
custom_total_amount: 0
|
||||
custom_total_amount: 0,
|
||||
calculate_depreciation: false,
|
||||
available_for_use_date: isNewAsset ? new Date().toISOString().split('T')[0] : undefined
|
||||
});
|
||||
|
||||
// Load user details on mount
|
||||
useEffect(() => {
|
||||
async function loadUserDetails() {
|
||||
try {
|
||||
const user = await apiService.getUserDetails();
|
||||
setUserSiteName(user.custom_site_name || '');
|
||||
} catch (err) {
|
||||
console.error('Error loading user details', err);
|
||||
}
|
||||
}
|
||||
|
||||
loadUserDetails();
|
||||
}, []);
|
||||
|
||||
// Update department filters when company or userSiteName changes
|
||||
useEffect(() => {
|
||||
const filters: Record<string, any> = {};
|
||||
|
||||
// Base filter: company must match
|
||||
if (formData.company) {
|
||||
filters['company'] = formData.company;
|
||||
}
|
||||
|
||||
// Apply department name filters based on site name and company
|
||||
const isMobileSite =
|
||||
(userSiteName && userSiteName.startsWith('Mobile')) ||
|
||||
(formData.company && formData.company.startsWith('Mobile'));
|
||||
|
||||
if (isMobileSite) {
|
||||
// For Mobile sites, exclude Non Bio departments (show Bio departments)
|
||||
// Frappe filter format: ['not like', 'pattern']
|
||||
filters['department_name'] = ['not like', 'Non Bio%'];
|
||||
} else if (userSiteName || formData.company) {
|
||||
// For non-Mobile sites, exclude Bio departments (show Non-Bio departments)
|
||||
filters['department_name'] = ['not like', 'Bio%'];
|
||||
}
|
||||
|
||||
console.log('Department filters updated:', filters); // Debug log
|
||||
|
||||
setDepartmentFilters(filters);
|
||||
}, [formData.company, userSiteName]);
|
||||
|
||||
// Load asset data for editing or duplicating
|
||||
useEffect(() => {
|
||||
if (asset) {
|
||||
@ -65,11 +114,52 @@ const AssetDetail: React.FC = () => {
|
||||
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
|
||||
custom_total_amount: asset.custom_total_amount || 0,
|
||||
gross_purchase_amount:asset.gross_purchase_amount || 0,
|
||||
available_for_use_date: asset.available_for_use_date || '',
|
||||
calculate_depreciation: asset.calculate_depreciation || false
|
||||
});
|
||||
}
|
||||
}, [asset, isDuplicating]);
|
||||
|
||||
const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!assetName || assetName === "new") return;
|
||||
|
||||
const fetchQRCode = async () => {
|
||||
try {
|
||||
// Try fixed predictable URL
|
||||
const directUrl = `/files/${assetName}-qr.png`;
|
||||
console.log(directUrl)
|
||||
|
||||
// Quickly test if file exists
|
||||
const response = await fetch(directUrl, { method: "HEAD" });
|
||||
console.log(response)
|
||||
|
||||
if (response.ok) {
|
||||
setQrCodeUrl(directUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// If not available, fallback to File doctype API
|
||||
const fileRes = await apiService.apiCall<any>(
|
||||
`/api/resource/File?filters=[["File","attached_to_name","=","${assetName}"]]`
|
||||
);
|
||||
|
||||
if (fileRes?.data?.length > 0) {
|
||||
setQrCodeUrl(fileRes.data[0].file_url);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error loading QR code:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchQRCode();
|
||||
}, [assetName, asset]);
|
||||
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
@ -98,6 +188,10 @@ const AssetDetail: React.FC = () => {
|
||||
try {
|
||||
if (isNewAsset || isDuplicating) {
|
||||
const newAsset = await createAsset(formData);
|
||||
if (newAsset.name) {
|
||||
const qrUrl = `/files/${newAsset.name}-qr.png`;
|
||||
setQrCodeUrl(qrUrl);
|
||||
}
|
||||
const successMessage = isDuplicating
|
||||
? 'Asset duplicated successfully!'
|
||||
: 'Asset created successfully!';
|
||||
@ -292,6 +386,7 @@ const AssetDetail: React.FC = () => {
|
||||
doctype="Asset Type"
|
||||
value={formData.custom_asset_type || ''}
|
||||
onChange={(val) => setFormData({ ...formData, custom_asset_type: val })}
|
||||
disabled={!isEditing}
|
||||
/>
|
||||
|
||||
|
||||
@ -319,6 +414,7 @@ const AssetDetail: React.FC = () => {
|
||||
doctype="Modality"
|
||||
value={formData.custom_modality || ''}
|
||||
onChange={(val) => setFormData({ ...formData, custom_modality: val })}
|
||||
disabled={!isEditing}
|
||||
/>
|
||||
|
||||
|
||||
@ -420,8 +516,9 @@ const AssetDetail: React.FC = () => {
|
||||
<LinkField
|
||||
label="Manufacturer"
|
||||
doctype="Manufacturer"
|
||||
value={formData.manufacturer || ''}
|
||||
onChange={(val) => setFormData({ ...formData, manufacturer: val })}
|
||||
value={formData.custom_manufacturer || ''}
|
||||
onChange={(val) => setFormData({ ...formData, custom_manufacturer: val })}
|
||||
disabled={!isEditing}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@ -478,7 +575,13 @@ const AssetDetail: React.FC = () => {
|
||||
label="Hospital"
|
||||
doctype="Company"
|
||||
value={formData.company || ''}
|
||||
onChange={(val) => setFormData({ ...formData, company: val })}
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, company: val, department: '' }); // Clear department when company changes
|
||||
}}
|
||||
disabled={!isEditing}
|
||||
filters={{ domain: 'Healthcare' }}
|
||||
// onChange={(val) => setFormData({ ...formData, company: val })}
|
||||
// disabled={!isEditing}
|
||||
/>
|
||||
|
||||
{/* <div>
|
||||
@ -504,6 +607,16 @@ const AssetDetail: React.FC = () => {
|
||||
doctype="Department"
|
||||
value={formData.department || ''}
|
||||
onChange={(val) => setFormData({ ...formData, department: val })}
|
||||
disabled={!isEditing}
|
||||
filters={departmentFilters}
|
||||
/>
|
||||
|
||||
<LinkField
|
||||
label="Location"
|
||||
doctype="Location"
|
||||
value={formData.location || ''}
|
||||
onChange={(val) => setFormData({ ...formData, location: val })}
|
||||
disabled={!isEditing}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@ -604,10 +717,19 @@ const AssetDetail: React.FC = () => {
|
||||
Service Agreement
|
||||
</label>
|
||||
<select
|
||||
name="custom_service_agreement"
|
||||
value={formData.custom_service_agreement}
|
||||
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</option>
|
||||
<option value="">Select Service Agreement</option>
|
||||
<option value="Warranty">Warranty</option>
|
||||
<option value="Contract">Contract</option>
|
||||
<option value="Frame Work">Frame Work</option>
|
||||
<option value="Out of warranty">Out of warranty</option>
|
||||
<option value="Under Dismantle">Under Dismantle</option>
|
||||
<option value="Under Installation">Under Installation</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -616,10 +738,17 @@ const AssetDetail: React.FC = () => {
|
||||
Service Coverage
|
||||
</label>
|
||||
<select
|
||||
name="custom_service_coverage"
|
||||
value={formData.custom_service_coverage}
|
||||
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</option>
|
||||
<option value="">Select Service Coverage</option>
|
||||
<option value="PM Only">PM Only</option>
|
||||
<option value="Labour">Labour</option>
|
||||
<option value="Labour & Parts">Labour & Parts</option>
|
||||
<option value="Comprehensive">Comprehensive</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -688,7 +817,7 @@ const AssetDetail: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{/* <div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Supplier/Vendor
|
||||
</label>
|
||||
@ -698,18 +827,27 @@ const AssetDetail: React.FC = () => {
|
||||
>
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
</div> */}
|
||||
<LinkField
|
||||
label="Supplier/Vendor"
|
||||
doctype="Supplier"
|
||||
value={formData.supplier || ''}
|
||||
onChange={(val) => setFormData({ ...formData, supplier: val })}
|
||||
disabled={!isEditing}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Gross Purchase Amount
|
||||
</label>
|
||||
<select
|
||||
<input
|
||||
type="number"
|
||||
name="gross_purchase_amount"
|
||||
value={formData.gross_purchase_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"
|
||||
>
|
||||
<option value="">Price</option>
|
||||
</select>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -740,6 +878,11 @@ const AssetDetail: React.FC = () => {
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
name="available_for_use_date"
|
||||
value={formData.available_for_use_date || ''}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, available_for_use_date: e.target.value }))
|
||||
}
|
||||
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"
|
||||
/>
|
||||
@ -809,6 +952,29 @@ const AssetDetail: React.FC = () => {
|
||||
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="flex items-center mt-4">
|
||||
<input
|
||||
id="calculate_depreciation"
|
||||
type="checkbox"
|
||||
|
||||
checked={formData.calculate_depreciation}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
calculate_depreciation: e.target.checked,
|
||||
})
|
||||
}
|
||||
disabled={!isEditing}
|
||||
className="h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
/>
|
||||
<label
|
||||
htmlFor="calculate_depreciation"
|
||||
className="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Calculate Depreciation
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -868,12 +1034,22 @@ const AssetDetail: React.FC = () => {
|
||||
</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" />
|
||||
{qrCodeUrl ? (
|
||||
<img
|
||||
src={qrCodeUrl}
|
||||
alt="QR Code"
|
||||
className="w-40 h-40 object-contain"
|
||||
/>
|
||||
) : (
|
||||
<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
|
||||
|
||||
@ -24,6 +24,7 @@ interface UserDetails {
|
||||
creation: string;
|
||||
modified: string;
|
||||
language: string;
|
||||
custom_site_name:string;
|
||||
}
|
||||
|
||||
interface DocTypeRecord {
|
||||
|
||||
@ -26,6 +26,10 @@ export interface Asset {
|
||||
modified?: string;
|
||||
owner?: string;
|
||||
modified_by?: string;
|
||||
|
||||
calculate_depreciation?: boolean;
|
||||
gross_purchase_amount?: number;
|
||||
available_for_use_date?:string;
|
||||
}
|
||||
|
||||
export interface AssetListResponse {
|
||||
@ -82,6 +86,7 @@ export interface CreateAssetData {
|
||||
custom_attach_image?: string;
|
||||
custom_site_contractor?: string;
|
||||
custom_total_amount?: number;
|
||||
calculate_depreciation?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user