edit for portal & tender

This commit is contained in:
Aya
2026-06-03 13:01:51 +03:00
parent 61ca570e7a
commit 96386887fb
17 changed files with 1280 additions and 147 deletions

View File

@@ -46,6 +46,7 @@ export default function ManagedExpenseClaimsPage() {
const [claims, setClaims] = useState<ExpenseClaim[]>([]);
const [loading, setLoading] = useState(true);
const [statusFilter, setStatusFilter] = useState('PENDING');
const [paidFilter, setPaidFilter] = useState<'all' | 'paid' | 'unpaid'>('all');
const [searchQuery, setSearchQuery] = useState('');
const [submittingId, setSubmittingId] = useState<string | null>(null);
const [payingId, setPayingId] = useState<string | null>(null);
@@ -57,12 +58,17 @@ export default function ManagedExpenseClaimsPage() {
const searchParams = useSearchParams();
const claimId = searchParams.get('claimId');
async function loadClaims(status = statusFilter, search = searchQuery) {
async function loadClaims(
status = statusFilter,
search = searchQuery,
paid: 'all' | 'paid' | 'unpaid' = paidFilter,
) {
try {
setLoading(true);
const data = await portalAPI.getManagedExpenseClaims(
status === 'all' ? undefined : status,
search.trim() || undefined,
paid,
);
setClaims(data);
} catch (error: any) {
@@ -75,11 +81,11 @@ export default function ManagedExpenseClaimsPage() {
useEffect(() => {
// Debounce the search so we don't fire a request on every keystroke.
const handle = setTimeout(() => {
loadClaims(statusFilter, searchQuery);
loadClaims(statusFilter, searchQuery, paidFilter);
}, 400);
return () => clearTimeout(handle);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [statusFilter, searchQuery]);
}, [statusFilter, searchQuery, paidFilter]);
async function openAttachment(attachment: any) {
try {
@@ -108,7 +114,7 @@ export default function ManagedExpenseClaimsPage() {
try {
setSubmittingId(id);
await portalAPI.approveManagedExpenseClaim(id, note.trim() || undefined);
await loadClaims(statusFilter, searchQuery);
await loadClaims(statusFilter, searchQuery, paidFilter);
} catch (error: any) {
alert(error?.response?.data?.message || 'تعذر تنفيذ الموافقة');
} finally {
@@ -141,7 +147,7 @@ export default function ManagedExpenseClaimsPage() {
setRejectModalOpen(false);
setSelectedClaim(null);
setRejectReason('');
await loadClaims(statusFilter, searchQuery);
await loadClaims(statusFilter, searchQuery, paidFilter);
} catch (error: any) {
alert(error?.response?.data?.message || 'تعذر تنفيذ الرفض');
} finally {
@@ -229,6 +235,19 @@ export default function ManagedExpenseClaimsPage() {
<option value="all">الكل</option>
</select>
</div>
<div className="flex items-center gap-2">
<label className="text-sm text-gray-600">القبض:</label>
<select
value={paidFilter}
onChange={(e) => setPaidFilter(e.target.value as 'all' | 'paid' | 'unpaid')}
className="rounded-lg border border-gray-300 px-3 py-2 text-sm"
>
<option value="all">الكل</option>
<option value="paid">مقبوض</option>
<option value="unpaid">غير مقبوض</option>
</select>
</div>
</div>
</div>