Álvaro Valenzuela Valdes commited on
Commit ·
482905a
1
Parent(s): 05e0832
Fix TenderSearch button interaction, guard duplicate submits, and add portal link in results
Browse files
frontend/app/page.tsx
CHANGED
|
@@ -116,7 +116,7 @@ export default function HomePage() {
|
|
| 116 |
window.history.pushState({}, '', `?tab=tender_search&q=${encodeURIComponent(value)}`);
|
| 117 |
};
|
| 118 |
|
| 119 |
-
const handleSearch = async (params: { keyword?: string; buyer?: string; provider_code?: string; date?: string; skip?: number; limit?: number; isAgile?: boolean }) => {
|
| 120 |
try {
|
| 121 |
let results: Tender[];
|
| 122 |
if (params.isAgile && params.keyword) {
|
|
|
|
| 116 |
window.history.pushState({}, '', `?tab=tender_search&q=${encodeURIComponent(value)}`);
|
| 117 |
};
|
| 118 |
|
| 119 |
+
const handleSearch = async (params: { keyword?: string; buyer?: string; provider_code?: string; org_code?: string; status?: string; code?: string; date?: string; skip?: number; limit?: number; isAgile?: boolean }) => {
|
| 120 |
try {
|
| 121 |
let results: Tender[];
|
| 122 |
if (params.isAgile && params.keyword) {
|
frontend/components/TenderSearch.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
-
import { Fragment, useMemo, useState, useEffect } from "react";
|
| 4 |
import BrandLoader from "./BrandLoader";
|
| 5 |
import type { Tender } from "../lib/types";
|
| 6 |
import { Language, translations } from "../lib/translations";
|
|
@@ -39,6 +39,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 39 |
const [showOnlyFollowed, setShowOnlyFollowed] = useState(forceShowFollowed);
|
| 40 |
const [isLoading, setIsLoading] = useState(false);
|
| 41 |
const [isAgileMode, setIsAgileMode] = useState(false);
|
|
|
|
| 42 |
const itemsPerPage = 50;
|
| 43 |
|
| 44 |
const isTenderCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
|
|
@@ -101,6 +102,8 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 101 |
|
| 102 |
const handleSearch = async (e?: React.FormEvent) => {
|
| 103 |
if (e) e.preventDefault();
|
|
|
|
|
|
|
| 104 |
setIsLoading(true);
|
| 105 |
try {
|
| 106 |
// Logic: If the query looks like a code (contains hyphens), prioritize code search
|
|
@@ -122,6 +125,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 122 |
console.error(error);
|
| 123 |
} finally {
|
| 124 |
setIsLoading(false);
|
|
|
|
| 125 |
}
|
| 126 |
};
|
| 127 |
|
|
@@ -380,6 +384,16 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 380 |
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
| 381 |
</button>
|
| 382 |
<span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
</div>
|
| 384 |
</td>
|
| 385 |
<td className="px-4 py-5">
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
+
import { Fragment, useMemo, useState, useRef, useEffect } from "react";
|
| 4 |
import BrandLoader from "./BrandLoader";
|
| 5 |
import type { Tender } from "../lib/types";
|
| 6 |
import { Language, translations } from "../lib/translations";
|
|
|
|
| 39 |
const [showOnlyFollowed, setShowOnlyFollowed] = useState(forceShowFollowed);
|
| 40 |
const [isLoading, setIsLoading] = useState(false);
|
| 41 |
const [isAgileMode, setIsAgileMode] = useState(false);
|
| 42 |
+
const isSearchPending = useRef(false);
|
| 43 |
const itemsPerPage = 50;
|
| 44 |
|
| 45 |
const isTenderCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
|
|
|
|
| 102 |
|
| 103 |
const handleSearch = async (e?: React.FormEvent) => {
|
| 104 |
if (e) e.preventDefault();
|
| 105 |
+
if (isSearchPending.current) return;
|
| 106 |
+
isSearchPending.current = true;
|
| 107 |
setIsLoading(true);
|
| 108 |
try {
|
| 109 |
// Logic: If the query looks like a code (contains hyphens), prioritize code search
|
|
|
|
| 125 |
console.error(error);
|
| 126 |
} finally {
|
| 127 |
setIsLoading(false);
|
| 128 |
+
isSearchPending.current = false;
|
| 129 |
}
|
| 130 |
};
|
| 131 |
|
|
|
|
| 384 |
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
| 385 |
</button>
|
| 386 |
<span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
|
| 387 |
+
<a
|
| 388 |
+
href={`https://www.mercadopublico.cl/fichaLicitacion.html?code=${tender.code}`}
|
| 389 |
+
target="_blank"
|
| 390 |
+
rel="noopener noreferrer"
|
| 391 |
+
onClick={(e) => e.stopPropagation()}
|
| 392 |
+
className="ml-2 text-[10px] text-slate-400 hover:text-white transition"
|
| 393 |
+
title="Open Tender in Mercado Público"
|
| 394 |
+
>
|
| 395 |
+
↗
|
| 396 |
+
</a>
|
| 397 |
</div>
|
| 398 |
</td>
|
| 399 |
<td className="px-4 py-5">
|