Álvaro Valenzuela Valdes commited on
Commit ·
2f5e8ea
1
Parent(s): 26fba59
fix: remove accidental backslashes in AgentAnalysis.tsx to fix build
Browse files- frontend/components/AgentAnalysis.tsx +177 -177
frontend/components/AgentAnalysis.tsx
CHANGED
|
@@ -254,20 +254,20 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 254 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 255 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Investment</p>
|
| 256 |
<p className="text-lg font-black text-white">
|
| 257 |
-
{tender.estimated_amount ? new Intl.NumberFormat(
|
| 258 |
</p>
|
| 259 |
</div>
|
| 260 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 261 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Closing Date</p>
|
| 262 |
-
<p className="text-lg font-black text-white">{tender.closing_date ||
|
| 263 |
</div>
|
| 264 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 265 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Region</p>
|
| 266 |
-
<p className="text-lg font-black text-white truncate" title={tender.region}>{tender.region ||
|
| 267 |
</div>
|
| 268 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 269 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Sector</p>
|
| 270 |
-
<p className="text-lg font-black text-white truncate">{tender.sector ||
|
| 271 |
</div>
|
| 272 |
</div>
|
| 273 |
)}
|
|
@@ -288,7 +288,7 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 288 |
<div>
|
| 289 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Purchases Executed</p>
|
| 290 |
<p className="text-2xl font-black text-cyan">
|
| 291 |
-
{tender?.buyer_purchases ?? tenderDetails?.metadata?.buyer_purchases ??
|
| 292 |
</p>
|
| 293 |
</div>
|
| 294 |
<div className="text-2xl opacity-50">🛒</div>
|
|
@@ -298,32 +298,32 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 298 |
|
| 299 |
{tender?.description && (
|
| 300 |
<div className="mt-8 p-6 rounded-2xl bg-white/[0.02] border border-white/5">
|
| 301 |
-
<h4 className=
|
| 302 |
-
<p className=
|
| 303 |
{tender.description}
|
| 304 |
</p>
|
| 305 |
</div>
|
| 306 |
)}
|
| 307 |
|
| 308 |
{tender?.items && tender.items.length > 0 && (
|
| 309 |
-
<div className=
|
| 310 |
-
<table className=
|
| 311 |
-
<thead className=
|
| 312 |
<tr>
|
| 313 |
-
<th className=
|
| 314 |
-
<th className=
|
| 315 |
</tr>
|
| 316 |
</thead>
|
| 317 |
-
<tbody className=
|
| 318 |
{tender.items.slice(0, 3).map((item, idx) => (
|
| 319 |
-
<tr key={idx} className=
|
| 320 |
-
<td className=
|
| 321 |
-
<td className=
|
| 322 |
</tr>
|
| 323 |
))}
|
| 324 |
{tender.items.length > 3 && (
|
| 325 |
<tr>
|
| 326 |
-
<td colSpan={2} className=
|
| 327 |
+ {tender.items.length - 3} more items...
|
| 328 |
</td>
|
| 329 |
</tr>
|
|
@@ -335,25 +335,25 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 335 |
|
| 336 |
{/* Scraped Intelligence / Tabs */}
|
| 337 |
{tenderDetails && (
|
| 338 |
-
<div className=
|
| 339 |
{tenderDetails.tabs?.history?.found && (
|
| 340 |
-
<div className=
|
| 341 |
-
<span className=
|
| 342 |
</div>
|
| 343 |
)}
|
| 344 |
{tenderDetails.tabs?.questions?.found && (
|
| 345 |
-
<div className=
|
| 346 |
-
<span className=
|
| 347 |
</div>
|
| 348 |
)}
|
| 349 |
{tenderDetails.tabs?.opening?.found && (
|
| 350 |
-
<div className=
|
| 351 |
-
<span className=
|
| 352 |
</div>
|
| 353 |
)}
|
| 354 |
{tenderDetails.metadata?.has_adjudication && (
|
| 355 |
-
<div className=
|
| 356 |
-
<span className=
|
| 357 |
</div>
|
| 358 |
)}
|
| 359 |
</div>
|
|
@@ -361,33 +361,33 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 361 |
|
| 362 |
{/* Scraped Attachments (Extended List) */}
|
| 363 |
{tenderDetails?.attachments && tenderDetails.attachments.length > 0 && (
|
| 364 |
-
<div className=
|
| 365 |
-
<div className=
|
| 366 |
-
<h4 className=
|
| 367 |
-
{isLoadingDetails && <span className=
|
| 368 |
</div>
|
| 369 |
-
<div className=
|
| 370 |
{tenderDetails.attachments.slice(0, 6).map((att, idx) => (
|
| 371 |
<a
|
| 372 |
key={idx}
|
| 373 |
href={att.url}
|
| 374 |
-
target=
|
| 375 |
-
rel=
|
| 376 |
-
className=
|
| 377 |
>
|
| 378 |
-
<span className=
|
| 379 |
{att.name.toLowerCase().includes('bases') ? '⚖️' :
|
| 380 |
att.name.toLowerCase().includes('tecnico') ? '🛠️' :
|
| 381 |
att.name.toLowerCase().includes('anexo') ? '📝' : '📄'}
|
| 382 |
</span>
|
| 383 |
-
<div className=
|
| 384 |
-
<p className=
|
| 385 |
-
<p className=
|
| 386 |
</div>
|
| 387 |
</a>
|
| 388 |
))}
|
| 389 |
{tenderDetails.attachments.length > 6 && (
|
| 390 |
-
<div className=
|
| 391 |
+ {tenderDetails.attachments.length - 6} more attachments
|
| 392 |
</div>
|
| 393 |
)}
|
|
@@ -396,12 +396,12 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 396 |
)}
|
| 397 |
</div>
|
| 398 |
|
| 399 |
-
<div className=
|
| 400 |
-
<div className=
|
| 401 |
-
<h4 className=
|
| 402 |
|
| 403 |
{/* The Corral (Animal Pen) */}
|
| 404 |
-
<div className=
|
| 405 |
{corral.map((item) => {
|
| 406 |
const icon = getFileIcon(item.file.name);
|
| 407 |
return (
|
|
@@ -414,58 +414,58 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 414 |
<span className={`text-2xl transition-all duration-500 group-hover:rotate-12 ${activeAnimalId === item.id ? 'animate-bounce' : 'group-hover:animate-wiggle'}`}>
|
| 415 |
{icon.animal}
|
| 416 |
</span>
|
| 417 |
-
<span className=
|
| 418 |
-
{item.analysis && <span className=
|
| 419 |
</button>
|
| 420 |
);
|
| 421 |
})}
|
| 422 |
|
| 423 |
-
<label className=
|
| 424 |
-
<span className=
|
| 425 |
-
<input type=
|
| 426 |
</label>
|
| 427 |
</div>
|
| 428 |
|
| 429 |
-
<div className=
|
| 430 |
-
{corral.length === 0 ?
|
| 431 |
</div>
|
| 432 |
|
| 433 |
-
{isUploading && <p className=
|
| 434 |
</div>
|
| 435 |
|
| 436 |
-
<label className=
|
| 437 |
-
<input type=
|
| 438 |
-
<span className=
|
| 439 |
</label>
|
| 440 |
|
| 441 |
<button
|
| 442 |
onClick={handleAnalyzeClick}
|
| 443 |
disabled={!tender || !approved || isRunning || !activeAnimalId}
|
| 444 |
-
className=
|
| 445 |
>
|
| 446 |
-
{isRunning ?
|
| 447 |
</button>
|
| 448 |
</div>
|
| 449 |
</div>
|
| 450 |
</div>
|
| 451 |
|
| 452 |
{/* Agents Row (Visual feedback & Configuration) */}
|
| 453 |
-
<div className=
|
| 454 |
{agents.map((agent) => (
|
| 455 |
-
<div key={agent.id} className=
|
| 456 |
<div className={`glass-card rounded-3xl p-6 flex items-center gap-4 transition-all duration-700 ${isRunning ? 'ring-2 ring-purple-500/50 animate-pulse' : ''} ${analysis ? 'border-purple-500/30' : 'border-white/5'} hover:border-purple-500/20`}>
|
| 457 |
<div className={`text-4xl ${isRunning ? 'animate-bounce' : ''}`}>{agent.avatar}</div>
|
| 458 |
-
<div className=
|
| 459 |
<div className={`text-[10px] font-bold uppercase tracking-widest ${agent.color}`}>{agent.role}</div>
|
| 460 |
-
<div className=
|
| 461 |
-
<div className=
|
| 462 |
-
<span className=
|
| 463 |
{agentModels[agent.id as keyof typeof agentModels]}
|
| 464 |
</div>
|
| 465 |
</div>
|
| 466 |
<button
|
| 467 |
onClick={() => setActiveSettings(activeSettings === agent.id ? null : agent.id)}
|
| 468 |
-
className=
|
| 469 |
>
|
| 470 |
⚙️
|
| 471 |
</button>
|
|
@@ -473,18 +473,18 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 473 |
|
| 474 |
{/* Model Selector Popover */}
|
| 475 |
{activeSettings === agent.id && (
|
| 476 |
-
<div className=
|
| 477 |
-
<p className=
|
| 478 |
-
<div className=
|
| 479 |
{[
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
].map(model => (
|
| 489 |
<button
|
| 490 |
key={model}
|
|
@@ -495,7 +495,7 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 495 |
className={`w-full text-left px-4 py-3 rounded-xl text-sm font-medium transition-all flex items-center justify-between border ${agentModels[agent.id as keyof typeof agentModels] === model ? 'bg-purple-500/20 text-white border-purple-500/50 shadow-lg shadow-purple-500/10' : 'text-slate-400 border-transparent hover:bg-white/10 hover:text-white hover:border-white/10'}`}
|
| 496 |
>
|
| 497 |
<span>{model}</span>
|
| 498 |
-
{agentModels[agent.id as keyof typeof agentModels] === model && <span className=
|
| 499 |
</button>
|
| 500 |
))}
|
| 501 |
</div>
|
|
@@ -507,16 +507,16 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 507 |
|
| 508 |
{/* Running State Log */}
|
| 509 |
{isRunning && (
|
| 510 |
-
<div className=
|
| 511 |
-
<div className=
|
| 512 |
-
<div className=
|
| 513 |
-
<h3 className=
|
| 514 |
</div>
|
| 515 |
-
<div className=
|
| 516 |
{statusLog.map((log, i) => (
|
| 517 |
-
<div key={i} className=
|
| 518 |
-
<span className=
|
| 519 |
-
<p className=
|
| 520 |
</div>
|
| 521 |
))}
|
| 522 |
</div>
|
|
@@ -525,15 +525,15 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 525 |
|
| 526 |
{/* Error State */}
|
| 527 |
{error && (
|
| 528 |
-
<div className=
|
| 529 |
-
<div className=
|
| 530 |
-
<span className=
|
| 531 |
-
<h3 className=
|
| 532 |
</div>
|
| 533 |
-
<p className=
|
| 534 |
<button
|
| 535 |
onClick={handleAnalyzeClick}
|
| 536 |
-
className=
|
| 537 |
>
|
| 538 |
Retry Analysis
|
| 539 |
</button>
|
|
@@ -542,26 +542,26 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 542 |
|
| 543 |
{/* Analysis Results View */}
|
| 544 |
{activeAnalysis && (
|
| 545 |
-
<div id=
|
| 546 |
-
<div className=
|
| 547 |
-
<div className=
|
| 548 |
-
<div className=
|
| 549 |
<div>
|
| 550 |
-
<div className=
|
| 551 |
-
<h3 className=
|
| 552 |
-
<div className=
|
| 553 |
-
<span className=
|
| 554 |
-
<span className=
|
| 555 |
</div>
|
| 556 |
</div>
|
| 557 |
-
<div className=
|
| 558 |
<div className={`rounded-2xl px-6 py-3 text-[10px] font-black uppercase tracking-widest shadow-lg ${activeAnalysis.decision === 'Recommended' ? 'bg-green-500/20 text-green-400 border border-green-500/30 shadow-green-500/10' : 'bg-amber-500/20 text-amber-400 border border-amber-500/30 shadow-amber-500/10'}`}>
|
| 559 |
{activeAnalysis.decision}
|
| 560 |
</div>
|
| 561 |
-
<div className=
|
| 562 |
<button
|
| 563 |
onClick={() => window.print()}
|
| 564 |
-
className=
|
| 565 |
>
|
| 566 |
Export PDF
|
| 567 |
</button>
|
|
@@ -573,36 +573,36 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 573 |
{isGeneratingAnnexes ? 'Generating...' : '✨ Anexos Express'}
|
| 574 |
</button>
|
| 575 |
<button
|
| 576 |
-
onClick={() => alert(
|
| 577 |
-
className=
|
| 578 |
-
title=
|
| 579 |
>
|
| 580 |
📧
|
| 581 |
</button>
|
| 582 |
</div>
|
| 583 |
</div>
|
| 584 |
</div>
|
| 585 |
-
<div className=
|
| 586 |
-
<p className=
|
| 587 |
</div>
|
| 588 |
|
| 589 |
{/* Requirement Q&A Section */}
|
| 590 |
{activeAnalysis.requirement_responses && activeAnalysis.requirement_responses.length > 0 && (
|
| 591 |
-
<div className=
|
| 592 |
-
<div className=
|
| 593 |
-
<span className=
|
| 594 |
-
<h4 className=
|
| 595 |
</div>
|
| 596 |
-
<div className=
|
| 597 |
{activeAnalysis.requirement_responses.map((item, i) => (
|
| 598 |
-
<div key={i} className=
|
| 599 |
-
<div className=
|
| 600 |
-
<span className=
|
| 601 |
-
<p className=
|
| 602 |
</div>
|
| 603 |
-
<div className=
|
| 604 |
-
<span className=
|
| 605 |
-
<p className=
|
| 606 |
</div>
|
| 607 |
</div>
|
| 608 |
))}
|
|
@@ -612,20 +612,20 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 612 |
|
| 613 |
{/* Proposal Draft Section */}
|
| 614 |
{activeAnalysis.proposal_draft && (
|
| 615 |
-
<div className=
|
| 616 |
-
<div className=
|
| 617 |
-
<h4 className=
|
| 618 |
<button
|
| 619 |
onClick={() => {
|
| 620 |
navigator.clipboard.writeText(activeAnalysis.proposal_draft);
|
| 621 |
-
alert(
|
| 622 |
}}
|
| 623 |
-
className=
|
| 624 |
>
|
| 625 |
Copy to Clipboard 📋
|
| 626 |
</button>
|
| 627 |
</div>
|
| 628 |
-
<div className=
|
| 629 |
{activeAnalysis.proposal_draft}
|
| 630 |
</div>
|
| 631 |
</div>
|
|
@@ -633,54 +633,54 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 633 |
</div>
|
| 634 |
|
| 635 |
|
| 636 |
-
<div className=
|
| 637 |
-
<div className=
|
| 638 |
-
<h4 className=
|
| 639 |
<span>⚠️</span> Legal Compliance Gaps
|
| 640 |
</h4>
|
| 641 |
-
<ul className=
|
| 642 |
{activeAnalysis.compliance_gaps.map((gap, i) => (
|
| 643 |
-
<li key={i} className=
|
| 644 |
-
<span className=
|
| 645 |
</li>
|
| 646 |
))}
|
| 647 |
</ul>
|
| 648 |
</div>
|
| 649 |
-
<div className=
|
| 650 |
-
<h4 className=
|
| 651 |
<span>💎</span> Technical Requirements
|
| 652 |
</h4>
|
| 653 |
-
<ul className=
|
| 654 |
{activeAnalysis.key_requirements.map((req, i) => (
|
| 655 |
-
<li key={i} className=
|
| 656 |
-
<span className=
|
| 657 |
</li>
|
| 658 |
))}
|
| 659 |
</ul>
|
| 660 |
</div>
|
| 661 |
</div>
|
| 662 |
|
| 663 |
-
<div className=
|
| 664 |
-
<h4 className=
|
| 665 |
-
<div className=
|
| 666 |
{activeAnalysis.risks.map((risk, i) => (
|
| 667 |
-
<div key={i} className=
|
| 668 |
-
<div className=
|
| 669 |
-
<span className=
|
| 670 |
<span className={`text-[9px] font-black px-3 py-1 rounded-full uppercase tracking-widest ${risk.severity === 'High' ? 'bg-red-500/20 text-red-500 border border-red-500/20' : 'bg-white/5 text-slate-500 border border-white/5'}`}>{risk.severity}</span>
|
| 671 |
</div>
|
| 672 |
-
<p className=
|
| 673 |
</div>
|
| 674 |
))}
|
| 675 |
</div>
|
| 676 |
|
| 677 |
{activeAnalysis.strategic_roadmap && (
|
| 678 |
-
<div className=
|
| 679 |
-
<h4 className=
|
| 680 |
-
<div className=
|
| 681 |
-
<div className=
|
| 682 |
{activeAnalysis.strategic_roadmap.split('\n').map((line, i) => (
|
| 683 |
-
<p key={i} className=
|
| 684 |
))}
|
| 685 |
</div>
|
| 686 |
</div>
|
|
@@ -690,21 +690,21 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 690 |
|
| 691 |
</div>
|
| 692 |
|
| 693 |
-
<div className=
|
| 694 |
-
<div className=
|
| 695 |
-
<div className=
|
| 696 |
-
<div className=
|
| 697 |
-
<h4 className=
|
| 698 |
</div>
|
| 699 |
-
<div className=
|
| 700 |
{activeAnalysis.audit_log?.map((log, i) => (
|
| 701 |
-
<div key={i} className=
|
| 702 |
-
<div className=
|
| 703 |
-
<div className=
|
| 704 |
-
{i < (activeAnalysis.audit_log?.length ?? 0) - 1 && <div className=
|
| 705 |
</div>
|
| 706 |
-
<div className=
|
| 707 |
-
<p className=
|
| 708 |
</div>
|
| 709 |
</div>
|
| 710 |
))}
|
|
@@ -713,23 +713,23 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 713 |
|
| 714 |
{/* Anexos Express Section */}
|
| 715 |
{generatedAnnexes.length > 0 && (
|
| 716 |
-
<div id=
|
| 717 |
-
<div className=
|
| 718 |
-
<div className=
|
| 719 |
<div>
|
| 720 |
-
<h4 className=
|
| 721 |
-
<p className=
|
| 722 |
</div>
|
| 723 |
</div>
|
| 724 |
|
| 725 |
-
<div className=
|
| 726 |
{generatedAnnexes.map((annex, i) => (
|
| 727 |
-
<div key={i} className=
|
| 728 |
-
<div className=
|
| 729 |
-
<h5 className=
|
| 730 |
-
<div className=
|
| 731 |
-
<pre className=
|
| 732 |
-
<div className=
|
| 733 |
</div>
|
| 734 |
<button
|
| 735 |
onClick={() => {
|
|
@@ -740,7 +740,7 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 740 |
a.download = `${annex.name.replace(/ /g, '_')}.md`;
|
| 741 |
a.click();
|
| 742 |
}}
|
| 743 |
-
className=
|
| 744 |
>
|
| 745 |
Download .md 📥
|
| 746 |
</button>
|
|
@@ -755,12 +755,12 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
|
|
| 755 |
|
| 756 |
{/* Expert Consultation Chat */}
|
| 757 |
{tender && (
|
| 758 |
-
<div className=
|
| 759 |
-
<div className=
|
| 760 |
-
<div className=
|
| 761 |
<div>
|
| 762 |
-
<h3 className=
|
| 763 |
-
<p className=
|
| 764 |
</div>
|
| 765 |
</div>
|
| 766 |
<AgentChat tender={tender} companyProfile={companyProfile} />
|
|
|
|
| 254 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 255 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Investment</p>
|
| 256 |
<p className="text-lg font-black text-white">
|
| 257 |
+
{tender.estimated_amount ? new Intl.NumberFormat("es-CL", { style: "currency", currency: tender.currency || "CLP", maximumFractionDigits: 0 }).format(tender.estimated_amount) : "N/A"}
|
| 258 |
</p>
|
| 259 |
</div>
|
| 260 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 261 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Closing Date</p>
|
| 262 |
+
<p className="text-lg font-black text-white">{tender.closing_date || "TBD"}</p>
|
| 263 |
</div>
|
| 264 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 265 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Region</p>
|
| 266 |
+
<p className="text-lg font-black text-white truncate" title={tender.region}>{tender.region || "Nacional"}</p>
|
| 267 |
</div>
|
| 268 |
<div className="rounded-2xl bg-white/5 p-4 border border-white/5 group hover:bg-white/10 transition-colors">
|
| 269 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Sector</p>
|
| 270 |
+
<p className="text-lg font-black text-white truncate">{tender.sector || "General"}</p>
|
| 271 |
</div>
|
| 272 |
</div>
|
| 273 |
)}
|
|
|
|
| 288 |
<div>
|
| 289 |
<p className="text-[10px] uppercase text-slate-500 font-bold mb-1 tracking-widest">Purchases Executed</p>
|
| 290 |
<p className="text-2xl font-black text-cyan">
|
| 291 |
+
{tender?.buyer_purchases ?? tenderDetails?.metadata?.buyer_purchases ?? "1.6k+"}
|
| 292 |
</p>
|
| 293 |
</div>
|
| 294 |
<div className="text-2xl opacity-50">🛒</div>
|
|
|
|
| 298 |
|
| 299 |
{tender?.description && (
|
| 300 |
<div className="mt-8 p-6 rounded-2xl bg-white/[0.02] border border-white/5">
|
| 301 |
+
<h4 className="text-[10px] font-bold uppercase text-slate-500 mb-3 tracking-[0.2em]">Detailed Scope</h4>
|
| 302 |
+
<p className="text-sm text-slate-400 leading-relaxed max-h-32 overflow-y-auto custom-scrollbar pr-2 whitespace-pre-wrap">
|
| 303 |
{tender.description}
|
| 304 |
</p>
|
| 305 |
</div>
|
| 306 |
)}
|
| 307 |
|
| 308 |
{tender?.items && tender.items.length > 0 && (
|
| 309 |
+
<div className="mt-8 overflow-hidden rounded-2xl border border-white/5 bg-white/[0.01]">
|
| 310 |
+
<table className="w-full text-left text-[10px]">
|
| 311 |
+
<thead className="bg-white/5 text-slate-500 uppercase font-black tracking-widest">
|
| 312 |
<tr>
|
| 313 |
+
<th className="px-6 py-3">Item Name</th>
|
| 314 |
+
<th className="px-6 py-3 text-right">Qty</th>
|
| 315 |
</tr>
|
| 316 |
</thead>
|
| 317 |
+
<tbody className="divide-y divide-white/5">
|
| 318 |
{tender.items.slice(0, 3).map((item, idx) => (
|
| 319 |
+
<tr key={idx} className="hover:bg-white/[0.02]">
|
| 320 |
+
<td className="px-6 py-3 text-slate-300 font-medium truncate max-w-[200px]">{item.name}</td>
|
| 321 |
+
<td className="px-6 py-3 text-right text-cyan font-mono font-bold">{item.quantity} {item.unit}</td>
|
| 322 |
</tr>
|
| 323 |
))}
|
| 324 |
{tender.items.length > 3 && (
|
| 325 |
<tr>
|
| 326 |
+
<td colSpan={2} className="px-6 py-2 text-center text-[9px] text-slate-600 italic">
|
| 327 |
+ {tender.items.length - 3} more items...
|
| 328 |
</td>
|
| 329 |
</tr>
|
|
|
|
| 335 |
|
| 336 |
{/* Scraped Intelligence / Tabs */}
|
| 337 |
{tenderDetails && (
|
| 338 |
+
<div className="mt-8 flex flex-wrap gap-4">
|
| 339 |
{tenderDetails.tabs?.history?.found && (
|
| 340 |
+
<div className="flex items-center gap-2 px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-[10px] font-bold text-slate-400">
|
| 341 |
+
<span className="text-purple-400 text-xs">📜</span> History Available
|
| 342 |
</div>
|
| 343 |
)}
|
| 344 |
{tenderDetails.tabs?.questions?.found && (
|
| 345 |
+
<div className="flex items-center gap-2 px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-[10px] font-bold text-slate-400">
|
| 346 |
+
<span className="text-cyan text-xs">❓</span> Q&A Active
|
| 347 |
</div>
|
| 348 |
)}
|
| 349 |
{tenderDetails.tabs?.opening?.found && (
|
| 350 |
+
<div className="flex items-center gap-2 px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-[10px] font-bold text-slate-400">
|
| 351 |
+
<span className="text-green-400 text-xs">🔓</span> Opening Log Found
|
| 352 |
</div>
|
| 353 |
)}
|
| 354 |
{tenderDetails.metadata?.has_adjudication && (
|
| 355 |
+
<div className="flex items-center gap-2 px-4 py-2 rounded-xl bg-green-500/10 border border-green-500/20 text-[10px] font-bold text-green-400">
|
| 356 |
+
<span className="text-xs">🏆</span> Adjudicated
|
| 357 |
</div>
|
| 358 |
)}
|
| 359 |
</div>
|
|
|
|
| 361 |
|
| 362 |
{/* Scraped Attachments (Extended List) */}
|
| 363 |
{tenderDetails?.attachments && tenderDetails.attachments.length > 0 && (
|
| 364 |
+
<div className="mt-8 space-y-4">
|
| 365 |
+
<div className="flex items-center justify-between">
|
| 366 |
+
<h4 className="text-[10px] font-bold uppercase text-slate-500 tracking-[0.2em]">Scraped Attachments ({tenderDetails.attachments.length})</h4>
|
| 367 |
+
{isLoadingDetails && <span className="text-[9px] text-purple-400 animate-pulse uppercase font-black">Refreshing...</span>}
|
| 368 |
</div>
|
| 369 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
| 370 |
{tenderDetails.attachments.slice(0, 6).map((att, idx) => (
|
| 371 |
<a
|
| 372 |
key={idx}
|
| 373 |
href={att.url}
|
| 374 |
+
target="_blank"
|
| 375 |
+
rel="noopener noreferrer"
|
| 376 |
+
className="flex items-center gap-3 p-3 rounded-xl bg-white/[0.03] border border-white/5 hover:bg-white/10 hover:border-purple-500/30 transition-all group"
|
| 377 |
>
|
| 378 |
+
<span className="text-lg group-hover:scale-110 transition-transform">
|
| 379 |
{att.name.toLowerCase().includes('bases') ? '⚖️' :
|
| 380 |
att.name.toLowerCase().includes('tecnico') ? '🛠️' :
|
| 381 |
att.name.toLowerCase().includes('anexo') ? '📝' : '📄'}
|
| 382 |
</span>
|
| 383 |
+
<div className="flex-1 min-w-0">
|
| 384 |
+
<p className="text-[11px] font-bold text-slate-300 truncate group-hover:text-white">{att.name}</p>
|
| 385 |
+
<p className="text-[9px] text-slate-500 uppercase tracking-tighter">Direct Download 📥</p>
|
| 386 |
</div>
|
| 387 |
</a>
|
| 388 |
))}
|
| 389 |
{tenderDetails.attachments.length > 6 && (
|
| 390 |
+
<div className="flex items-center justify-center p-3 rounded-xl border border-dashed border-white/10 text-[9px] text-slate-600 uppercase font-bold">
|
| 391 |
+ {tenderDetails.attachments.length - 6} more attachments
|
| 392 |
</div>
|
| 393 |
)}
|
|
|
|
| 396 |
)}
|
| 397 |
</div>
|
| 398 |
|
| 399 |
+
<div className="flex flex-col gap-4 lg:w-80">
|
| 400 |
+
<div className="glass-card rounded-2xl p-6 bg-white/5 border border-white/10">
|
| 401 |
+
<h4 className="text-[10px] font-bold uppercase text-slate-400 mb-4 tracking-widest">Document Corral</h4>
|
| 402 |
|
| 403 |
{/* The Corral (Animal Pen) */}
|
| 404 |
+
<div className="flex flex-wrap gap-3 mb-6">
|
| 405 |
{corral.map((item) => {
|
| 406 |
const icon = getFileIcon(item.file.name);
|
| 407 |
return (
|
|
|
|
| 414 |
<span className={`text-2xl transition-all duration-500 group-hover:rotate-12 ${activeAnimalId === item.id ? 'animate-bounce' : 'group-hover:animate-wiggle'}`}>
|
| 415 |
{icon.animal}
|
| 416 |
</span>
|
| 417 |
+
<span className="absolute -bottom-1 -right-1 text-[10px]">{icon.emoji}</span>
|
| 418 |
+
{item.analysis && <span className="absolute -top-1 -right-1 h-3 w-3 bg-green-500 rounded-full border-2 border-black" title="Analyzed" />}
|
| 419 |
</button>
|
| 420 |
);
|
| 421 |
})}
|
| 422 |
|
| 423 |
+
<label className="flex flex-col items-center justify-center h-16 w-16 rounded-2xl border border-dashed border-white/20 bg-white/5 cursor-pointer hover:bg-white/10 hover:border-purple-500/50 transition-all">
|
| 424 |
+
<span className="text-xl text-slate-500">+</span>
|
| 425 |
+
<input type="file" onChange={handleFileChange} className="hidden" />
|
| 426 |
</label>
|
| 427 |
</div>
|
| 428 |
|
| 429 |
+
<div className="text-[10px] text-slate-500 italic mb-4">
|
| 430 |
+
{corral.length === 0 ? "No documents in the corral." : `${corral.length} document(s) ready.`}
|
| 431 |
</div>
|
| 432 |
|
| 433 |
+
{isUploading && <p className="text-[10px] text-purple-400 animate-pulse font-bold">✨ Bringing animal to corral...</p>}
|
| 434 |
</div>
|
| 435 |
|
| 436 |
+
<label className="flex items-center gap-3 p-4 rounded-2xl bg-white/5 cursor-pointer hover:bg-white/10 transition border border-white/5">
|
| 437 |
+
<input type="checkbox" checked={approved} onChange={(e) => setApproved(e.target.checked)} className="h-5 w-5 rounded border-white/20 bg-black text-purple-500 outline-none accent-purple-500" />
|
| 438 |
+
<span className="text-xs font-semibold text-slate-300">Authorize Agent War Room</span>
|
| 439 |
</label>
|
| 440 |
|
| 441 |
<button
|
| 442 |
onClick={handleAnalyzeClick}
|
| 443 |
disabled={!tender || !approved || isRunning || !activeAnimalId}
|
| 444 |
+
className="w-full rounded-2xl premium-gradient py-5 font-bold text-white transition hover:opacity-90 disabled:opacity-30 disabled:cursor-not-allowed shadow-xl shadow-purple-500/20 active:scale-[0.98]"
|
| 445 |
>
|
| 446 |
+
{isRunning ? "Agents Debating..." : "Launch Analysis Pipeline"}
|
| 447 |
</button>
|
| 448 |
</div>
|
| 449 |
</div>
|
| 450 |
</div>
|
| 451 |
|
| 452 |
{/* Agents Row (Visual feedback & Configuration) */}
|
| 453 |
+
<div className="grid gap-6 md:grid-cols-3">
|
| 454 |
{agents.map((agent) => (
|
| 455 |
+
<div key={agent.id} className="relative group">
|
| 456 |
<div className={`glass-card rounded-3xl p-6 flex items-center gap-4 transition-all duration-700 ${isRunning ? 'ring-2 ring-purple-500/50 animate-pulse' : ''} ${analysis ? 'border-purple-500/30' : 'border-white/5'} hover:border-purple-500/20`}>
|
| 457 |
<div className={`text-4xl ${isRunning ? 'animate-bounce' : ''}`}>{agent.avatar}</div>
|
| 458 |
+
<div className="flex-1">
|
| 459 |
<div className={`text-[10px] font-bold uppercase tracking-widest ${agent.color}`}>{agent.role}</div>
|
| 460 |
+
<div className="text-sm font-bold text-white">{agent.name}</div>
|
| 461 |
+
<div className="text-[9px] text-slate-500 font-mono mt-1 flex items-center gap-1">
|
| 462 |
+
<span className="w-1 h-1 rounded-full bg-slate-500" />
|
| 463 |
{agentModels[agent.id as keyof typeof agentModels]}
|
| 464 |
</div>
|
| 465 |
</div>
|
| 466 |
<button
|
| 467 |
onClick={() => setActiveSettings(activeSettings === agent.id ? null : agent.id)}
|
| 468 |
+
className="p-2 rounded-xl bg-white/5 text-slate-500 hover:bg-white/10 hover:text-white transition-all active:scale-90"
|
| 469 |
>
|
| 470 |
⚙️
|
| 471 |
</button>
|
|
|
|
| 473 |
|
| 474 |
{/* Model Selector Popover */}
|
| 475 |
{activeSettings === agent.id && (
|
| 476 |
+
<div className="absolute top-full left-0 right-0 mt-2 z-50 glass-card rounded-2xl p-4 border border-purple-500/30 shadow-2xl animate-in fade-in zoom-in-95 duration-200">
|
| 477 |
+
<p className="text-[9px] font-black uppercase text-purple-400 mb-3 tracking-widest px-1">Select Engine</p>
|
| 478 |
+
<div className="space-y-1">
|
| 479 |
{[
|
| 480 |
+
"Gemini 2.5 Flash",
|
| 481 |
+
"DeepSeek-V3 (Featherless)",
|
| 482 |
+
"Qwen-2.5 (Featherless)",
|
| 483 |
+
"Llama-3.3-70B (Groq)",
|
| 484 |
+
"Llama-3.1-8B (Groq)",
|
| 485 |
+
"Mixtral-8x7B (Groq)",
|
| 486 |
+
"Gemma-4-31B (Featherless)",
|
| 487 |
+
"Llama-3.1-8B (Featherless)"
|
| 488 |
].map(model => (
|
| 489 |
<button
|
| 490 |
key={model}
|
|
|
|
| 495 |
className={`w-full text-left px-4 py-3 rounded-xl text-sm font-medium transition-all flex items-center justify-between border ${agentModels[agent.id as keyof typeof agentModels] === model ? 'bg-purple-500/20 text-white border-purple-500/50 shadow-lg shadow-purple-500/10' : 'text-slate-400 border-transparent hover:bg-white/10 hover:text-white hover:border-white/10'}`}
|
| 496 |
>
|
| 497 |
<span>{model}</span>
|
| 498 |
+
{agentModels[agent.id as keyof typeof agentModels] === model && <span className="text-purple-400">●</span>}
|
| 499 |
</button>
|
| 500 |
))}
|
| 501 |
</div>
|
|
|
|
| 507 |
|
| 508 |
{/* Running State Log */}
|
| 509 |
{isRunning && (
|
| 510 |
+
<div className="glass-card rounded-3xl p-8 border border-purple-500/30 bg-purple-500/5 animate-in fade-in zoom-in-95 duration-500">
|
| 511 |
+
<div className="flex items-center gap-4 mb-6">
|
| 512 |
+
<div className="h-4 w-4 rounded-full bg-purple-500 animate-ping" />
|
| 513 |
+
<h3 className="text-xl font-bold text-white">Pipeline in Progress</h3>
|
| 514 |
</div>
|
| 515 |
+
<div className="space-y-3">
|
| 516 |
{statusLog.map((log, i) => (
|
| 517 |
+
<div key={i} className="flex items-center gap-3 animate-in slide-in-from-left-4 duration-300">
|
| 518 |
+
<span className="text-purple-400 font-mono text-xs">[{new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })}]</span>
|
| 519 |
+
<p className="text-sm text-slate-300">{log}</p>
|
| 520 |
</div>
|
| 521 |
))}
|
| 522 |
</div>
|
|
|
|
| 525 |
|
| 526 |
{/* Error State */}
|
| 527 |
{error && (
|
| 528 |
+
<div className="glass-card rounded-3xl p-8 border border-red-500/30 bg-red-500/5 animate-in fade-in zoom-in-95 duration-500">
|
| 529 |
+
<div className="flex items-center gap-4 mb-4">
|
| 530 |
+
<span className="text-3xl">⚠️</span>
|
| 531 |
+
<h3 className="text-xl font-bold text-white">Analysis Failed</h3>
|
| 532 |
</div>
|
| 533 |
+
<p className="text-slate-400 mb-6">{error}</p>
|
| 534 |
<button
|
| 535 |
onClick={handleAnalyzeClick}
|
| 536 |
+
className="px-6 py-3 rounded-2xl bg-red-500/20 text-red-400 font-bold border border-red-500/30 hover:bg-red-500/30 transition-all active:scale-95"
|
| 537 |
>
|
| 538 |
Retry Analysis
|
| 539 |
</button>
|
|
|
|
| 542 |
|
| 543 |
{/* Analysis Results View */}
|
| 544 |
{activeAnalysis && (
|
| 545 |
+
<div id="analysis-results" className="grid gap-8 lg:grid-cols-12 animate-in fade-in slide-in-from-bottom-8 duration-500 scroll-mt-20">
|
| 546 |
+
<div className="lg:col-span-8 space-y-8">
|
| 547 |
+
<div className="glass-card rounded-3xl p-10 bg-white/[0.02]">
|
| 548 |
+
<div className="flex items-start justify-between mb-8">
|
| 549 |
<div>
|
| 550 |
+
<div className="text-[11px] font-bold uppercase tracking-[0.3em] text-purple-400 mb-2">Agent Consensus</div>
|
| 551 |
+
<h3 className="text-6xl font-black text-white">{activeAnalysis.fit_score}% <span className="text-2xl font-light text-slate-500">Fit Score</span></h3>
|
| 552 |
+
<div className="mt-2 flex items-center gap-2">
|
| 553 |
+
<span className="text-[10px] text-slate-500 font-mono">Analyzing:</span>
|
| 554 |
+
<span className="text-[10px] text-purple-300 font-bold">{corral.find(a => a.id === activeAnimalId)?.file.name || tender?.name}</span>
|
| 555 |
</div>
|
| 556 |
</div>
|
| 557 |
+
<div className="flex flex-col items-end gap-3">
|
| 558 |
<div className={`rounded-2xl px-6 py-3 text-[10px] font-black uppercase tracking-widest shadow-lg ${activeAnalysis.decision === 'Recommended' ? 'bg-green-500/20 text-green-400 border border-green-500/30 shadow-green-500/10' : 'bg-amber-500/20 text-amber-400 border border-amber-500/30 shadow-amber-500/10'}`}>
|
| 559 |
{activeAnalysis.decision}
|
| 560 |
</div>
|
| 561 |
+
<div className="flex gap-2">
|
| 562 |
<button
|
| 563 |
onClick={() => window.print()}
|
| 564 |
+
className="px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-[10px] font-bold text-slate-400 hover:text-white hover:bg-white/10 transition uppercase tracking-[0.2em]"
|
| 565 |
>
|
| 566 |
Export PDF
|
| 567 |
</button>
|
|
|
|
| 573 |
{isGeneratingAnnexes ? 'Generating...' : '✨ Anexos Express'}
|
| 574 |
</button>
|
| 575 |
<button
|
| 576 |
+
onClick={() => alert("Report sent to executive committee via REW Secure Channel.")}
|
| 577 |
+
className="px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-xs text-slate-400 hover:text-white hover:bg-white/10 transition"
|
| 578 |
+
title="Share Analysis"
|
| 579 |
>
|
| 580 |
📧
|
| 581 |
</button>
|
| 582 |
</div>
|
| 583 |
</div>
|
| 584 |
</div>
|
| 585 |
+
<div className="prose prose-invert max-w-none">
|
| 586 |
+
<p className="text-slate-300 text-xl leading-relaxed italic border-l-4 border-purple-500 pl-8">{activeAnalysis.executive_summary}</p>
|
| 587 |
</div>
|
| 588 |
|
| 589 |
{/* Requirement Q&A Section */}
|
| 590 |
{activeAnalysis.requirement_responses && activeAnalysis.requirement_responses.length > 0 && (
|
| 591 |
+
<div className="mt-12 space-y-6">
|
| 592 |
+
<div className="flex items-center gap-3 border-b border-white/5 pb-4">
|
| 593 |
+
<span className="text-2xl">📋</span>
|
| 594 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400">Requirement Response (Q&A Style)</h4>
|
| 595 |
</div>
|
| 596 |
+
<div className="grid gap-4">
|
| 597 |
{activeAnalysis.requirement_responses.map((item, i) => (
|
| 598 |
+
<div key={i} className="rounded-2xl bg-white/[0.03] border border-white/5 p-6 hover:border-purple-500/30 transition-all group">
|
| 599 |
+
<div className="flex gap-4">
|
| 600 |
+
<span className="text-purple-500 font-bold font-mono">Q.</span>
|
| 601 |
+
<p className="text-white font-semibold text-sm">{item.question}</p>
|
| 602 |
</div>
|
| 603 |
+
<div className="mt-4 flex gap-4 pl-8 border-l border-white/10">
|
| 604 |
+
<span className="text-green-400 font-bold font-mono">A.</span>
|
| 605 |
+
<p className="text-slate-400 text-sm leading-relaxed">{item.answer}</p>
|
| 606 |
</div>
|
| 607 |
</div>
|
| 608 |
))}
|
|
|
|
| 612 |
|
| 613 |
{/* Proposal Draft Section */}
|
| 614 |
{activeAnalysis.proposal_draft && (
|
| 615 |
+
<div className="mt-12 space-y-6">
|
| 616 |
+
<div className="flex items-center justify-between border-b border-white/5 pb-4">
|
| 617 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400">AI Generated Proposal Draft</h4>
|
| 618 |
<button
|
| 619 |
onClick={() => {
|
| 620 |
navigator.clipboard.writeText(activeAnalysis.proposal_draft);
|
| 621 |
+
alert("Proposal copied to clipboard!");
|
| 622 |
}}
|
| 623 |
+
className="text-[10px] font-bold uppercase text-slate-500 hover:text-white transition"
|
| 624 |
>
|
| 625 |
Copy to Clipboard 📋
|
| 626 |
</button>
|
| 627 |
</div>
|
| 628 |
+
<div className="p-8 rounded-3xl bg-white/[0.03] border border-white/10 font-serif text-slate-400 text-sm leading-relaxed whitespace-pre-wrap max-h-[500px] overflow-y-auto custom-scrollbar">
|
| 629 |
{activeAnalysis.proposal_draft}
|
| 630 |
</div>
|
| 631 |
</div>
|
|
|
|
| 633 |
</div>
|
| 634 |
|
| 635 |
|
| 636 |
+
<div className="grid gap-6 md:grid-cols-2">
|
| 637 |
+
<div className="glass-card rounded-3xl p-8 bg-white/[0.01]">
|
| 638 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-amber-400 mb-6 flex items-center gap-2">
|
| 639 |
<span>⚠️</span> Legal Compliance Gaps
|
| 640 |
</h4>
|
| 641 |
+
<ul className="space-y-4">
|
| 642 |
{activeAnalysis.compliance_gaps.map((gap, i) => (
|
| 643 |
+
<li key={i} className="flex gap-4 text-sm text-slate-400 leading-relaxed">
|
| 644 |
+
<span className="text-amber-500 font-bold">•</span> {gap}
|
| 645 |
</li>
|
| 646 |
))}
|
| 647 |
</ul>
|
| 648 |
</div>
|
| 649 |
+
<div className="glass-card rounded-3xl p-8 bg-white/[0.01]">
|
| 650 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-cyan mb-6 flex items-center gap-2">
|
| 651 |
<span>💎</span> Technical Requirements
|
| 652 |
</h4>
|
| 653 |
+
<ul className="space-y-4">
|
| 654 |
{activeAnalysis.key_requirements.map((req, i) => (
|
| 655 |
+
<li key={i} className="flex gap-4 text-sm text-slate-400 leading-relaxed">
|
| 656 |
+
<span className="text-cyan font-bold">▹</span> {req}
|
| 657 |
</li>
|
| 658 |
))}
|
| 659 |
</ul>
|
| 660 |
</div>
|
| 661 |
</div>
|
| 662 |
|
| 663 |
+
<div className="glass-card rounded-3xl p-10 bg-white/[0.01]">
|
| 664 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400 mb-8 text-center">Neural Risk Matrix</h4>
|
| 665 |
+
<div className="grid gap-6 md:grid-cols-2 mb-12">
|
| 666 |
{activeAnalysis.risks.map((risk, i) => (
|
| 667 |
+
<div key={i} className="group rounded-3xl bg-white/[0.02] p-6 border border-white/5 hover:border-purple-500/30 transition-all duration-300">
|
| 668 |
+
<div className="flex items-center justify-between mb-4">
|
| 669 |
+
<span className="font-bold text-white text-lg group-hover:text-purple-400 transition">{risk.title}</span>
|
| 670 |
<span className={`text-[9px] font-black px-3 py-1 rounded-full uppercase tracking-widest ${risk.severity === 'High' ? 'bg-red-500/20 text-red-500 border border-red-500/20' : 'bg-white/5 text-slate-500 border border-white/5'}`}>{risk.severity}</span>
|
| 671 |
</div>
|
| 672 |
+
<p className="text-xs text-slate-500 leading-relaxed">{risk.explanation}</p>
|
| 673 |
</div>
|
| 674 |
))}
|
| 675 |
</div>
|
| 676 |
|
| 677 |
{activeAnalysis.strategic_roadmap && (
|
| 678 |
+
<div className="mt-8 pt-8 border-t border-white/5">
|
| 679 |
+
<h4 className="text-[11px] font-bold uppercase tracking-widest text-cyan mb-6 text-center">Winning Strategic Roadmap</h4>
|
| 680 |
+
<div className="p-8 rounded-3xl bg-cyan/5 border border-cyan/20 text-sm text-slate-300 leading-relaxed italic">
|
| 681 |
+
<div className="prose prose-invert prose-sm max-w-none">
|
| 682 |
{activeAnalysis.strategic_roadmap.split('\n').map((line, i) => (
|
| 683 |
+
<p key={i} className="mb-2">{line}</p>
|
| 684 |
))}
|
| 685 |
</div>
|
| 686 |
</div>
|
|
|
|
| 690 |
|
| 691 |
</div>
|
| 692 |
|
| 693 |
+
<div className="lg:col-span-4">
|
| 694 |
+
<div className="glass-card rounded-3xl p-8 bg-black/40 h-full sticky top-32">
|
| 695 |
+
<div className="flex items-center gap-3 mb-8 border-b border-white/5 pb-6">
|
| 696 |
+
<div className="h-2 w-2 rounded-full bg-purple-500 animate-pulse shadow-[0_0_12px_rgba(168,85,247,0.8)]" />
|
| 697 |
+
<h4 className="text-[10px] font-bold uppercase tracking-widest text-slate-400">Agent Intelligence Log</h4>
|
| 698 |
</div>
|
| 699 |
+
<div className="space-y-8 overflow-y-auto max-h-[700px] pr-2 custom-scrollbar">
|
| 700 |
{activeAnalysis.audit_log?.map((log, i) => (
|
| 701 |
+
<div key={i} className="flex gap-5 group">
|
| 702 |
+
<div className="flex flex-col items-center">
|
| 703 |
+
<div className="h-8 w-8 rounded-xl bg-white/5 flex items-center justify-center text-sm border border-white/10 group-hover:border-purple-500/50 transition-all duration-300 shadow-lg">🤖</div>
|
| 704 |
+
{i < (activeAnalysis.audit_log?.length ?? 0) - 1 && <div className="w-px flex-1 bg-gradient-to-b from-purple-500/40 to-transparent my-3" />}
|
| 705 |
</div>
|
| 706 |
+
<div className="pb-6">
|
| 707 |
+
<p className="text-[13px] text-slate-400 leading-relaxed group-hover:text-white transition-colors duration-300">{log}</p>
|
| 708 |
</div>
|
| 709 |
</div>
|
| 710 |
))}
|
|
|
|
| 713 |
|
| 714 |
{/* Anexos Express Section */}
|
| 715 |
{generatedAnnexes.length > 0 && (
|
| 716 |
+
<div id="annexes-section" className="mt-8 glass-card rounded-3xl p-10 bg-purple-500/[0.03] border border-purple-500/20 animate-in fade-in slide-in-from-bottom-8 duration-700">
|
| 717 |
+
<div className="flex items-center gap-4 mb-8">
|
| 718 |
+
<div className="w-12 h-12 rounded-2xl bg-purple-500/20 flex items-center justify-center text-2xl shadow-lg shadow-purple-500/20">📄</div>
|
| 719 |
<div>
|
| 720 |
+
<h4 className="text-2xl font-black text-white tracking-tight">Compliance: Anexos Express</h4>
|
| 721 |
+
<p className="text-slate-500 text-sm">Official annexes pre-filled with company data and tender requirements.</p>
|
| 722 |
</div>
|
| 723 |
</div>
|
| 724 |
|
| 725 |
+
<div className="grid gap-6 md:grid-cols-1 lg:grid-cols-3">
|
| 726 |
{generatedAnnexes.map((annex, i) => (
|
| 727 |
+
<div key={i} className="group rounded-3xl bg-white/[0.02] border border-white/5 p-6 hover:border-purple-500/40 transition-all">
|
| 728 |
+
<div className="text-[10px] font-bold uppercase text-purple-400 mb-3 tracking-widest">Template Generated</div>
|
| 729 |
+
<h5 className="text-white font-bold mb-4 line-clamp-1">{annex.name}</h5>
|
| 730 |
+
<div className="bg-black/40 rounded-xl p-4 text-[9px] font-mono text-slate-500 mb-4 h-32 overflow-hidden relative">
|
| 731 |
+
<pre className="whitespace-pre-wrap">{annex.content}</pre>
|
| 732 |
+
<div className="absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/60 to-transparent" />
|
| 733 |
</div>
|
| 734 |
<button
|
| 735 |
onClick={() => {
|
|
|
|
| 740 |
a.download = `${annex.name.replace(/ /g, '_')}.md`;
|
| 741 |
a.click();
|
| 742 |
}}
|
| 743 |
+
className="w-full py-2.5 rounded-xl bg-white/5 border border-white/10 text-[10px] font-bold text-slate-400 hover:text-white hover:bg-white/10 transition uppercase tracking-widest"
|
| 744 |
>
|
| 745 |
Download .md 📥
|
| 746 |
</button>
|
|
|
|
| 755 |
|
| 756 |
{/* Expert Consultation Chat */}
|
| 757 |
{tender && (
|
| 758 |
+
<div className="mt-12 animate-in fade-in slide-in-from-bottom-8 duration-700">
|
| 759 |
+
<div className="flex items-center gap-4 mb-6 px-2">
|
| 760 |
+
<div className="w-10 h-10 rounded-2xl bg-purple-500/10 flex items-center justify-center text-xl shadow-lg shadow-purple-500/10">💬</div>
|
| 761 |
<div>
|
| 762 |
+
<h3 className="text-2xl font-black text-white tracking-tight">Expert Agent Consultation</h3>
|
| 763 |
+
<p className="text-slate-500 text-sm">Deep-dive into specific questions with our specialized AI agents.</p>
|
| 764 |
</div>
|
| 765 |
</div>
|
| 766 |
<AgentChat tender={tender} companyProfile={companyProfile} />
|