Added filters and buttons for print QR
This commit is contained in:
parent
9cf1f3201b
commit
d4478bdacc
@ -9,6 +9,7 @@ interface LinkFieldProps {
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
filters?: Record<string, any>;
|
||||
compact?: boolean; // ✅ Add this prop
|
||||
}
|
||||
|
||||
const LinkField: React.FC<LinkFieldProps> = ({
|
||||
@ -19,6 +20,7 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
placeholder,
|
||||
disabled = false,
|
||||
filters = {},
|
||||
compact = false, // ✅ Default to false
|
||||
}) => {
|
||||
const [searchResults, setSearchResults] = useState<{ value: string; description?: string }[]>([]);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
@ -35,7 +37,6 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
@ -54,15 +55,13 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
if (isDropdownOpen) {
|
||||
searchLink(searchText || '');
|
||||
}
|
||||
}, [isDropdownOpen, filters]); // Re-fetch when filters change
|
||||
}, [isDropdownOpen, filters]);
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||
setDropdownOpen(false);
|
||||
|
||||
// Reset search text to current value when closing
|
||||
setSearchText('');
|
||||
}
|
||||
};
|
||||
@ -85,8 +84,8 @@ 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">
|
||||
<div ref={containerRef} className={`relative w-full ${compact ? 'mb-2' : 'mb-4'}`}>
|
||||
<label className={`block font-medium text-gray-700 dark:text-gray-300 ${compact ? 'text-[10px] mb-0.5' : 'text-sm mb-1'}`}>
|
||||
{label}
|
||||
</label>
|
||||
|
||||
@ -96,9 +95,14 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
value={isDropdownOpen ? searchText : 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 ${value ? 'pr-8' : ''}`}
|
||||
className={`w-full border border-gray-300 dark:border-gray-600 rounded-md
|
||||
focus:outline-none disabled:bg-gray-100 dark:disabled:bg-gray-700
|
||||
bg-white dark:bg-gray-700 text-gray-900 dark:text-white
|
||||
${compact
|
||||
? 'px-2 py-1 text-xs focus:ring-1 focus:ring-blue-500 rounded'
|
||||
: 'px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500'
|
||||
}
|
||||
${value ? (compact ? 'pr-5' : 'pr-8') : ''}`}
|
||||
onFocus={() => {
|
||||
if (!disabled) {
|
||||
setDropdownOpen(true);
|
||||
@ -112,12 +116,13 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Clear button - only show when there's a selected value and not disabled */}
|
||||
{/* Clear button */}
|
||||
{value && !disabled && !isDropdownOpen && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
className={`absolute top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300
|
||||
${compact ? 'right-1 text-xs' : 'right-2 text-sm'}`}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
@ -125,24 +130,22 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
</div>
|
||||
|
||||
{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">
|
||||
<ul className={`absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md overflow-auto w-full shadow-lg
|
||||
${compact ? 'mt-0.5 max-h-36' : 'mt-1 max-h-48'}`}>
|
||||
{searchResults.map((item, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
// onClick={() => {
|
||||
// onChange(item.value);
|
||||
// setDropdownOpen(false);
|
||||
// }}
|
||||
onClick={() => handleSelect(item.value)}
|
||||
className={`px-3 py-2 cursor-pointer
|
||||
text-gray-900 dark:text-gray-100
|
||||
className={`cursor-pointer text-gray-900 dark:text-gray-100
|
||||
hover:bg-blue-500 dark:hover:bg-blue-600 hover:text-white
|
||||
${compact ? 'px-2 py-1 text-xs' : 'px-3 py-2 text-sm'}
|
||||
${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">
|
||||
<span className={`text-gray-600 dark:text-gray-300 ml-2
|
||||
${compact ? 'text-[9px] ml-1' : 'text-xs ml-2'}`}>
|
||||
{item.description}
|
||||
</span>
|
||||
)}
|
||||
@ -153,8 +156,9 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
||||
|
||||
{/* 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">
|
||||
<div className={`absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||
rounded-md w-full shadow-lg text-center text-gray-500 dark:text-gray-400
|
||||
${compact ? 'mt-0.5 p-1.5 text-[10px]' : 'mt-1 p-3 text-sm'}`}>
|
||||
No results found
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -377,6 +377,129 @@ const AssetDetail: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const handlePrintQR = () => {
|
||||
if (!qrCodeUrl || !asset) return;
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) return;
|
||||
|
||||
printWindow.document.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Print QR Code - ${asset.name}</title>
|
||||
<style>
|
||||
@page {
|
||||
margin: 0.5in;
|
||||
size: auto;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
}
|
||||
.qr-container {
|
||||
text-align: center;
|
||||
border: 2px solid #333;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
max-width: 400px;
|
||||
}
|
||||
.qr-image {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 15px 0;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.asset-info {
|
||||
margin: 10px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="qr-container">
|
||||
<h1>Asset QR Code</h1>
|
||||
<div class="asset-info">
|
||||
<strong>Asset ID:</strong> ${asset.name}<br/>
|
||||
<strong>Asset Name:</strong> ${asset.asset_name || 'N/A'}
|
||||
</div>
|
||||
<img src="${qrCodeUrl}" alt="QR Code" class="qr-image" onload="window.print();" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
};
|
||||
|
||||
const handlePrintStayPlugged = () => {
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) return;
|
||||
|
||||
printWindow.document.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stay Plugged</title>
|
||||
<style>
|
||||
@page {
|
||||
margin: 0.5in;
|
||||
size: auto;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
}
|
||||
.image-container {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.stay-plugged-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="image-container">
|
||||
<img src="/files/Stay Plugged.jpg" alt="Stay Plugged" class="stay-plugged-image" onload="window.print();" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-6">
|
||||
{/* Header */}
|
||||
@ -406,6 +529,24 @@ const AssetDetail: React.FC = () => {
|
||||
<div className="flex items-center gap-3">
|
||||
{!isNewAsset && !isEditing && !isCancelled && (
|
||||
<>
|
||||
{/* Print QR Button - Only show if QR code exists */}
|
||||
{qrCodeUrl && (
|
||||
<button
|
||||
onClick={handlePrintQR}
|
||||
className="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg flex items-center gap-2"
|
||||
>
|
||||
<FaQrcode />
|
||||
Print QR
|
||||
</button>
|
||||
)}
|
||||
{/* Stay Plugged Button */}
|
||||
<button
|
||||
onClick={handlePrintStayPlugged}
|
||||
className="bg-purple-500 hover:bg-purple-700 text-white px-6 py-2 rounded-lg flex items-center gap-2"
|
||||
>
|
||||
{/* <FaQrcode /> */}
|
||||
Stay Plugged
|
||||
</button>
|
||||
<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"
|
||||
|
||||
@ -143,7 +143,18 @@ const AssetList: React.FC = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
// Helper function to format date
|
||||
const formatDate = (dateString?: string) => {
|
||||
if (!dateString) return '-';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
@ -364,22 +375,22 @@ const AssetList: React.FC = () => {
|
||||
</div> */}
|
||||
|
||||
{/* Filter Section */}
|
||||
<div className="mb-6 bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Filters</h3>
|
||||
<div className="mb-3 bg-white dark:bg-gray-800 rounded-lg shadow p-2">
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<h3 className="text-[11px] font-semibold text-gray-700 dark:text-gray-300">Filters</h3>
|
||||
{hasActiveFilters && (
|
||||
<button
|
||||
onClick={handleClearFilters}
|
||||
className="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 flex items-center gap-1"
|
||||
className="text-[10px] text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 flex items-center gap-0.5"
|
||||
>
|
||||
<FaTimes />
|
||||
<FaTimes className="text-[10px]" />
|
||||
Clear All Filters
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* First Row - 5 filters */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-2 mb-2">
|
||||
{/* Asset ID Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
@ -389,6 +400,7 @@ const AssetList: React.FC = () => {
|
||||
onChange={(val) => setFilterAssetId(val)}
|
||||
placeholder="Select Asset ID"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -401,9 +413,59 @@ const AssetList: React.FC = () => {
|
||||
onChange={(val) => setFilterCompany(val)}
|
||||
placeholder="Select Hospital"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
filters={{ domain: 'Healthcare' }}
|
||||
/>
|
||||
</div>
|
||||
{/* Asset Name Filter - Text Input */}
|
||||
<div>
|
||||
<label className="block text-[10px] font-medium text-gray-700 dark:text-gray-300 mb-0.5">
|
||||
Asset Name
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={tempAssetName}
|
||||
onChange={(e) => handleAssetNameChange(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyPress(e, 'assetName')}
|
||||
placeholder="Type to search..."
|
||||
className="w-full px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
/>
|
||||
{tempAssetName && tempAssetName !== filterAssetName && (
|
||||
<span className="absolute right-1.5 top-1 text-[9px] text-gray-400">
|
||||
typing...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-[9px] text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
Press Enter or wait
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Serial Number Filter - Text Input */}
|
||||
<div>
|
||||
<label className="block text-[10px] font-medium text-gray-700 dark:text-gray-300 mb-0.5">
|
||||
Serial Number
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={tempSerialNumber}
|
||||
onChange={(e) => handleSerialNumberChange(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyPress(e, 'serialNumber')}
|
||||
placeholder="Type to search..."
|
||||
className="w-full px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
/>
|
||||
{tempSerialNumber && tempSerialNumber !== filterSerialNumber && (
|
||||
<span className="absolute right-1.5 top-1 text-[9px] text-gray-400">
|
||||
typing...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-[9px] text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
Press Enter or wait
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Location Filter */}
|
||||
<div>
|
||||
@ -414,36 +476,13 @@ const AssetList: React.FC = () => {
|
||||
onChange={(val) => setFilterLocation(val)}
|
||||
placeholder="Select Location"
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Department Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
label="Department"
|
||||
doctype="Department"
|
||||
value={filterDepartment}
|
||||
onChange={(val) => setFilterDepartment(val)}
|
||||
placeholder="Select Department"
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modality Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
label="Modality"
|
||||
doctype="Modality"
|
||||
value={filterModality}
|
||||
onChange={(val) => setFilterModality(val)}
|
||||
placeholder="Select Modality"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Second Row - 5 filters */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-2">
|
||||
{/* Manufacturer Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
@ -453,6 +492,7 @@ const AssetList: React.FC = () => {
|
||||
onChange={(val) => setFilterManufacturer(val)}
|
||||
placeholder="Select Manufacturer"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -465,18 +505,45 @@ const AssetList: React.FC = () => {
|
||||
onChange={(val) => setFilterSupplier(val)}
|
||||
placeholder="Select Supplier"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Department Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
label="Department"
|
||||
doctype="Department"
|
||||
value={filterDepartment}
|
||||
onChange={(val) => setFilterDepartment(val)}
|
||||
placeholder="Select Department"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modality Filter */}
|
||||
<div>
|
||||
<LinkField
|
||||
label="Modality"
|
||||
doctype="Modality"
|
||||
value={filterModality}
|
||||
onChange={(val) => setFilterModality(val)}
|
||||
placeholder="Select Modality"
|
||||
disabled={false}
|
||||
compact={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Device Status Filter - Dropdown */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<label className="block text-[10px] font-medium text-gray-700 dark:text-gray-300 mb-0.5">
|
||||
Device Status
|
||||
</label>
|
||||
<select
|
||||
value={filterDeviceStatus}
|
||||
onChange={(e) => setFilterDeviceStatus(e.target.value)}
|
||||
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 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
className="w-full px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
>
|
||||
<option value="">All Status</option>
|
||||
<option value="Up">Up</option>
|
||||
@ -484,196 +551,135 @@ const AssetList: React.FC = () => {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Asset Name Filter - Text Input */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Asset Name
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={tempAssetName}
|
||||
onChange={(e) => handleAssetNameChange(e.target.value)}
|
||||
// onKeyPress={(e) => handleKeyPress(e, 'assetName')}
|
||||
onKeyDown={(e) => handleKeyPress(e, 'assetName')}
|
||||
placeholder="Type to search..."
|
||||
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 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
/>
|
||||
{tempAssetName && tempAssetName !== filterAssetName && (
|
||||
<span className="absolute right-2 top-2 text-xs text-gray-400">
|
||||
typing...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Press Enter or wait to apply
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Serial Number Filter - Text Input */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Serial Number
|
||||
</label>
|
||||
{/* <input
|
||||
type="text"
|
||||
value={filterSerialNumber}
|
||||
onChange={(e) => setFilterSerialNumber(e.target.value)}
|
||||
placeholder="Enter serial number"
|
||||
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 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
/> */}
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={tempSerialNumber}
|
||||
onChange={(e) => handleSerialNumberChange(e.target.value)}
|
||||
// onKeyPress={(e) => handleKeyPress(e, 'serialNumber')}
|
||||
onKeyDown={(e) => handleKeyPress(e, 'serialNumber')}
|
||||
placeholder="Type to search..."
|
||||
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 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
||||
/>
|
||||
{tempSerialNumber && tempSerialNumber !== filterSerialNumber && (
|
||||
<span className="absolute right-2 top-2 text-xs text-gray-400">
|
||||
typing...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Press Enter or wait to apply
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Active Filters Display */}
|
||||
{hasActiveFilters && (
|
||||
<div className="mt-4 flex flex-wrap gap-2">
|
||||
<div className="mt-2 flex flex-wrap gap-1">
|
||||
{filterAssetId && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full text-[10px]">
|
||||
Asset ID: {filterAssetId}
|
||||
<button
|
||||
onClick={() => setFilterAssetId('')}
|
||||
className="hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterCompany && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full text-[10px]">
|
||||
Hospital: {filterCompany}
|
||||
<button
|
||||
onClick={() => setFilterCompany('')}
|
||||
className="hover:text-green-600 dark:hover:text-green-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterLocation && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full text-[10px]">
|
||||
Location: {filterLocation}
|
||||
<button
|
||||
onClick={() => setFilterLocation('')}
|
||||
className="hover:text-purple-600 dark:hover:text-purple-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterDepartment && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 rounded-full text-[10px]">
|
||||
Department: {filterDepartment}
|
||||
<button
|
||||
onClick={() => setFilterDepartment('')}
|
||||
className="hover:text-yellow-600 dark:hover:text-yellow-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterModality && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-pink-100 dark:bg-pink-900 text-pink-800 dark:text-pink-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-pink-100 dark:bg-pink-900 text-pink-800 dark:text-pink-200 rounded-full text-[10px]">
|
||||
Modality: {filterModality}
|
||||
<button
|
||||
onClick={() => setFilterModality('')}
|
||||
className="hover:text-pink-600 dark:hover:text-pink-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterManufacturer && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 rounded-full text-[10px]">
|
||||
Manufacturer: {filterManufacturer}
|
||||
<button
|
||||
onClick={() => setFilterManufacturer('')}
|
||||
className="hover:text-indigo-600 dark:hover:text-indigo-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterSupplier && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-teal-100 dark:bg-teal-900 text-teal-800 dark:text-teal-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-teal-100 dark:bg-teal-900 text-teal-800 dark:text-teal-200 rounded-full text-[10px]">
|
||||
Supplier: {filterSupplier}
|
||||
<button
|
||||
onClick={() => setFilterSupplier('')}
|
||||
className="hover:text-teal-600 dark:hover:text-teal-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterDeviceStatus && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-200 rounded-full text-[10px]">
|
||||
Status: {filterDeviceStatus}
|
||||
<button
|
||||
onClick={() => setFilterDeviceStatus('')}
|
||||
className="hover:text-orange-600 dark:hover:text-orange-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterAssetName && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-cyan-100 dark:bg-cyan-900 text-cyan-800 dark:text-cyan-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-cyan-100 dark:bg-cyan-900 text-cyan-800 dark:text-cyan-200 rounded-full text-[10px]">
|
||||
Asset Name: "{filterAssetName}"
|
||||
<button
|
||||
// onClick={() => setFilterAssetName('')}
|
||||
onClick={() => {
|
||||
setFilterAssetName('');
|
||||
setTempAssetName('');
|
||||
}}
|
||||
className="hover:text-cyan-600 dark:hover:text-cyan-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{filterSerialNumber && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-lime-100 dark:bg-lime-900 text-lime-800 dark:text-lime-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-lime-100 dark:bg-lime-900 text-lime-800 dark:text-lime-200 rounded-full text-[10px]">
|
||||
Serial: "{filterSerialNumber}"
|
||||
<button
|
||||
// onClick={() => setFilterSerialNumber('')}
|
||||
onClick={() => {
|
||||
setFilterSerialNumber('');
|
||||
setTempSerialNumber('');
|
||||
}}
|
||||
className="hover:text-lime-600 dark:hover:text-lime-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{searchTerm && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full text-sm">
|
||||
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full text-[10px]">
|
||||
Search: "{searchTerm}"
|
||||
<button
|
||||
onClick={() => setSearchTerm('')}
|
||||
className="hover:text-purple-600 dark:hover:text-purple-400"
|
||||
>
|
||||
<FaTimes className="text-xs" />
|
||||
<FaTimes className="text-[9px]" />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
@ -691,7 +697,7 @@ const AssetList: React.FC = () => {
|
||||
Asset Name
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Serial Number
|
||||
Serial No
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Company
|
||||
@ -702,6 +708,9 @@ const AssetList: React.FC = () => {
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Updated on
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
@ -710,7 +719,7 @@ const AssetList: React.FC = () => {
|
||||
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{assets.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
|
||||
<td colSpan={7} className="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
|
||||
<div className="flex flex-col items-center">
|
||||
<FaSearch className="text-4xl text-gray-300 dark:text-gray-600 mb-2" />
|
||||
<p>No assets found</p>
|
||||
@ -772,6 +781,9 @@ const AssetList: React.FC = () => {
|
||||
{asset.custom_device_status || 'Unknown'}
|
||||
</span> */}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300">
|
||||
{formatDate(asset.modified)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
|
||||
<button
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user