Álvaro Valenzuela Valdes commited on
Commit
1565213
·
1 Parent(s): 956c5af

fix: resolve syntax error in TenderSearch.tsx return statement

Browse files
Files changed (1) hide show
  1. frontend/components/TenderSearch.tsx +90 -398
frontend/components/TenderSearch.tsx CHANGED
@@ -41,37 +41,41 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
41
  });
42
 
43
  const followedCodes = useMemo(() => followedTenders.map(t => t.code), [followedTenders]);
44
-
45
  const [showOnlyFollowed, setShowOnlyFollowed] = useState(forceShowFollowed);
46
  const [isLoading, setIsLoading] = useState(false);
47
  const [isAgileMode, setIsAgileMode] = useState(false);
48
  const isSearchPending = useRef(false);
49
  const itemsPerPage = 50;
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  const isTenderCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
52
- const isLiveSearch = Boolean(isTenderCode || providerCode || orgCode || status || date);
53
- const showLiveDefaultBadge = !showOnlyFollowed && !keyword && !providerCode && !orgCode && !status && !date;
54
- const searchButtonLabel = isLoading
55
- ? "Searching..."
56
- : isLiveSearch
57
- ? "Live MP Search"
58
- : "Fetch Active Tenders";
59
 
60
  useEffect(() => {
61
  if (forceShowFollowed) setShowOnlyFollowed(true);
62
  }, [forceShowFollowed]);
63
 
64
  useEffect(() => {
65
- if (initialKeyword && initialKeyword !== keyword) {
66
- setKeyword(initialKeyword);
67
- }
68
  }, [initialKeyword]);
69
 
70
  useEffect(() => {
71
  localStorage.setItem('andes_followed_tenders_full', JSON.stringify(followedTenders));
72
  }, [followedTenders]);
73
 
