Added the Asset Filters in List view
This commit is contained in:
parent
c3a42d67b5
commit
9cf1f3201b
@ -61,34 +61,68 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
|
|
||||||
|
// Reset search text to current value when closing
|
||||||
|
setSearchText('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Handle selecting an item from dropdown
|
||||||
|
const handleSelect = (selectedValue: string) => {
|
||||||
|
onChange(selectedValue);
|
||||||
|
setSearchText('');
|
||||||
|
setDropdownOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle clearing the field
|
||||||
|
const handleClear = () => {
|
||||||
|
onChange('');
|
||||||
|
setSearchText('');
|
||||||
|
setDropdownOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
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 className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input
|
<div className="relative">
|
||||||
type="text"
|
<input
|
||||||
value={value}
|
type="text"
|
||||||
placeholder={placeholder || `Select ${label}`}
|
value={isDropdownOpen ? searchText : value}
|
||||||
disabled={disabled}
|
placeholder={placeholder || `Select ${label}`}
|
||||||
className={`w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md
|
disabled={disabled}
|
||||||
focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700
|
className={`w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md
|
||||||
bg-white dark:bg-gray-700 text-gray-900 dark:text-white`}
|
focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700
|
||||||
onFocus={() => !disabled && setDropdownOpen(true)}
|
bg-white dark:bg-gray-700 text-gray-900 dark:text-white ${value ? 'pr-8' : ''}`}
|
||||||
onChange={(e) => {
|
onFocus={() => {
|
||||||
const text = e.target.value;
|
if (!disabled) {
|
||||||
setSearchText(text);
|
setDropdownOpen(true);
|
||||||
searchLink(text);
|
setSearchText('');
|
||||||
onChange(text);
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
onChange={(e) => {
|
||||||
|
const text = e.target.value;
|
||||||
|
setSearchText(text);
|
||||||
|
searchLink(text);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Clear button - only show when there's a selected value and not disabled */}
|
||||||
|
{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"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{isDropdownOpen && searchResults.length > 0 && !disabled && (
|
{isDropdownOpen && searchResults.length > 0 && !disabled && (
|
||||||
<ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
<ul className="absolute z-50 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600
|
||||||
@ -96,10 +130,11 @@ const LinkField: React.FC<LinkFieldProps> = ({
|
|||||||
{searchResults.map((item, idx) => (
|
{searchResults.map((item, idx) => (
|
||||||
<li
|
<li
|
||||||
key={idx}
|
key={idx}
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
onChange(item.value);
|
// onChange(item.value);
|
||||||
setDropdownOpen(false);
|
// setDropdownOpen(false);
|
||||||
}}
|
// }}
|
||||||
|
onClick={() => handleSelect(item.value)}
|
||||||
className={`px-3 py-2 cursor-pointer
|
className={`px-3 py-2 cursor-pointer
|
||||||
text-gray-900 dark:text-gray-100
|
text-gray-900 dark:text-gray-100
|
||||||
hover:bg-blue-500 dark:hover:bg-blue-600 hover:text-white
|
hover:bg-blue-500 dark:hover:bg-blue-600 hover:text-white
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
|
|||||||
import { useAssetDetails, useAssetMutations } from '../hooks/useAsset';
|
import { useAssetDetails, useAssetMutations } from '../hooks/useAsset';
|
||||||
import { useDocTypeMeta } from '../hooks/useDocTypeMeta';
|
import { useDocTypeMeta } from '../hooks/useDocTypeMeta';
|
||||||
import { FaArrowLeft, FaSave, FaEdit, FaQrcode, FaCheck } from 'react-icons/fa';
|
import { FaArrowLeft, FaSave, FaEdit, FaQrcode, FaCheck } from 'react-icons/fa';
|
||||||
import type { CreateAssetData } from '../services/assetService';
|
import type { CreateAssetData,AssetFinanceBookRow } from '../services/assetService';
|
||||||
|
|
||||||
import LinkField from '../components/LinkField';
|
import LinkField from '../components/LinkField';
|
||||||
import apiService from '../services/apiService';
|
import apiService from '../services/apiService';
|
||||||
@ -535,6 +535,22 @@ const AssetDetail: React.FC = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
Device Status <span className="text-red-500">*</span>
|
||||||
|
</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 class</option> */}
|
||||||
|
<option value="Up">Up</option>
|
||||||
|
<option value="Down">Down</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -710,7 +726,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
More Details
|
More Details
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
{/* <div>
|
||||||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
|
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
Condition
|
Condition
|
||||||
</label>
|
</label>
|
||||||
@ -726,7 +742,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
<option value="Under Maintenance">Under Maintenance</option>
|
<option value="Under Maintenance">Under Maintenance</option>
|
||||||
<option value="Decommissioned">Decommissioned</option>
|
<option value="Decommissioned">Decommissioned</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
{/* QR Code */}
|
{/* QR Code */}
|
||||||
<div className="flex flex-col items-center my-4">
|
<div className="flex flex-col items-center my-4">
|
||||||
@ -1018,14 +1034,206 @@ const AssetDetail: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Financial Details - Full Width */}
|
|
||||||
|
{/* Updated Financial Details */}
|
||||||
<div className="mt-6">
|
<div className="mt-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">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="flex items-center mb-6">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{/* Asset Finance Book child table — shown only when checkbox checked */}
|
||||||
|
{formData.calculate_depreciation && (
|
||||||
|
<div className="border-t pt-4">
|
||||||
|
{/* Header with Add Row button */}
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h3 className="text-md font-semibold text-gray-800 dark:text-white">
|
||||||
|
Asset Finance Books
|
||||||
|
</h3>
|
||||||
|
{isEditing && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addFinanceRow}
|
||||||
|
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span>+</span> Add Row
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Show message if no rows */}
|
||||||
|
{(!formData.finance_books || formData.finance_books.length === 0) && (
|
||||||
|
<div className="text-center py-8 text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||||
|
No finance books added yet. Click "Add Row" to add one.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* TABLE - Full width desktop view with overflow fix */}
|
||||||
|
{formData.finance_books && formData.finance_books.length > 0 && (
|
||||||
|
<div className="overflow-visible">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-100 dark:bg-gray-700 border-b border-gray-300 dark:border-gray-600">
|
||||||
|
<th className="text-left px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[200px]">
|
||||||
|
Finance Book
|
||||||
|
</th>
|
||||||
|
<th className="text-left px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[200px]">
|
||||||
|
Depreciation Method*
|
||||||
|
</th>
|
||||||
|
<th className="text-left px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[180px]">
|
||||||
|
Total Depreciations*
|
||||||
|
</th>
|
||||||
|
<th className="text-left px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[180px]">
|
||||||
|
Frequency (Months)*
|
||||||
|
</th>
|
||||||
|
<th className="text-left px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[200px]">
|
||||||
|
Depreciation Posting Date*
|
||||||
|
</th>
|
||||||
|
{isEditing && (
|
||||||
|
<th className="text-center px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-300 min-w-[120px]">
|
||||||
|
Action
|
||||||
|
</th>
|
||||||
|
)}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{formData.finance_books.map((row: AssetFinanceBookRow, idx: number) => (
|
||||||
|
<tr
|
||||||
|
key={idx}
|
||||||
|
className="border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-750"
|
||||||
|
>
|
||||||
|
{/* Finance Book - with overflow visible */}
|
||||||
|
<td className="px-4 py-3 relative" style={{ overflow: 'visible' }}>
|
||||||
|
<div className="relative z-20">
|
||||||
|
<LinkField
|
||||||
|
label=""
|
||||||
|
doctype="Finance Book"
|
||||||
|
value={row.finance_book || ''}
|
||||||
|
onChange={(val) => updateFinanceRow(idx, { finance_book: val })}
|
||||||
|
disabled={!isEditing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Depreciation Method */}
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<select
|
||||||
|
value={row.depreciation_method || ''}
|
||||||
|
onChange={(e) => updateFinanceRow(idx, { depreciation_method: 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"
|
||||||
|
>
|
||||||
|
<option value="">Select Method</option>
|
||||||
|
<option value="Straight Line">Straight Line</option>
|
||||||
|
<option value="Double Declining Balance">Double Declining Balance</option>
|
||||||
|
<option value="Written Down Value">Written Down Value</option>
|
||||||
|
<option value="Manual">Manual</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Total Depreciations */}
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={row.total_number_of_depreciations ?? ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateFinanceRow(idx, {
|
||||||
|
total_number_of_depreciations: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={!isEditing}
|
||||||
|
placeholder="0"
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Frequency */}
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={row.frequency_of_depreciation ?? ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateFinanceRow(idx, {
|
||||||
|
frequency_of_depreciation: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={!isEditing}
|
||||||
|
placeholder="0"
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Start Date */}
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={row.depreciation_start_date || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateFinanceRow(idx, { depreciation_start_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"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* REMOVE BUTTON */}
|
||||||
|
{isEditing && (
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => removeFinanceRow(idx)}
|
||||||
|
className="px-3 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{/* Financial Details - Full Width */}
|
||||||
|
{/* <div className="mt-6">
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||||||
<h2 className="text-base font-semibold text-gray-800 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
<h2 className="text-base font-semibold text-gray-800 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
|
||||||
Financial Details
|
Financial Details
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* Financial Input Fields */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
|
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
@ -1081,7 +1289,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Calculate Depreciation Checkbox */}
|
|
||||||
<div className="flex items-center mb-6">
|
<div className="flex items-center mb-6">
|
||||||
<input
|
<input
|
||||||
id="calculate_depreciation"
|
id="calculate_depreciation"
|
||||||
@ -1104,7 +1312,6 @@ const AssetDetail: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Depreciation Schedule Table */}
|
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
|
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
|
||||||
<thead>
|
<thead>
|
||||||
@ -1127,7 +1334,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{/* Sample rows - Replace with actual data from API */}
|
|
||||||
<tr className="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
<tr className="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
||||||
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2 text-xs text-gray-700 dark:text-gray-300">
|
<td className="border border-gray-300 dark:border-gray-600 px-4 py-2 text-xs text-gray-700 dark:text-gray-300">
|
||||||
Year 1
|
Year 1
|
||||||
@ -1188,7 +1395,7 @@ const AssetDetail: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useAssets, useAssetMutations } from '../hooks/useAsset';
|
import { useAssets, useAssetMutations } from '../hooks/useAsset';
|
||||||
import { FaPlus, FaSearch, FaEdit, FaEye, FaTrash, FaCopy, FaEllipsisV, FaDownload, FaPrint, FaFileExport } from 'react-icons/fa';
|
import { FaPlus, FaSearch, FaEdit, FaEye, FaTrash, FaCopy, FaEllipsisV, FaDownload, FaPrint, FaFileExport, FaTimes } from 'react-icons/fa';
|
||||||
|
import LinkField from '../components/LinkField';
|
||||||
|
|
||||||
const AssetList: React.FC = () => {
|
const AssetList: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -12,8 +13,65 @@ const AssetList: React.FC = () => {
|
|||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
const limit = 20;
|
const limit = 20;
|
||||||
|
|
||||||
|
// Filter states
|
||||||
|
const [filterAssetId, setFilterAssetId] = useState('');
|
||||||
|
const [filterCompany, setFilterCompany] = useState('');
|
||||||
|
const [filterManufacturer, setFilterManufacturer] = useState('');
|
||||||
|
const [filterSupplier, setFilterSupplier] = useState('');
|
||||||
|
const [filterLocation, setFilterLocation] = useState('');
|
||||||
|
const [filterDepartment, setFilterDepartment] = useState('');
|
||||||
|
const [filterModality, setFilterModality] = useState('');
|
||||||
|
const [filterDeviceStatus, setFilterDeviceStatus] = useState('');
|
||||||
|
const [filterAssetName, setFilterAssetName] = useState('');
|
||||||
|
const [filterSerialNumber, setFilterSerialNumber] = useState('');
|
||||||
|
|
||||||
|
// Temporary states for text inputs (not applied until user stops typing or presses Enter)
|
||||||
|
const [tempAssetName, setTempAssetName] = useState('');
|
||||||
|
const [tempSerialNumber, setTempSerialNumber] = useState('');
|
||||||
|
|
||||||
|
// Debounce timer refs
|
||||||
|
const assetNameDebounceRef = useRef<number | null>(null);
|
||||||
|
const serialNumberDebounceRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
// Build filters object
|
||||||
|
const filters: Record<string, any> = {};
|
||||||
|
if (filterAssetId) {
|
||||||
|
filters['name'] = filterAssetId;
|
||||||
|
}
|
||||||
|
if (filterCompany) {
|
||||||
|
filters['company'] = filterCompany;
|
||||||
|
}
|
||||||
|
if (filterManufacturer) {
|
||||||
|
filters['custom_manufacturer'] = filterManufacturer;
|
||||||
|
}
|
||||||
|
if (filterSupplier) {
|
||||||
|
filters['supplier'] = filterSupplier;
|
||||||
|
}
|
||||||
|
if (filterLocation) {
|
||||||
|
filters['location'] = filterLocation;
|
||||||
|
}
|
||||||
|
if (filterDepartment) {
|
||||||
|
filters['department'] = filterDepartment;
|
||||||
|
}
|
||||||
|
if (filterModality) {
|
||||||
|
filters['custom_modality'] = filterModality;
|
||||||
|
}
|
||||||
|
if (filterDeviceStatus) {
|
||||||
|
filters['custom_device_status'] = filterDeviceStatus;
|
||||||
|
}
|
||||||
|
if (filterAssetName) {
|
||||||
|
filters['asset_name'] = ['like', `%${filterAssetName}%`];
|
||||||
|
}
|
||||||
|
if (filterSerialNumber) {
|
||||||
|
filters['custom_serial_number'] = ['like', `%${filterSerialNumber}%`];
|
||||||
|
}
|
||||||
|
if (searchTerm) {
|
||||||
|
// Search across multiple fields
|
||||||
|
filters['asset_name'] = ['like', `%${searchTerm}%`];
|
||||||
|
}
|
||||||
|
|
||||||
const { assets, totalCount, hasMore, loading, error, refetch } = useAssets(
|
const { assets, totalCount, hasMore, loading, error, refetch } = useAssets(
|
||||||
{},
|
filters,
|
||||||
limit,
|
limit,
|
||||||
page * limit,
|
page * limit,
|
||||||
'creation desc'
|
'creation desc'
|
||||||
@ -21,6 +79,73 @@ const AssetList: React.FC = () => {
|
|||||||
|
|
||||||
const { deleteAsset, loading: mutationLoading } = useAssetMutations();
|
const { deleteAsset, loading: mutationLoading } = useAssetMutations();
|
||||||
|
|
||||||
|
// Reset page when filters change
|
||||||
|
useEffect(() => {
|
||||||
|
setPage(0);
|
||||||
|
}, [filterAssetId, filterCompany, filterManufacturer, filterSupplier, filterLocation, filterDepartment, filterModality,
|
||||||
|
filterDeviceStatus, filterAssetName, filterSerialNumber, searchTerm]);
|
||||||
|
|
||||||
|
// Debounce function for text inputs
|
||||||
|
const handleAssetNameChange = (value: string) => {
|
||||||
|
setTempAssetName(value);
|
||||||
|
|
||||||
|
// Clear existing timeout
|
||||||
|
if (assetNameDebounceRef.current) {
|
||||||
|
clearTimeout(assetNameDebounceRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new timeout - apply filter after 800ms of no typing
|
||||||
|
assetNameDebounceRef.current = setTimeout(() => {
|
||||||
|
setFilterAssetName(value);
|
||||||
|
}, 800);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSerialNumberChange = (value: string) => {
|
||||||
|
setTempSerialNumber(value);
|
||||||
|
|
||||||
|
// Clear existing timeout
|
||||||
|
if (serialNumberDebounceRef.current) {
|
||||||
|
clearTimeout(serialNumberDebounceRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new timeout - apply filter after 800ms of no typing
|
||||||
|
serialNumberDebounceRef.current = setTimeout(() => {
|
||||||
|
setFilterSerialNumber(value);
|
||||||
|
}, 800);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Enter key press for immediate filter application
|
||||||
|
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>, type: 'assetName' | 'serialNumber') => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
if (type === 'assetName') {
|
||||||
|
if (assetNameDebounceRef.current) {
|
||||||
|
clearTimeout(assetNameDebounceRef.current);
|
||||||
|
}
|
||||||
|
setFilterAssetName(tempAssetName);
|
||||||
|
} else if (type === 'serialNumber') {
|
||||||
|
if (serialNumberDebounceRef.current) {
|
||||||
|
clearTimeout(serialNumberDebounceRef.current);
|
||||||
|
}
|
||||||
|
setFilterSerialNumber(tempSerialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cleanup timeouts on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (assetNameDebounceRef.current) {
|
||||||
|
clearTimeout(assetNameDebounceRef.current);
|
||||||
|
}
|
||||||
|
if (serialNumberDebounceRef.current) {
|
||||||
|
clearTimeout(serialNumberDebounceRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
// Close dropdown when clicking outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
@ -107,6 +232,34 @@ const AssetList: React.FC = () => {
|
|||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearFilters = () => {
|
||||||
|
setFilterAssetId('');
|
||||||
|
setFilterCompany('');
|
||||||
|
setFilterManufacturer('');
|
||||||
|
setFilterSupplier('');
|
||||||
|
setFilterLocation('');
|
||||||
|
setFilterDepartment('');
|
||||||
|
setFilterModality('');
|
||||||
|
setFilterDeviceStatus('');
|
||||||
|
setFilterAssetName('');
|
||||||
|
setFilterSerialNumber('');
|
||||||
|
setTempAssetName('');
|
||||||
|
setTempSerialNumber('');
|
||||||
|
setSearchTerm('');
|
||||||
|
|
||||||
|
// Clear any pending debounce timers
|
||||||
|
if (assetNameDebounceRef.current) {
|
||||||
|
clearTimeout(assetNameDebounceRef.current);
|
||||||
|
}
|
||||||
|
if (serialNumberDebounceRef.current) {
|
||||||
|
clearTimeout(serialNumberDebounceRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasActiveFilters = filterAssetId || filterCompany || filterManufacturer || filterSupplier ||
|
||||||
|
filterLocation || filterDepartment || filterModality || filterDeviceStatus ||
|
||||||
|
filterAssetName || filterSerialNumber || searchTerm;
|
||||||
|
|
||||||
if (loading && page === 0) {
|
if (loading && page === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-screen bg-gray-50 dark:bg-gray-900">
|
<div className="flex items-center justify-center h-screen bg-gray-50 dark:bg-gray-900">
|
||||||
@ -189,7 +342,7 @@ const AssetList: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
<div className="mb-6 bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
{/* <div className="mb-6 bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
||||||
<div className="flex items-center gap-2 border border-gray-300 dark:border-gray-600 rounded-lg px-4 py-2 bg-white dark:bg-gray-700">
|
<div className="flex items-center gap-2 border border-gray-300 dark:border-gray-600 rounded-lg px-4 py-2 bg-white dark:bg-gray-700">
|
||||||
<FaSearch className="text-gray-400 dark:text-gray-500" />
|
<FaSearch className="text-gray-400 dark:text-gray-500" />
|
||||||
<input
|
<input
|
||||||
@ -199,7 +352,333 @@ const AssetList: React.FC = () => {
|
|||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="flex-1 outline-none text-gray-700 dark:text-gray-200 bg-transparent"
|
className="flex-1 outline-none text-gray-700 dark:text-gray-200 bg-transparent"
|
||||||
/>
|
/>
|
||||||
|
{searchTerm && (
|
||||||
|
<button
|
||||||
|
onClick={() => setSearchTerm('')}
|
||||||
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<FaTimes />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</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>
|
||||||
|
{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"
|
||||||
|
>
|
||||||
|
<FaTimes />
|
||||||
|
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">
|
||||||
|
{/* Asset ID Filter */}
|
||||||
|
<div>
|
||||||
|
<LinkField
|
||||||
|
label="Asset ID"
|
||||||
|
doctype="Asset"
|
||||||
|
value={filterAssetId}
|
||||||
|
onChange={(val) => setFilterAssetId(val)}
|
||||||
|
placeholder="Select Asset ID"
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Company Filter */}
|
||||||
|
<div>
|
||||||
|
<LinkField
|
||||||
|
label="Hospital"
|
||||||
|
doctype="Company"
|
||||||
|
value={filterCompany}
|
||||||
|
onChange={(val) => setFilterCompany(val)}
|
||||||
|
placeholder="Select Hospital"
|
||||||
|
disabled={false}
|
||||||
|
filters={{ domain: 'Healthcare' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Location Filter */}
|
||||||
|
<div>
|
||||||
|
<LinkField
|
||||||
|
label="Location"
|
||||||
|
doctype="Location"
|
||||||
|
value={filterLocation}
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second Row - 5 filters */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||||
|
{/* Manufacturer Filter */}
|
||||||
|
<div>
|
||||||
|
<LinkField
|
||||||
|
label="Manufacturer"
|
||||||
|
doctype="Manufacturer"
|
||||||
|
value={filterManufacturer}
|
||||||
|
onChange={(val) => setFilterManufacturer(val)}
|
||||||
|
placeholder="Select Manufacturer"
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Supplier Filter */}
|
||||||
|
<div>
|
||||||
|
<LinkField
|
||||||
|
label="Supplier"
|
||||||
|
doctype="Supplier"
|
||||||
|
value={filterSupplier}
|
||||||
|
onChange={(val) => setFilterSupplier(val)}
|
||||||
|
placeholder="Select Supplier"
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Device Status Filter - Dropdown */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<option value="">All Status</option>
|
||||||
|
<option value="Up">Up</option>
|
||||||
|
<option value="Down">Down</option>
|
||||||
|
</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">
|
||||||
|
{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">
|
||||||
|
Asset ID: {filterAssetId}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterAssetId('')}
|
||||||
|
className="hover:text-blue-600 dark:hover:text-blue-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Hospital: {filterCompany}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterCompany('')}
|
||||||
|
className="hover:text-green-600 dark:hover:text-green-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Location: {filterLocation}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterLocation('')}
|
||||||
|
className="hover:text-purple-600 dark:hover:text-purple-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Department: {filterDepartment}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterDepartment('')}
|
||||||
|
className="hover:text-yellow-600 dark:hover:text-yellow-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Modality: {filterModality}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterModality('')}
|
||||||
|
className="hover:text-pink-600 dark:hover:text-pink-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Manufacturer: {filterManufacturer}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterManufacturer('')}
|
||||||
|
className="hover:text-indigo-600 dark:hover:text-indigo-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Supplier: {filterSupplier}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterSupplier('')}
|
||||||
|
className="hover:text-teal-600 dark:hover:text-teal-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Status: {filterDeviceStatus}
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterDeviceStatus('')}
|
||||||
|
className="hover:text-orange-600 dark:hover:text-orange-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Asset Name: "{filterAssetName}"
|
||||||
|
<button
|
||||||
|
// onClick={() => setFilterAssetName('')}
|
||||||
|
onClick={() => {
|
||||||
|
setFilterAssetName('');
|
||||||
|
setTempAssetName('');
|
||||||
|
}}
|
||||||
|
className="hover:text-cyan-600 dark:hover:text-cyan-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Serial: "{filterSerialNumber}"
|
||||||
|
<button
|
||||||
|
// onClick={() => setFilterSerialNumber('')}
|
||||||
|
onClick={() => {
|
||||||
|
setFilterSerialNumber('');
|
||||||
|
setTempSerialNumber('');
|
||||||
|
}}
|
||||||
|
className="hover:text-lime-600 dark:hover:text-lime-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</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">
|
||||||
|
Search: "{searchTerm}"
|
||||||
|
<button
|
||||||
|
onClick={() => setSearchTerm('')}
|
||||||
|
className="hover:text-purple-600 dark:hover:text-purple-400"
|
||||||
|
>
|
||||||
|
<FaTimes className="text-xs" />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Assets Table */}
|
{/* Assets Table */}
|
||||||
@ -235,12 +714,29 @@ const AssetList: React.FC = () => {
|
|||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<FaSearch className="text-4xl text-gray-300 dark:text-gray-600 mb-2" />
|
<FaSearch className="text-4xl text-gray-300 dark:text-gray-600 mb-2" />
|
||||||
<p>No assets found</p>
|
<p>No assets found</p>
|
||||||
<button
|
{/* <button
|
||||||
onClick={handleCreateNew}
|
onClick={handleCreateNew}
|
||||||
className="mt-4 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline"
|
className="mt-4 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline"
|
||||||
>
|
>
|
||||||
Create your first asset
|
Create your first asset
|
||||||
</button>
|
</button> */}
|
||||||
|
|
||||||
|
{hasActiveFilters ? (
|
||||||
|
<button
|
||||||
|
onClick={handleClearFilters}
|
||||||
|
className="mt-4 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline"
|
||||||
|
>
|
||||||
|
Clear filters
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={handleCreateNew}
|
||||||
|
className="mt-4 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline"
|
||||||
|
>
|
||||||
|
Create your first asset
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -265,7 +761,8 @@ const AssetList: React.FC = () => {
|
|||||||
{asset.location || '-'}
|
{asset.location || '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<span className={`
|
{asset.custom_device_status || '-'}
|
||||||
|
{/* <span className={`
|
||||||
px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full
|
px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full
|
||||||
${asset.custom_device_status === 'Operational' ? 'bg-green-100 text-green-800' : ''}
|
${asset.custom_device_status === 'Operational' ? 'bg-green-100 text-green-800' : ''}
|
||||||
${asset.custom_device_status === 'Under Maintenance' ? 'bg-yellow-100 text-yellow-800' : ''}
|
${asset.custom_device_status === 'Under Maintenance' ? 'bg-yellow-100 text-yellow-800' : ''}
|
||||||
@ -273,7 +770,7 @@ const AssetList: React.FC = () => {
|
|||||||
${!asset.custom_device_status ? 'bg-gray-100 text-gray-800' : ''}
|
${!asset.custom_device_status ? 'bg-gray-100 text-gray-800' : ''}
|
||||||
`}>
|
`}>
|
||||||
{asset.custom_device_status || 'Unknown'}
|
{asset.custom_device_status || 'Unknown'}
|
||||||
</span>
|
</span> */}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||||
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
|
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user