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 React, { useState, useEffect, useRef } from 'react';
|
||||||
import apiService from '../services/apiService'; // ✅ your ApiService
|
import apiService from '../services/apiService';
|
||||||
|
|
||||||
interface LinkFieldProps {
|
interface LinkFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
@ -8,6 +8,7 @@ interface LinkFieldProps {
|
|||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
filters?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LinkField: React.FC<LinkFieldProps> = ({
|
const LinkField: React.FC<LinkFieldProps> = ({
|
||||||
@ -17,29 +18,43 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
placeholder,
|
placeholder,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
filters = {},
|
||||||
}) => {
|
}) => {
|
||||||
const [searchResults, setSearchResults] = useState<{ value: string; description?: string }[]>([]);
|
const [searchResults, setSearchResults] = useState<{ value: string; description?: string }[]>([]);
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText, setSearchText] = useState('');
|
||||||
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Fetch link options from ERPNext
|
// Fetch link options from ERPNext with filters
|
||||||
const searchLink = async (text: string = '') => {
|
const searchLink = async (text: string = '') => {
|
||||||
try {
|
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 }[]>(
|
const response = await apiService.apiCall<{ value: string; description?: string }[]>(
|
||||||
`/api/method/frappe.desk.search.search_link?${params.toString()}`
|
`/api/method/frappe.desk.search.search_link?${params.toString()}`
|
||||||
);
|
);
|
||||||
setSearchResults(response || []);
|
setSearchResults(response || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching ${doctype} links:`, 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(() => {
|
useEffect(() => {
|
||||||
if (isDropdownOpen) searchLink('');
|
if (isDropdownOpen) {
|
||||||
}, [isDropdownOpen]);
|
searchLink(searchText || '');
|
||||||
|
}
|
||||||
|
}, [isDropdownOpen, filters]); // Re-fetch when filters change
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
// Close dropdown when clicking outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -54,7 +69,9 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="relative w-full mb-4">
|
<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
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@ -62,47 +79,8 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
placeholder={placeholder || `Select ${label}`}
|
placeholder={placeholder || `Select ${label}`}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md
|
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
|
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`}
|
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}
|
|
||||||
onFocus={() => !disabled && setDropdownOpen(true)}
|
onFocus={() => !disabled && setDropdownOpen(true)}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const text = e.target.value;
|
const text = e.target.value;
|
||||||
@ -110,17 +88,11 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
searchLink(text);
|
searchLink(text);
|
||||||
onChange(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 && (
|
{isDropdownOpen && searchResults.length > 0 && !disabled && (
|
||||||
<ul
|
<ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||||
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">
|
||||||
rounded-md mt-1 max-h-48 overflow-auto w-full shadow-lg"
|
|
||||||
>
|
|
||||||
{searchResults.map((item, idx) => (
|
{searchResults.map((item, idx) => (
|
||||||
<li
|
<li
|
||||||
key={idx}
|
key={idx}
|
||||||
@ -128,22 +100,137 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
onChange(item.value);
|
onChange(item.value);
|
||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
}}
|
}}
|
||||||
className={`px-3 py-2 hover:bg-blue-100 dark:hover:bg-gray-700 cursor-pointer
|
className={`px-3 py-2 cursor-pointer
|
||||||
${value === item.value ? 'bg-blue-50 dark:bg-gray-600 font-semibold' : ''}`}
|
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 && (
|
{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>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LinkField;
|
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 type { CreateAssetData } from '../services/assetService';
|
||||||
|
|
||||||
import LinkField from '../components/LinkField';
|
import LinkField from '../components/LinkField';
|
||||||
|
import apiService from '../services/apiService'; // ✅ your ApiService
|
||||||
|
|
||||||
|
|
||||||
const AssetDetail: React.FC = () => {
|
const AssetDetail: React.FC = () => {
|
||||||
@ -23,6 +24,10 @@ const AssetDetail: React.FC = () => {
|
|||||||
const { createAsset, updateAsset, loading: saving } = useAssetMutations();
|
const { createAsset, updateAsset, loading: saving } = useAssetMutations();
|
||||||
|
|
||||||
const [isEditing, setIsEditing] = useState(isNewAsset);
|
const [isEditing, setIsEditing] = useState(isNewAsset);
|
||||||
|
|
||||||
|
const [userSiteName, setUserSiteName] = useState('');
|
||||||
|
const [departmentFilters, setDepartmentFilters] = useState<Record<string, any>>({});
|
||||||
|
|
||||||
const [formData, setFormData] = useState<CreateAssetData>({
|
const [formData, setFormData] = useState<CreateAssetData>({
|
||||||
asset_name: '',
|
asset_name: '',
|
||||||
company: '',
|
company: '',
|
||||||
@ -41,9 +46,53 @@ const AssetDetail: React.FC = () => {
|
|||||||
custom_modality: '',
|
custom_modality: '',
|
||||||
custom_attach_image: '',
|
custom_attach_image: '',
|
||||||
custom_site_contractor: '',
|
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
|
// Load asset data for editing or duplicating
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
@ -65,11 +114,52 @@ const AssetDetail: React.FC = () => {
|
|||||||
custom_modality: asset.custom_modality || '',
|
custom_modality: asset.custom_modality || '',
|
||||||
custom_attach_image: asset.custom_attach_image || '',
|
custom_attach_image: asset.custom_attach_image || '',
|
||||||
custom_site_contractor: asset.custom_site_contractor || '',
|
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]);
|
}, [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 handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
@ -98,6 +188,10 @@ const AssetDetail: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
if (isNewAsset || isDuplicating) {
|
if (isNewAsset || isDuplicating) {
|
||||||
const newAsset = await createAsset(formData);
|
const newAsset = await createAsset(formData);
|
||||||
|
if (newAsset.name) {
|
||||||
|
const qrUrl = `/files/${newAsset.name}-qr.png`;
|
||||||
|
setQrCodeUrl(qrUrl);
|
||||||
|
}
|
||||||
const successMessage = isDuplicating
|
const successMessage = isDuplicating
|
||||||
? 'Asset duplicated successfully!'
|
? 'Asset duplicated successfully!'
|
||||||
: 'Asset created successfully!';
|
: 'Asset created successfully!';
|
||||||
@ -292,6 +386,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
doctype="Asset Type"
|
doctype="Asset Type"
|
||||||
value={formData.custom_asset_type || ''}
|
value={formData.custom_asset_type || ''}
|
||||||
onChange={(val) => setFormData({ ...formData, custom_asset_type: val })}
|
onChange={(val) => setFormData({ ...formData, custom_asset_type: val })}
|
||||||
|
disabled={!isEditing}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
@ -319,6 +414,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
doctype="Modality"
|
doctype="Modality"
|
||||||
value={formData.custom_modality || ''}
|
value={formData.custom_modality || ''}
|
||||||
onChange={(val) => setFormData({ ...formData, custom_modality: val })}
|
onChange={(val) => setFormData({ ...formData, custom_modality: val })}
|
||||||
|
disabled={!isEditing}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
@ -420,8 +516,9 @@ const AssetDetail: React.FC = () => {
|
|||||||
<LinkField
|
<LinkField
|
||||||
label="Manufacturer"
|
label="Manufacturer"
|
||||||
doctype="Manufacturer"
|
doctype="Manufacturer"
|
||||||
value={formData.manufacturer || ''}
|
value={formData.custom_manufacturer || ''}
|
||||||
onChange={(val) => setFormData({ ...formData, manufacturer: val })}
|
onChange={(val) => setFormData({ ...formData, custom_manufacturer: val })}
|
||||||
|
disabled={!isEditing}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -478,7 +575,13 @@ const AssetDetail: React.FC = () => {
|
|||||||
label="Hospital"
|
label="Hospital"
|
||||||
doctype="Company"
|
doctype="Company"
|
||||||
value={formData.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>
|
{/* <div>
|
||||||
@ -504,6 +607,16 @@ const AssetDetail: React.FC = () => {
|
|||||||
doctype="Department"
|
doctype="Department"
|
||||||
value={formData.department || ''}
|
value={formData.department || ''}
|
||||||
onChange={(val) => setFormData({ ...formData, department: val })}
|
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>
|
<div>
|
||||||
@ -604,10 +717,19 @@ const AssetDetail: React.FC = () => {
|
|||||||
Service Agreement
|
Service Agreement
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
name="custom_service_agreement"
|
||||||
|
value={formData.custom_service_agreement}
|
||||||
|
onChange={handleChange}
|
||||||
disabled={!isEditing}
|
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"
|
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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -616,10 +738,17 @@ const AssetDetail: React.FC = () => {
|
|||||||
Service Coverage
|
Service Coverage
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
name="custom_service_coverage"
|
||||||
|
value={formData.custom_service_coverage}
|
||||||
|
onChange={handleChange}
|
||||||
disabled={!isEditing}
|
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"
|
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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -688,7 +817,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* <div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
Supplier/Vendor
|
Supplier/Vendor
|
||||||
</label>
|
</label>
|
||||||
@ -698,18 +827,27 @@ const AssetDetail: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<option value="">Select</option>
|
<option value="">Select</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div> */}
|
||||||
|
<LinkField
|
||||||
|
label="Supplier/Vendor"
|
||||||
|
doctype="Supplier"
|
||||||
|
value={formData.supplier || ''}
|
||||||
|
onChange={(val) => setFormData({ ...formData, supplier: val })}
|
||||||
|
disabled={!isEditing}
|
||||||
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
Gross Purchase Amount
|
Gross Purchase Amount
|
||||||
</label>
|
</label>
|
||||||
<select
|
<input
|
||||||
|
type="number"
|
||||||
|
name="gross_purchase_amount"
|
||||||
|
value={formData.gross_purchase_amount}
|
||||||
|
onChange={handleChange}
|
||||||
disabled={!isEditing}
|
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"
|
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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -740,6 +878,11 @@ const AssetDetail: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
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}
|
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"
|
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"
|
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 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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -868,12 +1034,22 @@ const AssetDetail: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* QR Code */}
|
{/* QR Code */}
|
||||||
|
|
||||||
<div className="flex justify-center my-6">
|
<div className="flex justify-center my-6">
|
||||||
<div className="border-2 border-gray-300 dark:border-gray-600 p-4 rounded-lg">
|
<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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
Description
|
Description
|
||||||
|
|||||||
@ -24,6 +24,7 @@ interface UserDetails {
|
|||||||
creation: string;
|
creation: string;
|
||||||
modified: string;
|
modified: string;
|
||||||
language: string;
|
language: string;
|
||||||
|
custom_site_name:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DocTypeRecord {
|
interface DocTypeRecord {
|
||||||
|
|||||||
@ -26,6 +26,10 @@ export interface Asset {
|
|||||||
modified?: string;
|
modified?: string;
|
||||||
owner?: string;
|
owner?: string;
|
||||||
modified_by?: string;
|
modified_by?: string;
|
||||||
|
|
||||||
|
calculate_depreciation?: boolean;
|
||||||
|
gross_purchase_amount?: number;
|
||||||
|
available_for_use_date?:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AssetListResponse {
|
export interface AssetListResponse {
|
||||||
@ -82,6 +86,7 @@ export interface CreateAssetData {
|
|||||||
custom_attach_image?: string;
|
custom_attach_image?: string;
|
||||||
custom_site_contractor?: string;
|
custom_site_contractor?: string;
|
||||||
custom_total_amount?: number;
|
custom_total_amount?: number;
|
||||||
|
calculate_depreciation?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user