Álvaro Valenzuela Valdes commited on
Commit ·
a81ee34
1
Parent(s): 35c7f2f
feat: Replace expandable rows with detailed tender modal and simplify table
Browse files- frontend/components/TenderSearch.tsx +157 -122
frontend/components/TenderSearch.tsx
CHANGED
|
@@ -16,7 +16,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 16 |
const [keyword, setKeyword] = useState(initialKeyword);
|
| 17 |
const [buyerCode, setBuyerCode] = useState("");
|
| 18 |
const [date, setDate] = useState("");
|
| 19 |
-
const [
|
| 20 |
const [selectedCodes, setSelectedCodes] = useState<string[]>([]);
|
| 21 |
const [isSyncingToAgents, setIsSyncingToAgents] = useState(false);
|
| 22 |
|
|
@@ -44,12 +44,6 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 44 |
localStorage.setItem('andes_followed_codes', JSON.stringify(followedCodes));
|
| 45 |
}, [followedCodes]);
|
| 46 |
|
| 47 |
-
const toggleExpanded = (code: string) => {
|
| 48 |
-
setExpandedTenderCodes((current) =>
|
| 49 |
-
current.includes(code) ? current.filter((value) => value !== code) : [...current, code]
|
| 50 |
-
);
|
| 51 |
-
};
|
| 52 |
-
|
| 53 |
const toggleFollow = (code: string) => {
|
| 54 |
setFollowedCodes(prev =>
|
| 55 |
prev.includes(code) ? prev.filter(c => c !== code) : [...prev, code]
|
|
@@ -64,7 +58,6 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 64 |
|
| 65 |
const handleSyncToAgents = () => {
|
| 66 |
setIsSyncingToAgents(true);
|
| 67 |
-
// Simulate system ingestion
|
| 68 |
setTimeout(() => {
|
| 69 |
setIsSyncingToAgents(false);
|
| 70 |
const firstSelected = tenders.find(t => t.code === selectedCodes[0]);
|
|
@@ -206,129 +199,54 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 206 |
<th className="px-4 py-5 w-[250px]">Opportunity</th>
|
| 207 |
<th className="px-4 py-5 w-[180px]">Buyer</th>
|
| 208 |
<th className="px-4 py-5 text-center w-[100px]">Status</th>
|
| 209 |
-
<th className="px-4 py-5 text-right w-[90px]">Deadline</th>
|
| 210 |
-
<th className="px-4 py-5 text-right pr-6 w-[100px]">Actions</th>
|
| 211 |
</tr>
|
| 212 |
</thead>
|
| 213 |
<tbody className="divide-y divide-white/5">
|
| 214 |
{filteredTenders.map((tender) => (
|
| 215 |
-
<
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
onClick={(e) => {
|
| 233 |
-
e.stopPropagation();
|
| 234 |
-
toggleFollow(tender.code);
|
| 235 |
-
}}
|
| 236 |
-
className={`text-base transition-all hover:scale-125 ${followedCodes.includes(tender.code) ? 'text-purple-400 drop-shadow-[0_0_8px_rgba(168,85,247,0.4)]' : 'text-slate-600 hover:text-slate-400'}`}
|
| 237 |
-
>
|
| 238 |
-
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
| 239 |
-
</button>
|
| 240 |
-
<span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
|
| 241 |
-
</div>
|
| 242 |
-
</td>
|
| 243 |
-
<td className="px-4 py-5">
|
| 244 |
-
<div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate text-xs">{tender.name}</div>
|
| 245 |
-
<div className="flex items-center gap-2 mt-1">
|
| 246 |
-
<span className="text-[9px] text-slate-500 truncate">{tender.region || "Nacional"}</span>
|
| 247 |
-
<span className="text-[8px] px-1.5 py-0.5 rounded-md bg-white/5 text-slate-600 border border-white/5 uppercase tracking-tighter">{tender.sector}</span>
|
| 248 |
-
</div>
|
| 249 |
-
</td>
|
| 250 |
-
<td className="px-4 py-5 text-slate-400 text-[11px] truncate">{tender.buyer}</td>
|
| 251 |
-
<td className="px-4 py-5 text-center">
|
| 252 |
-
<span className={`inline-block rounded-full px-2 py-0.5 text-[9px] font-bold ${
|
| 253 |
-
tender.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400 border border-green-500/20' : 'bg-slate-800/50 text-slate-500'
|
| 254 |
-
}`}>
|
| 255 |
-
{tender.status}
|
| 256 |
-
</span>
|
| 257 |
-
</td>
|
| 258 |
-
<td className="px-4 py-5 text-right font-mono text-[11px] text-slate-400">
|
| 259 |
-
{tender.closing_date ? new Date(tender.closing_date).toLocaleDateString() : "---"}
|
| 260 |
-
</td>
|
| 261 |
-
<td className="px-4 py-5 text-right pr-6">
|
| 262 |
-
<button
|
| 263 |
onClick={(e) => {
|
| 264 |
e.stopPropagation();
|
| 265 |
-
|
| 266 |
}}
|
| 267 |
-
className=
|
| 268 |
>
|
| 269 |
-
|
| 270 |
</button>
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
</div>
|
| 291 |
-
</div>
|
| 292 |
-
<div className="p-5 rounded-2xl bg-white/[0.03] border border-white/5 shadow-inner">
|
| 293 |
-
<div className="text-[9px] uppercase text-slate-500 font-black mb-1">Market Sector</div>
|
| 294 |
-
<div className="text-sm text-white font-bold">{tender.sector || "General"}</div>
|
| 295 |
-
</div>
|
| 296 |
-
</div>
|
| 297 |
-
</div>
|
| 298 |
-
<div className="lg:col-span-2 space-y-8">
|
| 299 |
-
<div>
|
| 300 |
-
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-purple-400 mb-4 flex items-center gap-2">
|
| 301 |
-
<span className="w-4 h-[1px] bg-purple-500/50" />
|
| 302 |
-
Resources & Direct Links
|
| 303 |
-
</h4>
|
| 304 |
-
<div className="grid gap-3">
|
| 305 |
-
{tender.attachments?.map((att, i) => (
|
| 306 |
-
<a key={i} href={att.url} target="_blank" className="flex items-center justify-between p-4 rounded-2xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/5 transition-all group/file">
|
| 307 |
-
<div className="flex items-center gap-4">
|
| 308 |
-
<span className="text-2xl">{att.name.endsWith('.pdf') ? "📕" : "📘"}</span>
|
| 309 |
-
<div className="flex flex-col">
|
| 310 |
-
<span className="text-xs font-bold text-slate-200 group-hover/file:text-white transition-colors">{att.name}</span>
|
| 311 |
-
<span className="text-[10px] text-slate-500 uppercase tracking-tighter">Official Document</span>
|
| 312 |
-
</div>
|
| 313 |
-
</div>
|
| 314 |
-
<span className="text-[10px] font-black text-purple-400 opacity-0 group-hover/file:opacity-100 transition-all">DOWNLOAD</span>
|
| 315 |
-
</a>
|
| 316 |
-
))}
|
| 317 |
-
<a
|
| 318 |
-
href={`https://www.mercadopublico.cl/fichaLicitacion.html?code=${tender.code}`}
|
| 319 |
-
target="_blank"
|
| 320 |
-
className="flex items-center justify-center gap-2 p-3 rounded-xl bg-purple-500/10 border border-purple-500/20 text-purple-300 text-xs font-bold hover:bg-purple-500/20 transition mt-2"
|
| 321 |
-
>
|
| 322 |
-
Open official portal
|
| 323 |
-
</a>
|
| 324 |
-
</div>
|
| 325 |
-
</div>
|
| 326 |
-
</div>
|
| 327 |
-
</div>
|
| 328 |
-
</td>
|
| 329 |
-
</tr>
|
| 330 |
-
)}
|
| 331 |
-
</Fragment>
|
| 332 |
))}
|
| 333 |
</tbody>
|
| 334 |
</table>
|
|
@@ -337,6 +255,123 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 337 |
)}
|
| 338 |
</div>
|
| 339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
{isLoading && <BrandLoader />}
|
| 341 |
</div>
|
| 342 |
);
|
|
|
|
| 16 |
const [keyword, setKeyword] = useState(initialKeyword);
|
| 17 |
const [buyerCode, setBuyerCode] = useState("");
|
| 18 |
const [date, setDate] = useState("");
|
| 19 |
+
const [selectedTenderForModal, setSelectedTenderForModal] = useState<Tender | null>(null);
|
| 20 |
const [selectedCodes, setSelectedCodes] = useState<string[]>([]);
|
| 21 |
const [isSyncingToAgents, setIsSyncingToAgents] = useState(false);
|
| 22 |
|
|
|
|
| 44 |
localStorage.setItem('andes_followed_codes', JSON.stringify(followedCodes));
|
| 45 |
}, [followedCodes]);
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
const toggleFollow = (code: string) => {
|
| 48 |
setFollowedCodes(prev =>
|
| 49 |
prev.includes(code) ? prev.filter(c => c !== code) : [...prev, code]
|
|
|
|
| 58 |
|
| 59 |
const handleSyncToAgents = () => {
|
| 60 |
setIsSyncingToAgents(true);
|
|
|
|
| 61 |
setTimeout(() => {
|
| 62 |
setIsSyncingToAgents(false);
|
| 63 |
const firstSelected = tenders.find(t => t.code === selectedCodes[0]);
|
|
|
|
| 199 |
<th className="px-4 py-5 w-[250px]">Opportunity</th>
|
| 200 |
<th className="px-4 py-5 w-[180px]">Buyer</th>
|
| 201 |
<th className="px-4 py-5 text-center w-[100px]">Status</th>
|
|
|
|
|
|
|
| 202 |
</tr>
|
| 203 |
</thead>
|
| 204 |
<tbody className="divide-y divide-white/5">
|
| 205 |
{filteredTenders.map((tender) => (
|
| 206 |
+
<tr
|
| 207 |
+
key={tender.code}
|
| 208 |
+
className={`hover:bg-white/[0.04] cursor-pointer transition-colors group ${selectedCodes.includes(tender.code) ? 'bg-purple-500/5' : ''}`}
|
| 209 |
+
onClick={() => setSelectedTenderForModal(tender)}
|
| 210 |
+
>
|
| 211 |
+
<td className="px-4 py-5">
|
| 212 |
+
<div className="flex items-center gap-2">
|
| 213 |
+
<input
|
| 214 |
+
type="checkbox"
|
| 215 |
+
checked={selectedCodes.includes(tender.code)}
|
| 216 |
+
onChange={(e) => {
|
| 217 |
+
e.stopPropagation();
|
| 218 |
+
toggleSelect(tender.code);
|
| 219 |
+
}}
|
| 220 |
+
className="w-3.5 h-3.5 rounded border-white/10 bg-white/5 text-purple-500 focus:ring-purple-500/40"
|
| 221 |
+
/>
|
| 222 |
+
<button
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
onClick={(e) => {
|
| 224 |
e.stopPropagation();
|
| 225 |
+
toggleFollow(tender.code);
|
| 226 |
}}
|
| 227 |
+
className={`text-base transition-all hover:scale-125 ${followedCodes.includes(tender.code) ? 'text-purple-400 drop-shadow-[0_0_8px_rgba(168,85,247,0.4)]' : 'text-slate-600 hover:text-slate-400'}`}
|
| 228 |
>
|
| 229 |
+
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
| 230 |
</button>
|
| 231 |
+
<span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
|
| 232 |
+
</div>
|
| 233 |
+
</td>
|
| 234 |
+
<td className="px-4 py-5">
|
| 235 |
+
<div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate text-xs">{tender.name}</div>
|
| 236 |
+
<div className="flex items-center gap-2 mt-1">
|
| 237 |
+
<span className="text-[9px] text-slate-500 truncate">{tender.region || "Nacional"}</span>
|
| 238 |
+
<span className="text-[8px] px-1.5 py-0.5 rounded-md bg-white/5 text-slate-600 border border-white/5 uppercase tracking-tighter">{tender.sector}</span>
|
| 239 |
+
</div>
|
| 240 |
+
</td>
|
| 241 |
+
<td className="px-4 py-5 text-slate-400 text-[11px] truncate">{tender.buyer}</td>
|
| 242 |
+
<td className="px-4 py-5 text-center">
|
| 243 |
+
<span className={`inline-block rounded-full px-2 py-0.5 text-[9px] font-bold ${
|
| 244 |
+
tender.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400 border border-green-500/20' : 'bg-slate-800/50 text-slate-500'
|
| 245 |
+
}`}>
|
| 246 |
+
{tender.status}
|
| 247 |
+
</span>
|
| 248 |
+
</td>
|
| 249 |
+
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
))}
|
| 251 |
</tbody>
|
| 252 |
</table>
|
|
|
|
| 255 |
)}
|
| 256 |
</div>
|
| 257 |
|
| 258 |
+
{/* Details Modal */}
|
| 259 |
+
{selectedTenderForModal && (
|
| 260 |
+
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-10">
|
| 261 |
+
<div className="absolute inset-0 bg-slate-950/80 backdrop-blur-md" onClick={() => setSelectedTenderForModal(null)} />
|
| 262 |
+
<div className="relative w-full max-w-4xl max-h-[90vh] bg-slate-900 border border-white/10 rounded-3xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-300">
|
| 263 |
+
{/* Modal Header */}
|
| 264 |
+
<div className="p-6 md:p-8 border-b border-white/10 flex justify-between items-start">
|
| 265 |
+
<div>
|
| 266 |
+
<div className="flex items-center gap-3 mb-2">
|
| 267 |
+
<span className="text-xs font-mono text-purple-400 bg-purple-400/10 px-2 py-0.5 rounded-md">{selectedTenderForModal.code}</span>
|
| 268 |
+
<span className={`px-2 py-0.5 rounded-md text-[10px] font-bold uppercase tracking-tighter ${
|
| 269 |
+
selectedTenderForModal.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400' : 'bg-slate-800 text-slate-500'
|
| 270 |
+
}`}>
|
| 271 |
+
{selectedTenderForModal.status}
|
| 272 |
+
</span>
|
| 273 |
+
</div>
|
| 274 |
+
<h3 className="text-2xl font-bold text-white">{selectedTenderForModal.name}</h3>
|
| 275 |
+
<p className="text-slate-400 text-sm mt-1">{selectedTenderForModal.buyer}</p>
|
| 276 |
+
</div>
|
| 277 |
+
<button
|
| 278 |
+
onClick={() => setSelectedTenderForModal(null)}
|
| 279 |
+
className="p-2 bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white rounded-xl transition-all"
|
| 280 |
+
>
|
| 281 |
+
✕
|
| 282 |
+
</button>
|
| 283 |
+
</div>
|
| 284 |
+
|
| 285 |
+
{/* Modal Content */}
|
| 286 |
+
<div className="flex-1 overflow-y-auto p-6 md:p-8 custom-scrollbar">
|
| 287 |
+
<div className="grid gap-10 md:grid-cols-3">
|
| 288 |
+
{/* Left Column: Main Info */}
|
| 289 |
+
<div className="md:col-span-2 space-y-8">
|
| 290 |
+
<div>
|
| 291 |
+
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-4 flex items-center gap-2">
|
| 292 |
+
<span className="w-4 h-[1px] bg-slate-700" />
|
| 293 |
+
Project Description
|
| 294 |
+
</h4>
|
| 295 |
+
<p className="text-slate-300 leading-relaxed text-sm bg-slate-800/50 p-6 rounded-2xl border border-white/5 whitespace-pre-wrap">
|
| 296 |
+
{selectedTenderForModal.description || "No detailed description provided."}
|
| 297 |
+
</p>
|
| 298 |
+
</div>
|
| 299 |
+
|
| 300 |
+
<div className="grid grid-cols-2 gap-4">
|
| 301 |
+
<div className="p-5 rounded-2xl bg-white/[0.03] border border-white/5">
|
| 302 |
+
<div className="text-[9px] uppercase text-slate-500 font-black mb-1">Estimated Amount</div>
|
| 303 |
+
<div className="text-sm text-white font-bold">
|
| 304 |
+
{selectedTenderForModal.estimated_amount ? new Intl.NumberFormat("es-CL", { style: "currency", currency: "CLP" }).format(selectedTenderForModal.estimated_amount) : "Not Disclosed"}
|
| 305 |
+
</div>
|
| 306 |
+
</div>
|
| 307 |
+
<div className="p-5 rounded-2xl bg-white/[0.03] border border-white/5">
|
| 308 |
+
<div className="text-[9px] uppercase text-slate-500 font-black mb-1">Market Sector</div>
|
| 309 |
+
<div className="text-sm text-white font-bold">{selectedTenderForModal.sector || "General"}</div>
|
| 310 |
+
</div>
|
| 311 |
+
</div>
|
| 312 |
+
</div>
|
| 313 |
+
|
| 314 |
+
{/* Right Column: Meta & Links */}
|
| 315 |
+
<div className="space-y-8">
|
| 316 |
+
<div className="p-6 rounded-2xl bg-purple-500/5 border border-purple-500/10">
|
| 317 |
+
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-purple-400 mb-4">Deadline</h4>
|
| 318 |
+
<div className="text-xl font-mono text-white mb-1">
|
| 319 |
+
{selectedTenderForModal.closing_date ? new Date(selectedTenderForModal.closing_date).toLocaleDateString() : "---"}
|
| 320 |
+
</div>
|
| 321 |
+
<p className="text-[10px] text-purple-400/60 uppercase">Final day for submission</p>
|
| 322 |
+
</div>
|
| 323 |
+
|
| 324 |
+
<div>
|
| 325 |
+
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-4">Official Resources</h4>
|
| 326 |
+
<div className="space-y-3">
|
| 327 |
+
{selectedTenderForModal.attachments?.map((att, i) => (
|
| 328 |
+
<a key={i} href={att.url} target="_blank" className="flex items-center justify-between p-4 rounded-xl bg-slate-800 hover:bg-slate-700 border border-white/5 transition-all group/file">
|
| 329 |
+
<div className="flex items-center gap-3">
|
| 330 |
+
<span className="text-xl">{att.name.endsWith('.pdf') ? "📕" : "📘"}</span>
|
| 331 |
+
<span className="text-xs font-medium text-slate-300 truncate max-w-[120px]">{att.name}</span>
|
| 332 |
+
</div>
|
| 333 |
+
<span className="text-[8px] font-black text-purple-400 opacity-0 group-hover/file:opacity-100 uppercase transition-all tracking-tighter">Download</span>
|
| 334 |
+
</a>
|
| 335 |
+
))}
|
| 336 |
+
{!selectedTenderForModal.attachments?.length && (
|
| 337 |
+
<p className="text-[10px] text-slate-600 italic">No attachments found.</p>
|
| 338 |
+
)}
|
| 339 |
+
</div>
|
| 340 |
+
</div>
|
| 341 |
+
|
| 342 |
+
<a
|
| 343 |
+
href={`https://www.mercadopublico.cl/fichaLicitacion.html?code=${selectedTenderForModal.code}`}
|
| 344 |
+
target="_blank"
|
| 345 |
+
className="flex items-center justify-center gap-2 w-full p-4 rounded-xl bg-white/5 border border-white/10 text-slate-300 text-xs font-bold hover:bg-white/10 transition"
|
| 346 |
+
>
|
| 347 |
+
Open official portal ↗
|
| 348 |
+
</a>
|
| 349 |
+
</div>
|
| 350 |
+
</div>
|
| 351 |
+
</div>
|
| 352 |
+
|
| 353 |
+
{/* Modal Footer */}
|
| 354 |
+
<div className="p-6 bg-slate-950/50 border-t border-white/10 flex justify-end gap-4">
|
| 355 |
+
<button
|
| 356 |
+
onClick={() => setSelectedTenderForModal(null)}
|
| 357 |
+
className="px-6 py-3 text-sm font-bold text-slate-400 hover:text-white transition"
|
| 358 |
+
>
|
| 359 |
+
Cancel
|
| 360 |
+
</button>
|
| 361 |
+
<button
|
| 362 |
+
onClick={() => {
|
| 363 |
+
onAnalyze(selectedTenderForModal);
|
| 364 |
+
setSelectedTenderForModal(null);
|
| 365 |
+
}}
|
| 366 |
+
className="premium-gradient text-white px-8 py-3 rounded-xl font-bold text-sm shadow-xl shadow-purple-500/20 active:scale-95 transition-all"
|
| 367 |
+
>
|
| 368 |
+
Run Agent Analysis
|
| 369 |
+
</button>
|
| 370 |
+
</div>
|
| 371 |
+
</div>
|
| 372 |
+
</div>
|
| 373 |
+
)}
|
| 374 |
+
|
| 375 |
{isLoading && <BrandLoader />}
|
| 376 |
</div>
|
| 377 |
);
|