74
- // Force scroll to top when opening detail modal
75
  useEffect(() => {
76
  if (selectedTenderForModal) {
77
  window.scrollTo({ top: 0, behavior: 'instant' });
@@ -83,26 +87,16 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
83
  const toggleFollow = (tender: Tender) => {
84
  setFollowedTenders(prev => {
85
  const isFollowing = prev.some(t => t.code === tender.code);
86
- if (isFollowing) {
87
- return prev.filter(t => t.code !== tender.code);
88
- } else {
89
- return [...prev, tender];
90
- }
91
  });
92
  };
93
 
94
  const toggleSelect = (code: string) => {
95
- setSelectedCodes(prev =>
96
- prev.includes(code) ? prev.filter(c => c !== code) : [...prev, code]
97
- );
98
  };
99
 
100
  const toggleSelectAll = () => {
101
- if (selectedCodes.length === filteredTenders.length) {
102
- setSelectedCodes([]);
103
- } else {
104
- setSelectedCodes(filteredTenders.map(t => t.code));
105
- }
106
  };
107
 
108
  const handleSyncToAgents = () => {
@@ -121,9 +115,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
121
  isSearchPending.current = true;
122
  setIsLoading(true);
123
  try {
124
- // Logic: If the query looks like a code (contains hyphens), prioritize code search
125
  const isCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
126
-
127
  await onSearch({
128
  keyword: isCode ? undefined : keyword,
129
  code: isCode ? keyword : undefined,
@@ -145,29 +137,10 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
145
  }
146
  };
147
 
148
- const filteredTenders = useMemo(() => {
149
- if (showOnlyFollowed) {
150
- return followedTenders;
151
- }
152
-
153
- let list = tenders;
154
- if (isAgileMode) {
155
- list = list.filter(t =>
156
- t.code.includes('COT26') ||
157
- t.name.toLowerCase().includes('compra ágil') ||
158
- t.sector?.toLowerCase().includes('agil') ||
159
- t.source?.toLowerCase().includes('real-time') ||
160
- t.source?.toLowerCase().includes('synthetic')
161
- );
162
- }
163
- return list;
164
- }, [tenders, showOnlyFollowed, followedTenders, isAgileMode]);
165
-
166
  return (
167
  <div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700">
168
  {!selectedTenderForModal ? (
169
  <>
170
- {/* Header Section */}
171
  <div className={`glass-card rounded-3xl p-8 mb-4 border transition-all duration-500 ${forceShowFollowed ? 'border-purple-500/30 bg-purple-500/5 shadow-[0_0_50px_rgba(168,85,247,0.1)]' : 'border-white/10'}`}>
172
  <div className="mb-6 flex justify-between items-start">
173
  <div>
@@ -202,10 +175,8 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
202
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Keyword / Tender Code</label>
203
  <input
204
  type="text"
205
- name="keyword"
206
- autoComplete="off"
207
  placeholder="e.g. Software or 1509-5-L114"
208
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
209
  value={keyword}
210
  onChange={(e) => setKeyword(e.target.value)}
211
  />
@@ -214,10 +185,8 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
214
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Buyer name</label>
215
  <input
216
  type="text"
217
- name="buyerCode"
218
- autoComplete="off"
219
  placeholder="e.g. Servicio de Salud"
220
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
221
  value={buyerCode}
222
  onChange={(e) => setBuyerCode(e.target.value)}
223
  />
@@ -226,10 +195,8 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
226
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Provider Code</label>
227
  <input
228
  type="text"
229
- name="providerCode"
230
- autoComplete="off"
231
  placeholder="e.g. 17793"
232
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
233
  value={providerCode}
234
  onChange={(e) => setProviderCode(e.target.value)}
235
  />
@@ -238,10 +205,8 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
238
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Org Code</label>
239
  <input
240
  type="text"
241
- name="orgCode"
242
- autoComplete="off"
243
  placeholder="e.g. 6945"
244
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
245
  value={orgCode}
246
  onChange={(e) => setOrgCode(e.target.value)}
247
  />
@@ -249,7 +214,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
249
  <div className="space-y-2">
250
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Status</label>
251
  <select
252
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
253
  value={status}
254
  onChange={(e) => setStatus(e.target.value)}
255
  >
@@ -259,51 +224,41 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
259
  <option value="6">Cerrada (6)</option>
260
  <option value="7">Desierta (7)</option>
261
  <option value="8">Adjudicada (8)</option>
262
- <option value="18">Revocada (18)</option>
263
- <option value="19">Suspendida (19)</option>
264
  </select>
265
  </div>
266
  <div className="space-y-2">
267
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Tender Type</label>
268
  <select
269
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
270
  value={typeCode}
271
  onChange={(e) => setTypeCode(e.target.value)}
272
  >
273
  <option value="">Any type</option>
274
- <option value="L1">L1 - Licitación Pública < 100 UTM</option>
275
- <option value="LE">LE - Licitación Pública 100-1000 UTM</option>
276
- <option value="LP">LP - Licitación Pública > 1000 UTM</option>
277
  <option value="LS">LS - Servicios Personales</option>
278
  <option value="A1">A1 - Privada por Desierta</option>
279
- <option value="D1">D1 - Trato Directo Proveedor Único</option>
280
- <option value="C2">C2 - Trato Directo (Cotización)</option>
281
  </select>
282
  </div>
283
  <div className="space-y-2">
284
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Date</label>
285
  <input
286
  type="date"
287
- className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all [color-scheme:dark]"
288
  value={date}
289
  onChange={(e) => setDate(e.target.value)}
290
  />
291
  </div>
292
  </div>
293
- <div className="rounded-3xl bg-slate-900/70 border border-white/10 p-4 text-slate-400 text-xs leading-5 flex justify-between items-center">
294
  <div>
295
- <span className="font-bold text-slate-200">Tip:</span> Deja todos los campos vacíos para mostrar las licitaciones activas del día. Usa <span className="font-semibold text-white">Tender Code</span> para una búsqueda exacta o los filtros avanzados para consultas directas en Mercado Público.
296
  </div>
297
  <button
298
  type="button"
299
  onClick={() => {
300
- setKeyword("");
301
- setBuyerCode("");
302
- setProviderCode("");
303
- setOrgCode("");
304
- setStatus("");
305
- setDate("");
306
- setTypeCode("");
307
  }}
308
  className="text-[10px] font-bold uppercase text-slate-500 hover:text-white transition"
309
  >
@@ -316,25 +271,15 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
316
  type="button"
317
  disabled={isLoading}
318
  onClick={handleSearch}
319
- className="w-full sm:w-auto premium-gradient hover:opacity-90 text-white font-bold py-3.5 rounded-xl transition-all shadow-lg shadow-purple-500/20 active:scale-[0.98] disabled:opacity-50"
320
  >
321
  {searchButtonLabel}
322
  </button>
323
- {isLiveSearch && (
324
- <span className="inline-flex items-center justify-center rounded-full bg-emerald-500/10 text-emerald-300 text-[10px] uppercase tracking-[0.25em] px-3 py-2 border border-emerald-500/20">
325
- Live Mercado Público search
326
- </span>
327
- )}
328
  </div>
329
  <button
330
  type="button"
331
  onClick={() => setIsAgileMode(!isAgileMode)}
332
- className={`w-full sm:w-auto px-4 py-3.5 rounded-xl border font-bold text-[10px] uppercase tracking-widest transition-all ${
333
- isAgileMode
334
- ? "bg-cyan/20 border-cyan/40 text-cyan animate-pulse"
335
- : "bg-white/5 border-white/10 text-slate-500 hover:border-white/20"
336
- }`}
337
- title="Toggle Compra Ágil Mode"
338
  >
339
  🚀 Agile
340
  </button>
@@ -343,131 +288,61 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
343
  )}
344
  </div>
345
 
346
- {/* Results Controls */}
347
  <div className="flex flex-col gap-3 px-2 md:flex-row md:items-center md:justify-between">
348
- <div className="flex flex-wrap items-center gap-3">
349
- <h3 className="text-lg font-bold text-white">
350
- {showOnlyFollowed ? "Saved Opportunities" : "Market Results"}
351
- </h3>
352
- <span className="text-[10px] bg-white/5 text-slate-400 px-2 py-0.5 rounded-full border border-white/5">
353
- {filteredTenders.length} items
354
- </span>
355
- {showLiveDefaultBadge && (
356
- <span className="text-[10px] bg-emerald-500/10 text-emerald-200 px-2 py-0.5 rounded-full border border-emerald-500/20 font-bold uppercase tracking-[0.25em]">
357
- Live Mercado Público data
358
- </span>
359
- )}
360
- </div>
361
  {followedCodes.length > 0 && (
362
  <button
363
  onClick={() => setShowOnlyFollowed(!showOnlyFollowed)}
364
- className={`flex items-center gap-2 rounded-xl px-4 py-2 text-xs font-bold transition-all border ${
365
- showOnlyFollowed
366
- ? "bg-purple-500/20 border-purple-500/40 text-purple-300"
367
- : "bg-white/5 border-white/10 text-slate-400 hover:border-white/20"
368
- }`}
369
  >
370
- {showOnlyFollowed ? "★ Viewing Portfolio" : "☆ Show Favorites Only"}
371
  </button>
372
  )}
373
  </div>
374
 
375
- {/* Results List */}
376
  <div className="space-y-4">
377
  {filteredTenders.length === 0 ? (
378
  <div className="flex flex-col items-center justify-center rounded-3xl border border-white/5 bg-white/[0.02] p-20 text-center">
379
- <div className="w-16 h-16 rounded-2xl bg-white/5 flex items-center justify-center text-3xl mb-4 opacity-50">
380
- {showOnlyFollowed ? "🌟" : "📡"}
381
- </div>
382
- <p className="text-slate-400 max-w-xs mx-auto text-sm">
383
- {showOnlyFollowed
384
- ? "No followed opportunities found."
385
- : "Enter keywords above to fetch real-time data."}
386
- </p>
387
  </div>
388
  ) : (
389
- <div className="glass-card rounded-3xl overflow-hidden shadow-2xl border border-white/5">
390
  <div className="overflow-x-auto custom-scrollbar">
391
- <table className="w-full text-left text-sm table-fixed border-collapse">
392
- <thead className="bg-white/5 text-slate-500 uppercase text-[10px] tracking-widest font-bold border-b border-white/5">
393
  <tr>
394
  <th className="px-4 py-5 w-[100px]">
395
- <div className="flex items-center gap-2">
396
- <input
397
- type="checkbox"
398
- checked={filteredTenders.length > 0 && selectedCodes.length === filteredTenders.length}
399
- onChange={toggleSelectAll}
400
- className="w-3.5 h-3.5 rounded border-white/10 bg-white/5 text-purple-500 focus:ring-purple-500/40"
401
- />
402
- {t.idSelect}
403
- </div>
404
  </th>
405
- <th className="px-4 py-5 w-[250px]">{t.opportunity}</th>
406
- <th className="px-4 py-5 w-[180px]">{t.buyer}</th>
407
- <th className="px-4 py-5 text-center w-[100px]">{t.status}</th>
408
  </tr>
409
  </thead>
410
  <tbody className="divide-y divide-white/5">
411
  {filteredTenders.map((tender) => (
412
- <tr
413
- key={tender.code}
414
- className={`hover:bg-white/[0.04] transition-colors group ${selectedCodes.includes(tender.code) ? 'bg-purple-500/5' : ''}`}
415
- >
416
- <td className="px-4 py-5" onClick={(e) => e.stopPropagation()}>
417
- <div className="flex items-center gap-2 pointer-events-auto">
418
- <input
419
- type="checkbox"
420
- checked={selectedCodes.includes(tender.code)}
421
- onChange={(e) => {
422
- e.stopPropagation();
423
- toggleSelect(tender.code);
424
- }}
425
- onClick={(e) => e.stopPropagation()}
426
- className="w-3.5 h-3.5 rounded border-white/10 bg-white/5 text-purple-500 focus:ring-purple-500/40 cursor-pointer"
427
- />
428
- <button
429
- onClick={(e) => {
430
- e.stopPropagation();
431
- toggleFollow(tender);
432
- }}
433
- className={`text-base transition-all hover:scale-125 cursor-pointer ${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'}`}
434
- >
435
  {followedCodes.includes(tender.code) ? "★" : "☆"}
436
  </button>
437
  <span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
438
- <a
439
- href={`https://www.mercadopublico.cl/fichaLicitacion.html?code=${tender.code}`}
440
- target="_blank"
441
- rel="noopener noreferrer"
442
- onClick={(e) => e.stopPropagation()}
443
- className="ml-2 text-[10px] text-slate-400 hover:text-white transition"
444
- title="Open Tender in Mercado Público"
445
- >
446
-
447
- </a>
448
  </div>
449
  </td>
450
- <td className="px-4 py-5 cursor-pointer hover:text-purple-400 transition-colors" onClick={() => setSelectedTenderForModal(tender)}>
451
  <div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate text-xs">{tender.name}</div>
452
- <div className="flex items-center gap-2 mt-1">
453
- <span className="text-[9px] text-slate-500 truncate">{tender.region || "Nacional"}</span>
454
- <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>
455
- </div>
456
  </td>
457
  <td className="px-4 py-5 text-slate-400 text-[11px] truncate">{tender.buyer}</td>
458
  <td className="px-4 py-5 text-center">
459
- <div className="flex flex-col items-center gap-1">
460
- <span className={`inline-block rounded-full px-2 py-0.5 text-[9px] font-bold ${
461
- tender.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400 border border-green-500/20' : 'bg-slate-800/50 text-slate-500'
462
- }`}>
463
- {tender.status}
464
- </span>
465
- {tender.type && (
466
- <span className="text-[8px] font-mono text-slate-600 bg-white/5 px-1 rounded">
467
- {tender.type}
468
- </span>
469
- )}
470
- </div>
471
  </td>
472
  </tr>
473
  ))}
@@ -479,251 +354,68 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
479
  </div>
480
  </>
481
  ) : (
482
- /* Immersive Detail View (Replaces List) */
483
  <div className="animate-in slide-in-from-right-8 fade-in duration-700 w-full max-w-[1600px] mx-auto pt-4 pb-20">
484
-
485
  <div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-8">
486
  <div className="flex flex-col gap-4">
487
- <button
488
- onClick={() => setSelectedTenderForModal(null)}
489
- className="flex items-center gap-2 text-slate-400 hover:text-white transition group w-fit"
490
- >
491
  <span className="text-xl group-hover:-translate-x-1 transition-transform">←</span>
492
- <span className="text-sm font-bold uppercase tracking-widest">{lang === 'en' ? 'Back to search' : 'Volver a búsqueda'}</span>
493
  </button>
494
-
495
  <div className="flex bg-white/5 p-1 rounded-2xl border border-white/10 w-fit">
496
- <button
497
- onClick={() => setActiveDetailTab("Overview")}
498
- className={`px-6 py-2.5 rounded-xl text-xs font-black uppercase tracking-widest transition-all ${activeDetailTab === "Overview" ? "bg-purple-600 text-white shadow-lg" : "text-slate-500 hover:text-slate-300"}`}
499
- >
500
- Overview
501
- </button>
502
- <button
503
- onClick={() => setActiveDetailTab("Agent Chat")}
504
- className={`px-6 py-2.5 rounded-xl text-xs font-black uppercase tracking-widest transition-all ${activeDetailTab === "Agent Chat" ? "bg-purple-600 text-white shadow-lg" : "text-slate-500 hover:text-slate-300"}`}
505
- >
506
- Agent Chat
507
- </button>
508
- </div>
509
- </div>
510
-
511
- <div className="grid grid-cols-2 gap-4 w-full md:w-auto">
512
- <div className="px-4 py-3 rounded-lg bg-white/5 border border-white/10">
513
- <div className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-400 mb-2">Search</div>
514
- <p className="text-xs text-slate-300">View detailed information</p>
515
- </div>
516
- <div className="px-4 py-3 rounded-lg bg-purple-500/5 border border-purple-500/20">
517
- <div className="text-[10px] font-black uppercase tracking-[0.2em] text-purple-300 mb-2">Save</div>
518
- <button
519
- onClick={() => toggleFollow(selectedTenderForModal)}
520
- className={`text-xs font-bold transition-all ${
521
- followedCodes.includes(selectedTenderForModal.code)
522
- ? "text-purple-300"
523
- : "text-slate-400 hover:text-purple-300"
524
- }`}
525
- >
526
- {followedCodes.includes(selectedTenderForModal.code) ? "★ Saved to Portfolio" : "☆ Save to Portfolio"}
527
- </button>
528
  </div>
529
  </div>
 
 
 
530
  </div>
531
 
532
  {activeDetailTab === "Overview" ? (
533
- <div className="glass-card rounded-[2.5rem] overflow-hidden border border-white/5 bg-slate-900/40 backdrop-blur-xl shadow-2xl">
534
- {/* Header Section */}
535
- <div className="p-10 md:p-14 border-b border-white/5 relative overflow-hidden">
536
- <div className="absolute top-0 right-0 w-64 h-64 bg-purple-500/10 blur-[100px] -translate-y-1/2 translate-x-1/2" />
537
- <div className="relative z-10">
538
- <div className="flex items-center gap-3 mb-6">
539
- <span className="text-sm font-mono text-purple-400 bg-purple-400/10 px-3 py-1 rounded-lg border border-purple-400/20">{selectedTenderForModal.code}</span>
540
- <span className={`px-3 py-1 rounded-lg text-xs font-black uppercase tracking-widest ${
541
- selectedTenderForModal.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400 border border-green-500/20' : 'bg-slate-800 text-slate-500'
542
- }`}>
543
- {selectedTenderForModal.status}
544
- </span>
545
- </div>
546
- <h3 className="text-3xl md:text-4xl font-black text-white leading-tight tracking-tight mb-4">{selectedTenderForModal.name}</h3>
547
-
548
- <div className="flex flex-wrap items-center gap-x-8 gap-y-4">
549
- <div className="flex items-center gap-3">
550
- <div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center text-lg">🏢</div>
551
- <span className="text-slate-400 font-medium">{selectedTenderForModal.buyer}</span>
552
- </div>
553
- <div className="flex items-center gap-3">
554
- <div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center text-lg">📍</div>
555
- <span className="text-slate-400 font-medium">{selectedTenderForModal.buyer_region || selectedTenderForModal.region || "Nacional"}</span>
556
- </div>
557
- <div className="flex items-center gap-3">
558
- <div className="w-8 h-8 rounded-full bg-white/5 flex items-center justify-center text-lg">🏷️</div>
559
- <span className="text-slate-400 font-medium">Type: {selectedTenderForModal.type || "N/A"}</span>
560
- </div>
561
- </div>
562
  </div>
563
  </div>
564
-
565
- {/* Content Section */}
566
  <div className="p-10 md:p-14">
567
  <div className="grid gap-16 lg:grid-cols-3">
568
- {/* Main Column */}
569
  <div className="lg:col-span-2 space-y-12">
570
  <section>
571
- <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-500 mb-6 flex items-center gap-3">
572
- <span className="w-8 h-[1px] bg-slate-700" />
573
- Project Scope & Description
574
- </h4>
575
- <div className="text-slate-300 leading-relaxed text-lg bg-white/[0.02] p-8 rounded-[2rem] border border-white/5 whitespace-pre-wrap font-light mb-12">
576
- {selectedTenderForModal.description || "No detailed description provided."}
577
  </div>
578
-
579
- {selectedTenderForModal.items && selectedTenderForModal.items.length > 0 && (
580
- <section>
581
- <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-500 mb-6 flex items-center gap-3">
582
- <span className="w-8 h-[1px] bg-slate-700" />
583
- Line Items & Requirements
584
- </h4>
585
- <div className="overflow-hidden rounded-3xl border border-white/5 bg-white/[0.01]">
586
- <table className="w-full text-left text-xs">
587
- <thead className="bg-white/5 text-slate-500 uppercase font-black tracking-tighter">
588
- <tr>
589
- <th className="px-6 py-4">Item / Product</th>
590
- <th className="px-6 py-4">Category</th>
591
- <th className="px-6 py-4 text-right">Quantity</th>
592
- </tr>
593
- </thead>
594
- <tbody className="divide-y divide-white/5">
595
- {selectedTenderForModal.items.map((item, idx) => (
596
- <tr key={idx} className="hover:bg-white/[0.02]">
597
- <td className="px-6 py-4">
598
- <div className="text-slate-200 font-bold">{item.name}</div>
599
- <div className="text-[10px] text-slate-500 mt-1">{item.description}</div>
600
- </td>
601
- <td className="px-6 py-4 text-slate-400">{item.category || "N/A"}</td>
602
- <td className="px-6 py-4 text-right">
603
- <span className="text-cyan font-mono font-bold">{item.quantity}</span>
604
- <span className="ml-1 text-slate-500 uppercase text-[10px]">{item.unit}</span>
605
- </td>
606
- </tr>
607
- ))}
608
- </tbody>
609
- </table>
610
- </div>
611
- </section>
612
- )}
613
  </section>
614
-
615
  <div className="grid grid-cols-2 gap-6">
616
- <div className="p-6 rounded-3xl bg-white/[0.03] border border-white/5 group hover:bg-white/[0.05] transition-colors">
617
- <div className="text-[10px] uppercase text-slate-500 font-black mb-2 tracking-widest">Estimated Investment</div>
618
- <div className="text-xl text-white font-bold tracking-tight">
619
- {selectedTenderForModal.estimated_amount
620
- ? new Intl.NumberFormat("es-CL", { style: "currency", currency: selectedTenderForModal.currency || "CLP" }).format(selectedTenderForModal.estimated_amount)
621
- : "Not Disclosed"}
622
  </div>
623
- {selectedTenderForModal.currency && selectedTenderForModal.currency !== 'CLP' && (
624
- <div className="text-[10px] text-cyan mt-1 font-bold">Currency: {selectedTenderForModal.currency}</div>
625
- )}
626
- </div>
627
- <div className="p-6 rounded-3xl bg-white/[0.03] border border-white/5 group hover:bg-white/[0.05] transition-colors">
628
- <div className="text-[10px] uppercase text-slate-500 font-black mb-2 tracking-widest">Industry Classification</div>
629
- <div className="text-xl text-white font-bold tracking-tight">{selectedTenderForModal.sector || "General Procurement"}</div>
630
  </div>
631
  </div>
632
  </div>
633
-
634
- {/* Sidebar Column */}
635
  <div className="space-y-12">
636
- <div className="p-8 rounded-[2rem] bg-purple-600/10 border border-purple-500/20 shadow-2xl shadow-purple-500/5 relative overflow-hidden group">
637
- <div className="absolute top-0 right-0 w-32 h-32 bg-purple-500/20 blur-[60px] opacity-0 group-hover:opacity-100 transition-opacity" />
638
  <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-purple-400 mb-6">Timeline</h4>
639
-
640
- <div className="space-y-4">
641
- <div>
642
- <div className="text-[10px] text-slate-500 font-bold uppercase tracking-widest mb-1">Closing Deadline</div>
643
- <div className="text-2xl font-black text-white font-mono">
644
- {selectedTenderForModal.closing_date ? new Date(selectedTenderForModal.closing_date).toLocaleDateString() : "---"}
645
- </div>
646
- </div>
647
-
648
- {selectedTenderForModal.publication_date && (
649
- <div>
650
- <div className="text-[10px] text-slate-500 font-bold uppercase tracking-widest mb-1">Published On</div>
651
- <div className="text-sm font-bold text-slate-300">
652
- {new Date(selectedTenderForModal.publication_date).toLocaleDateString()}
653
- </div>
654
- </div>
655
- )}
656
- </div>
657
-
658
- <p className="mt-6 text-[10px] text-purple-400/60 font-bold uppercase tracking-tighter border-t border-purple-400/10 pt-4">Final Window for Submission</p>
659
- </div>
660
-
661
- <div>
662
- <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-500 mb-6">Official Documentation</h4>
663
- <div className="grid gap-3">
664
- {selectedTenderForModal.attachments?.map((att, i) => (
665
- <a key={i} href={att.url} target="_blank" className="flex items-center justify-between p-5 rounded-2xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/5 transition-all group/file">
666
- <div className="flex items-center gap-4">
667
- <div className="w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center text-2xl">
668
- {att.name.endsWith('.pdf') ? "📕" : "📘"}
669
- </div>
670
- <div className="flex flex-col">
671
- <span className="text-sm font-bold text-slate-200 group-hover/file:text-white transition-colors truncate max-w-[150px]">{att.name}</span>
672
- <span className="text-[9px] text-slate-600 uppercase font-black">Official Basis</span>
673
- </div>
674
- </div>
675
- <span className="text-xl text-slate-600 group-hover/file:text-purple-400 transition-colors">↓</span>
676
- </a>
677
- ))}
678
- {!selectedTenderForModal.attachments?.length && (
679
- <div className="p-8 text-center rounded-2xl border border-white/5 bg-white/[0.01]">
680
- <p className="text-xs text-slate-600 italic font-medium">No external files registered.</p>
681
- </div>
682
- )}
683
- </div>
684
  </div>
685
-
686
- <a
687
- href={`https://www.mercadopublico.cl/fichaLicitacion.html?code=${selectedTenderForModal.code}`}
688
- target="_blank"
689
- className="flex items-center justify-center gap-3 w-full p-5 rounded-2xl bg-white/5 border border-white/10 text-white text-sm font-black uppercase tracking-widest hover:bg-white/10 transition-all shadow-xl active:scale-[0.98]"
690
  >
691
- Mercado Público Portal ↗
692
- </a>
693
- </div>
694
- </div>
695
- </div>
696
-
697
- {/* Action Footer */}
698
- <div className="p-10 md:p-14 bg-slate-950/40 border-t border-white/5 flex flex-col md:flex-row items-center justify-between gap-8">
699
- <div className="flex items-center gap-4">
700
- <div className="w-12 h-12 rounded-full bg-cyan/10 border border-cyan/20 flex items-center justify-center text-cyan">🤖</div>
701
- <div>
702
- <p className="text-white font-bold text-sm">Ready for Analysis</p>
703
- <p className="text-slate-500 text-xs">Run AI compliance check and opportunity fit assessment.</p>
704
  </div>
705
  </div>
706
- <div className="flex items-center gap-4 w-full md:w-auto">
707
- <button
708
- onClick={() => toggleFollow(selectedTenderForModal)}
709
- className={`flex-1 md:flex-none px-6 py-3 text-xs font-bold uppercase tracking-widest rounded-lg border transition-all ${
710
- followedCodes.includes(selectedTenderForModal.code)
711
- ? 'bg-purple-500/20 border-purple-500/40 text-purple-300'
712
- : 'border-white/10 text-slate-400 hover:border-purple-400/40 hover:text-purple-300'
713
- }`}
714
- >
715
- {followedCodes.includes(selectedTenderForModal.code) ? "★ In Portfolio" : "☆ Add to Portfolio"}
716
- </button>
717
- <button
718
- onClick={() => {
719
- onAnalyze(selectedTenderForModal);
720
- setSelectedTenderForModal(null);
721
- }}
722
- className="flex-1 md:flex-none premium-gradient text-white px-8 py-3 rounded-lg font-black text-xs uppercase tracking-[0.2em] shadow-xl shadow-purple-500/30 hover:scale-105 active:scale-95 transition-all"
723
- >
724
- {t.analyze}
725
- </button>
726
- </div>
727
  </div>
728
  </div>
729
  ) : (
 
41
  });
42
 
43
  const followedCodes = useMemo(() => followedTenders.map(t => t.code), [followedTenders]);
 
44
  const [showOnlyFollowed, setShowOnlyFollowed] = useState(forceShowFollowed);
45
  const [isLoading, setIsLoading] = useState(false);
46
  const [isAgileMode, setIsAgileMode] = useState(false);
47
  const isSearchPending = useRef(false);
48
  const itemsPerPage = 50;
49
 
50
+ const filteredTenders = useMemo(() => {
51
+ if (showOnlyFollowed) return followedTenders;
52
+ let list = tenders;
53
+ if (isAgileMode) {
54
+ list = list.filter(t =>
55
+ t.code.includes('COT26') ||
56
+ t.name.toLowerCase().includes('compra ágil') ||
57
+ t.sector?.toLowerCase().includes('agil')
58
+ );
59
+ }
60
+ return list;
61
+ }, [tenders, showOnlyFollowed, followedTenders, isAgileMode]);
62
+
63
  const isTenderCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
64
+ const isLiveSearch = Boolean(isTenderCode || providerCode || orgCode || status || date || typeCode);
65
+ const searchButtonLabel = isLoading ? "Searching..." : isLiveSearch ? "Live MP Search" : "Fetch Active Tenders";
 
 
 
 
 
66
 
67
  useEffect(() => {
68
  if (forceShowFollowed) setShowOnlyFollowed(true);
69
  }, [forceShowFollowed]);
70
 
71
  useEffect(() => {
72
+ if (initialKeyword && initialKeyword !== keyword) setKeyword(initialKeyword);
 
 
73
  }, [initialKeyword]);
74
 
75
  useEffect(() => {
76
  localStorage.setItem('andes_followed_tenders_full', JSON.stringify(followedTenders));
77
  }, [followedTenders]);
78
 
 
79
  useEffect(() => {
80
  if (selectedTenderForModal) {
81
  window.scrollTo({ top: 0, behavior: 'instant' });
 
87
  const toggleFollow = (tender: Tender) => {
88
  setFollowedTenders(prev => {
89
  const isFollowing = prev.some(t => t.code === tender.code);
90
+ return isFollowing ? prev.filter(t => t.code !== tender.code) : [...prev, tender];
 
 
 
 
91
  });
92
  };
93
 
94
  const toggleSelect = (code: string) => {
95
+ setSelectedCodes(prev => prev.includes(code) ? prev.filter(c => c !== code) : [...prev, code]);
 
 
96
  };
97
 
98
  const toggleSelectAll = () => {
99
+ setSelectedCodes(selectedCodes.length === filteredTenders.length ? [] : filteredTenders.map(t => t.code));
 
 
 
 
100
  };
101
 
102
  const handleSyncToAgents = () => {
 
115
  isSearchPending.current = true;
116
  setIsLoading(true);
117
  try {
 
118
  const isCode = /^[0-9]+-[0-9]+-[A-Z0-9]+$/i.test(keyword);
 
119
  await onSearch({
120
  keyword: isCode ? undefined : keyword,
121
  code: isCode ? keyword : undefined,
 
137
  }
138
  };
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  return (
141
  <div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700">
142
  {!selectedTenderForModal ? (
143
  <>
 
144
  <div className={`glass-card rounded-3xl p-8 mb-4 border transition-all duration-500 ${forceShowFollowed ? 'border-purple-500/30 bg-purple-500/5 shadow-[0_0_50px_rgba(168,85,247,0.1)]' : 'border-white/10'}`}>
145
  <div className="mb-6 flex justify-between items-start">
146
  <div>
 
175
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Keyword / Tender Code</label>
176
  <input
177
  type="text"
 
 
178
  placeholder="e.g. Software or 1509-5-L114"
179
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
180
  value={keyword}
181
  onChange={(e) => setKeyword(e.target.value)}
182
  />
 
185
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Buyer name</label>
186
  <input
187
  type="text"
 
 
188
  placeholder="e.g. Servicio de Salud"
189
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
190
  value={buyerCode}
191
  onChange={(e) => setBuyerCode(e.target.value)}
192
  />
 
195
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Provider Code</label>
196
  <input
197
  type="text"
 
 
198
  placeholder="e.g. 17793"
199
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
200
  value={providerCode}
201
  onChange={(e) => setProviderCode(e.target.value)}
202
  />
 
205
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Org Code</label>
206
  <input
207
  type="text"
 
 
208
  placeholder="e.g. 6945"
209
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all"
210
  value={orgCode}
211
  onChange={(e) => setOrgCode(e.target.value)}
212
  />
 
214
  <div className="space-y-2">
215
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Status</label>
216
  <select
217
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40"
218
  value={status}
219
  onChange={(e) => setStatus(e.target.value)}
220
  >
 
224
  <option value="6">Cerrada (6)</option>
225
  <option value="7">Desierta (7)</option>
226
  <option value="8">Adjudicada (8)</option>
 
 
227
  </select>
228
  </div>
229
  <div className="space-y-2">
230
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Tender Type</label>
231
  <select
232
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40"
233
  value={typeCode}
234
  onChange={(e) => setTypeCode(e.target.value)}
235
  >
236
  <option value="">Any type</option>
237
+ <option value="L1">L1 - < 100 UTM</option>
238
+ <option value="LE">LE - 100-1000 UTM</option>
239
+ <option value="LP">LP - > 1000 UTM</option>
240
  <option value="LS">LS - Servicios Personales</option>
241
  <option value="A1">A1 - Privada por Desierta</option>
 
 
242
  </select>
243
  </div>
244
  <div className="space-y-2">
245
  <label className="text-[10px] uppercase tracking-wider text-slate-500 font-bold px-1">Date</label>
246
  <input
247
  type="date"
248
+ className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500/40 [color-scheme:dark]"
249
  value={date}
250
  onChange={(e) => setDate(e.target.value)}
251
  />
252
  </div>
253
  </div>
254
+ <div className="rounded-3xl bg-slate-900/70 border border-white/10 p-4 text-slate-400 text-xs flex justify-between items-center">
255
  <div>
256
+ <span className="font-bold text-slate-200">Tip:</span> Use Tender Code for exact search or filters for direct live data.
257
  </div>
258
  <button
259
  type="button"
260
  onClick={() => {
261
+ setKeyword(""); setBuyerCode(""); setProviderCode(""); setOrgCode(""); setStatus(""); setDate(""); setTypeCode("");
 
 
 
 
 
 
262
  }}
263
  className="text-[10px] font-bold uppercase text-slate-500 hover:text-white transition"
264
  >
 
271
  type="button"
272
  disabled={isLoading}
273
  onClick={handleSearch}
274
+ className="w-full sm:w-auto premium-gradient text-white font-bold py-3.5 rounded-xl px-8 transition-all"
275
  >
276
  {searchButtonLabel}
277
  </button>
 
 
 
 
 
278
  </div>
279
  <button
280
  type="button"
281
  onClick={() => setIsAgileMode(!isAgileMode)}
282
+ className={`px-4 py-3.5 rounded-xl border font-bold text-[10px] uppercase tracking-widest transition-all ${isAgileMode ? "bg-cyan/20 border-cyan/40 text-cyan animate-pulse" : "bg-white/5 border-white/10 text-slate-500"}`}
 
 
 
 
 
283
  >
284
  🚀 Agile
285
  </button>
 
288
  )}
289
  </div>
290
 
 
291
  <div className="flex flex-col gap-3 px-2 md:flex-row md:items-center md:justify-between">
292
+ <h3 className="text-lg font-bold text-white">
293
+ {showOnlyFollowed ? "Portfolio" : "Market Results"} ({filteredTenders.length})
294
+ </h3>
 
 
 
 
 
 
 
 
 
 
295
  {followedCodes.length > 0 && (
296
  <button
297
  onClick={() => setShowOnlyFollowed(!showOnlyFollowed)}
298
+ className={`rounded-xl px-4 py-2 text-xs font-bold border transition-all ${showOnlyFollowed ? "bg-purple-500/20 border-purple-500/40 text-purple-300" : "bg-white/5 border-white/10 text-slate-400"}`}
 
 
 
 
299
  >
300
+ {showOnlyFollowed ? "★ Viewing Portfolio" : "☆ Show Favorites"}
301
  </button>
302
  )}
303
  </div>
304
 
 
305
  <div className="space-y-4">
306
  {filteredTenders.length === 0 ? (
307
  <div className="flex flex-col items-center justify-center rounded-3xl border border-white/5 bg-white/[0.02] p-20 text-center">
308
+ <p className="text-slate-400 text-sm">No opportunities found.</p>
 
 
 
 
 
 
 
309
  </div>
310
  ) : (
311
+ <div className="glass-card rounded-3xl overflow-hidden border border-white/5">
312
  <div className="overflow-x-auto custom-scrollbar">
313
+ <table className="w-full text-left text-sm table-fixed">
314
+ <thead className="bg-white/5 text-slate-500 uppercase text-[10px] font-bold border-b border-white/5">
315
  <tr>
316
  <th className="px-4 py-5 w-[100px]">
317
+ <input type="checkbox" checked={selectedCodes.length === filteredTenders.length} onChange={toggleSelectAll} className="mr-2" />
318
+ ID
 
 
 
 
 
 
 
319
  </th>
320
+ <th className="px-4 py-5 w-[250px]">Opportunity</th>
321
+ <th className="px-4 py-5 w-[180px]">Buyer</th>
322
+ <th className="px-4 py-5 text-center w-[100px]">Status</th>
323
  </tr>
324
  </thead>
325
  <tbody className="divide-y divide-white/5">
326
  {filteredTenders.map((tender) => (
327
+ <tr key={tender.code} className="hover:bg-white/[0.04] transition-colors group">
328
+ <td className="px-4 py-5">
329
+ <div className="flex items-center gap-2">
330
+ <input type="checkbox" checked={selectedCodes.includes(tender.code)} onChange={() => toggleSelect(tender.code)} />
331
+ <button onClick={() => toggleFollow(tender)} className={followedCodes.includes(tender.code) ? 'text-purple-400' : 'text-slate-600'}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  {followedCodes.includes(tender.code) ? "★" : "☆"}
333
  </button>
334
  <span className="font-mono text-purple-400 text-[9px] truncate">{tender.code}</span>
 
 
 
 
 
 
 
 
 
 
335
  </div>
336
  </td>
337
+ <td className="px-4 py-5 cursor-pointer" onClick={() => setSelectedTenderForModal(tender)}>
338
  <div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate text-xs">{tender.name}</div>
339
+ <div className="text-[9px] text-slate-500">{tender.region || "Nacional"}</div>
 
 
 
340
  </td>
341
  <td className="px-4 py-5 text-slate-400 text-[11px] truncate">{tender.buyer}</td>
342
  <td className="px-4 py-5 text-center">
343
+ <span className={`inline-block rounded-full px-2 py-0.5 text-[9px] font-bold ${tender.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400' : 'bg-slate-800 text-slate-500'}`}>
344
+ {tender.status}
345
+ </span>
 
 
 
 
 
 
 
 
 
346
  </td>
347
  </tr>
348
  ))}
 
354
  </div>
355
  </>
356
  ) : (
 
357
  <div className="animate-in slide-in-from-right-8 fade-in duration-700 w-full max-w-[1600px] mx-auto pt-4 pb-20">
 
358
  <div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-8">
359
  <div className="flex flex-col gap-4">
360
+ <button onClick={() => setSelectedTenderForModal(null)} className="flex items-center gap-2 text-slate-400 hover:text-white transition group w-fit">
 
 
 
361
  <span className="text-xl group-hover:-translate-x-1 transition-transform">←</span>
362
+ <span className="text-sm font-bold uppercase tracking-widest">Back to search</span>
363
  </button>
 
364
  <div className="flex bg-white/5 p-1 rounded-2xl border border-white/10 w-fit">
365
+ <button onClick={() => setActiveDetailTab("Overview")} className={`px-6 py-2.5 rounded-xl text-xs font-black uppercase tracking-widest transition-all ${activeDetailTab === "Overview" ? "bg-purple-600 text-white" : "text-slate-500"}`}>Overview</button>
366
+ <button onClick={() => setActiveDetailTab("Agent Chat")} className={`px-6 py-2.5 rounded-xl text-xs font-black uppercase tracking-widest transition-all ${activeDetailTab === "Agent Chat" ? "bg-purple-600 text-white" : "text-slate-500"}`}>Agent Chat</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  </div>
368
  </div>
369
+ <button onClick={() => toggleFollow(selectedTenderForModal)} className="px-4 py-3 rounded-lg bg-white/5 border border-white/10 text-xs text-slate-400">
370
+ {followedCodes.includes(selectedTenderForModal.code) ? "★ Saved" : "☆ Save"}
371
+ </button>
372
  </div>
373
 
374
  {activeDetailTab === "Overview" ? (
375
+ <div className="glass-card rounded-[2.5rem] overflow-hidden border border-white/5 bg-slate-900/40 backdrop-blur-xl">
376
+ <div className="p-10 md:p-14 border-b border-white/5 relative">
377
+ <div className="flex items-center gap-3 mb-6">
378
+ <span className="text-sm font-mono text-purple-400 bg-purple-400/10 px-3 py-1 rounded-lg">{selectedTenderForModal.code}</span>
379
+ <span className="px-3 py-1 rounded-lg text-xs font-black uppercase bg-green-500/10 text-green-400">{selectedTenderForModal.status}</span>
380
+ </div>
381
+ <h3 className="text-3xl md:text-4xl font-black text-white leading-tight mb-4">{selectedTenderForModal.name}</h3>
382
+ <div className="flex flex-wrap gap-8">
383
+ <span className="text-slate-400">🏢 {selectedTenderForModal.buyer}</span>
384
+ <span className="text-slate-400">📍 {selectedTenderForModal.buyer_region || "Nacional"}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  </div>
386
  </div>
 
 
387
  <div className="p-10 md:p-14">
388
  <div className="grid gap-16 lg:grid-cols-3">
 
389
  <div className="lg:col-span-2 space-y-12">
390
  <section>
391
+ <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-500 mb-6">Description</h4>
392
+ <div className="text-slate-300 leading-relaxed text-lg bg-white/[0.02] p-8 rounded-[2rem] border border-white/5 whitespace-pre-wrap">
393
+ {selectedTenderForModal.description || "No description."}
 
 
 
394
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  </section>
 
396
  <div className="grid grid-cols-2 gap-6">
397
+ <div className="p-6 rounded-3xl bg-white/[0.03] border border-white/5">
398
+ <div className="text-[10px] uppercase text-slate-500 font-black mb-2">Estimated Investment</div>
399
+ <div className="text-xl text-white font-bold">
400
+ {selectedTenderForModal.estimated_amount ? new Intl.NumberFormat("es-CL", { style: "currency", currency: "CLP" }).format(selectedTenderForModal.estimated_amount) : "Not Disclosed"}
 
 
401
  </div>
 
 
 
 
 
 
 
402
  </div>
403
  </div>
404
  </div>
 
 
405
  <div className="space-y-12">
406
+ <div className="p-8 rounded-[2rem] bg-purple-600/10 border border-purple-500/20">
 
407
  <h4 className="text-[10px] font-black uppercase tracking-[0.3em] text-purple-400 mb-6">Timeline</h4>
408
+ <div className="text-[10px] text-slate-500 font-bold uppercase mb-1">Closing Deadline</div>
409
+ <div className="text-2xl font-black text-white font-mono">{selectedTenderForModal.closing_date ? new Date(selectedTenderForModal.closing_date).toLocaleDateString() : "---"}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  </div>
411
+ <button
412
+ onClick={() => { onAnalyze(selectedTenderForModal); setSelectedTenderForModal(null); }}
413
+ className="w-full premium-gradient text-white px-8 py-5 rounded-2xl font-black uppercase tracking-widest"
 
 
414
  >
415
+ Analyze Opportunity
416
+ </button>
 
 
 
 
 
 
 
 
 
 
 
417
  </div>
418
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  </div>
420
  </div>
421
  ) : (