Álvaro Valenzuela Valdes commited on
Commit
bd7895c
·
1 Parent(s): d7c9d51

Optimize mobile UI, fix layout overlaps and add tender questions link

Browse files
frontend/components/AgentAnalysis.tsx CHANGED
@@ -256,11 +256,11 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
256
  ) : null}
257
  <span className="text-xs text-slate-500 font-mono">{tender?.code}</span>
258
  </div>
259
- <h2 className="text-4xl font-bold text-white tracking-tight leading-tight mb-4">{tender?.name}</h2>
260
- <p className="text-slate-400 text-lg leading-relaxed">{tender?.buyer}</p>
261
 
262
  {tender && (
263
- <div className="mt-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
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">Investment</p>
266
  <p className="text-lg font-black text-white">
@@ -345,24 +345,35 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
345
 
346
  {/* Scraped Intelligence / Tabs */}
347
  {tenderDetails && (
348
- <div className="mt-8 flex flex-wrap gap-4">
349
  {tenderDetails.tabs?.history?.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-purple-400 text-xs">📜</span> History Available
352
  </div>
353
  )}
354
- {tenderDetails.tabs?.questions?.found && (
355
- <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">
356
- <span className="text-cyan text-xs">❓</span> Q&A Active
357
- </div>
 
 
 
 
 
 
 
 
 
 
 
358
  )}
359
  {tenderDetails.tabs?.opening?.found && (
360
- <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">
361
  <span className="text-green-400 text-xs">🔓</span> Opening Log Found
362
  </div>
363
  )}
364
  {tenderDetails.metadata?.has_adjudication && (
365
- <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">
366
  <span className="text-xs">🏆</span> Adjudicated
367
  </div>
368
  )}
@@ -553,68 +564,50 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
553
  <p className="text-slate-400 mb-6">{error}</p>
554
  <button
555
  onClick={handleAnalyzeClick}
556
- 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"
557
- >
558
- Retry Analysis
559
- </button>
560
- </div>
561
- )}
562
-
563
- {/* Analysis Results View */}
564
  {activeAnalysis && (
565
- <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">
566
  <div className="lg:col-span-8 space-y-8">
567
- <div className="glass-card rounded-3xl p-10 bg-white/[0.02]">
568
- + {/* Professional Print Header */}
569
- + <div className="hidden print-only mb-12 border-b-4 border-slate-900 pb-8 text-center">
570
- + <h1 className="text-4xl font-black text-slate-900 mb-2">ANDESOPS AI</h1>
571
- + <p className="text-sm font-bold uppercase tracking-[0.5em] text-slate-500">Intelligent Bidding Analysis Report</p>
572
- + <div className="mt-6 flex justify-between text-[10px] font-mono text-slate-400">
573
- + <span>DATE: {new Date().toLocaleDateString()}</span>
574
- + <span>REF ID: {tender?.code}</span>
575
- + <span>CONFIDENTIAL - FOR INTERNAL USE ONLY</span>
576
- + </div>
577
- + </div>
578
- +
579
- <div className="flex items-start justify-between mb-8">
580
  <div>
581
  <div className="text-[11px] font-bold uppercase tracking-[0.3em] text-purple-400 mb-2">Agent Consensus</div>
582
- <h3 className="text-6xl font-black text-white">{activeAnalysis.fit_score}% <span className="text-2xl font-light text-slate-500">Fit Score</span></h3>
583
  <div className="mt-2 flex items-center gap-2">
584
  <span className="text-[10px] text-slate-500 font-mono">Analyzing:</span>
585
- <span className="text-[10px] text-purple-300 font-bold">{corral.find(a => a.id === activeAnimalId)?.file.name || tender?.name}</span>
586
  </div>
587
  </div>
588
- <div className="flex flex-col items-end gap-3">
589
- <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'}`}>
590
  {activeAnalysis.decision}
591
  </div>
592
- <div className="flex gap-2">
593
  <button
594
  onClick={() => window.print()}
595
- 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]"
596
  >
597
  Export PDF
598
  </button>
599
  <button
600
  onClick={generateAnnexes}
601
  disabled={isGeneratingAnnexes}
602
- className={`px-4 py-2 rounded-xl border text-[10px] font-bold transition uppercase tracking-[0.2em] ${isGeneratingAnnexes ? 'bg-purple-500/20 border-purple-500/50 text-purple-300 animate-pulse' : 'bg-purple-500/10 border-purple-500/20 text-purple-400 hover:bg-purple-500/20'}`}
603
  >
604
- {isGeneratingAnnexes ? 'Generating...' : '✨ Anexos Express'}
605
  </button>
606
- <button
607
- onClick={() => alert("Report sent to executive committee via REW Secure Channel.")}
608
- 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"
609
- title="Share Analysis"
610
- >
611
- 📧
612
- </button>
613
  </div>
614
  </div>
615
  </div>
616
  <div className="prose prose-invert max-w-none">
617
- <p className="text-slate-300 text-xl leading-relaxed italic border-l-4 border-purple-500 pl-8">{activeAnalysis.executive_summary}</p>
618
  </div>
619
 
620
  {/* Requirement Q&A Section */}
@@ -622,176 +615,131 @@ export default function AgentAnalysis({ tender, companyProfile, analysis, onAnal
622
  <div className="mt-12 space-y-6">
623
  <div className="flex items-center gap-3 border-b border-white/5 pb-4">
624
  <span className="text-2xl">📋</span>
625
- <h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400">Requirement Response (Q&A Style)</h4>
626
  </div>
627
  <div className="grid gap-4">
628
  {activeAnalysis.requirement_responses.map((item, i) => (
629
- <div key={i} className="rounded-2xl bg-white/[0.03] border border-white/5 p-6 hover:border-purple-500/30 transition-all group">
630
  <div className="flex gap-4">
631
  <span className="text-purple-500 font-bold font-mono">Q.</span>
632
- <p className="text-white font-semibold text-sm">{item.question}</p>
633
  </div>
634
- <div className="mt-4 flex gap-4 pl-8 border-l border-white/10">
635
  <span className="text-green-400 font-bold font-mono">A.</span>
636
- <p className="text-slate-400 text-sm leading-relaxed">{item.answer}</p>
637
  </div>
638
  </div>
639
  ))}
640
  </div>
641
  </div>
642
  )}
643
-
644
- {/* Proposal Draft Section */}
645
- {activeAnalysis.proposal_draft && (
646
- <div className="mt-12 space-y-6">
647
- <div className="flex items-center justify-between border-b border-white/5 pb-4">
648
- <h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400">AI Generated Proposal Draft</h4>
649
- <button
650
- onClick={() => {
651
- navigator.clipboard.writeText(activeAnalysis.proposal_draft);
652
- alert("Proposal copied to clipboard!");
653
- }}
654
- className="text-[10px] font-bold uppercase text-slate-500 hover:text-white transition"
655
- >
656
- Copy to Clipboard 📋
657
- </button>
658
- </div>
659
- <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">
660
- {activeAnalysis.proposal_draft}
661
- </div>
662
- </div>
663
- )}
664
  </div>
665
 
666
-
667
  <div className="grid gap-6 md:grid-cols-2">
668
- <div className="glass-card rounded-3xl p-8 bg-white/[0.01]">
669
  <h4 className="text-[11px] font-bold uppercase tracking-widest text-amber-400 mb-6 flex items-center gap-2">
670
- <span>⚠️</span> Legal Compliance Gaps
671
  </h4>
672
  <ul className="space-y-4">
673
  {activeAnalysis.compliance_gaps.map((gap, i) => (
674
- <li key={i} className="flex gap-4 text-sm text-slate-400 leading-relaxed">
675
  <span className="text-amber-500 font-bold">•</span> {gap}
676
  </li>
677
  ))}
678
  </ul>
679
  </div>
680
- <div className="glass-card rounded-3xl p-8 bg-white/[0.01]">
681
  <h4 className="text-[11px] font-bold uppercase tracking-widest text-cyan mb-6 flex items-center gap-2">
682
- <span>💎</span> Technical Requirements
683
  </h4>
684
  <ul className="space-y-4">
685
  {activeAnalysis.key_requirements.map((req, i) => (
686
- <li key={i} className="flex gap-4 text-sm text-slate-400 leading-relaxed">
687
  <span className="text-cyan font-bold">▹</span> {req}
688
  </li>
689
  ))}
690
  </ul>
691
  </div>
692
  </div>
693
-
694
- <div className="glass-card rounded-3xl p-10 bg-white/[0.01]">
695
- <h4 className="text-[11px] font-bold uppercase tracking-widest text-purple-400 mb-8 text-center">Neural Risk Matrix</h4>
696
- <div className="grid gap-6 md:grid-cols-2 mb-12">
697
- {activeAnalysis.risks.map((risk, i) => (
698
- <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">
699
- <div className="flex items-center justify-between mb-4">
700
- <span className="font-bold text-white text-lg group-hover:text-purple-400 transition">{risk.title}</span>
701
- <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>
702
- </div>
703
- <p className="text-xs text-slate-500 leading-relaxed">{risk.explanation}</p>
704
- </div>
705
- ))}
706
- </div>
707
-
708
- {activeAnalysis.strategic_roadmap && (
709
- <div className="mt-8 pt-8 border-t border-white/5">
710
- <h4 className="text-[11px] font-bold uppercase tracking-widest text-cyan mb-6 text-center">Winning Strategic Roadmap</h4>
711
- <div className="p-8 rounded-3xl bg-cyan/5 border border-cyan/20 text-sm text-slate-300 leading-relaxed italic">
712
- <div className="prose prose-invert prose-sm max-w-none">
713
- {activeAnalysis.strategic_roadmap.split('\n').map((line, i) => (
714
- <p key={i} className="mb-2">{line}</p>
715
- ))}
716
- </div>
717
- </div>
718
- </div>
719
- )}
720
- </div>
721
-
722
  </div>
723
 
724
- <div className="lg:col-span-4">
725
- <div className="glass-card rounded-3xl p-8 bg-black/40 h-full sticky top-32">
726
- <div className="flex items-center gap-3 mb-8 border-b border-white/5 pb-6">
727
- <div className="h-2 w-2 rounded-full bg-purple-500 animate-pulse shadow-[0_0_12px_rgba(168,85,247,0.8)]" />
728
- <h4 className="text-[10px] font-bold uppercase tracking-widest text-slate-400">Agent Intelligence Log</h4>
 
729
  </div>
730
- <div className="space-y-8 overflow-y-auto max-h-[700px] pr-2 custom-scrollbar">
731
  {activeAnalysis.audit_log?.map((log, i) => (
732
- <div key={i} className="flex gap-5 group">
733
  <div className="flex flex-col items-center">
734
- <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>
735
- {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" />}
736
- </div>
737
- <div className="pb-6">
738
- <p className="text-[13px] text-slate-400 leading-relaxed group-hover:text-white transition-colors duration-300">{log}</p>
739
  </div>
 
740
  </div>
741
  ))}
742
  </div>
743
  </div>
744
-
745
- {/* Anexos Express Section */}
746
- {generatedAnnexes.length > 0 && (
747
- <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">
748
- <div className="flex items-center gap-4 mb-8">
749
- <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>
750
- <div>
751
- <h4 className="text-2xl font-black text-white tracking-tight">Compliance: Anexos Express</h4>
752
- <p className="text-slate-500 text-sm">Official annexes pre-filled with company data and tender requirements.</p>
753
- </div>
754
- </div>
755
-
756
- <div className="grid gap-6 md:grid-cols-1 lg:grid-cols-3">
757
- {generatedAnnexes.map((annex, i) => (
758
- <div key={i} className="group rounded-3xl bg-white/[0.02] border border-white/5 p-6 hover:border-purple-500/40 transition-all">
759
- <div className="text-[10px] font-bold uppercase text-purple-400 mb-3 tracking-widest">Template Generated</div>
760
- <h5 className="text-white font-bold mb-4 line-clamp-1">{annex.name}</h5>
761
- <div className="bg-black/40 rounded-xl p-4 text-[9px] font-mono text-slate-500 mb-4 h-32 overflow-hidden relative">
762
- <pre className="whitespace-pre-wrap">{annex.content}</pre>
763
- <div className="absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/60 to-transparent" />
764
- </div>
765
- <button
766
- onClick={() => {
767
- const blob = new Blob([annex.content], { type: 'text/markdown' });
768
- const url = window.URL.createObjectURL(blob);
769
- const a = document.createElement('a');
770
- a.href = url;
771
- a.download = `${annex.name.replace(/ /g, '_')}.md`;
772
- a.click();
773
- }}
774
- 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"
775
- >
776
- Download .md 📥
777
- </button>
778
- </div>
779
- ))}
780
- </div>
781
- </div>
782
- )}
783
  </div>
784
  </div>
785
  )}
786
 
787
- {/* Expert Consultation Chat */}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  {tender && (
789
  <div className="mt-12 animate-in fade-in slide-in-from-bottom-8 duration-700">
790
- <div className="flex items-center gap-4 mb-6 px-2">
791
  <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>
792
  <div>
793
- <h3 className="text-2xl font-black text-white tracking-tight">Expert Agent Consultation</h3>
794
- <p className="text-slate-500 text-sm">Deep-dive into specific questions with our specialized AI agents.</p>
 
 
 
 
 
 
 
795
  </div>
796
  </div>
797
  <AgentChat tender={tender} companyProfile={companyProfile} />
 
256
  ) : null}
257
  <span className="text-xs text-slate-500 font-mono">{tender?.code}</span>
258
  </div>
259
+ <h2 className="text-3xl md:text-4xl font-bold text-white tracking-tight leading-tight mb-4">{tender?.name}</h2>
260
+ <p className="text-slate-400 text-base md:text-lg leading-relaxed">{tender?.buyer}</p>
261
 
262
  {tender && (
263
+ <div className="mt-8 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
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">Investment</p>
266
  <p className="text-lg font-black text-white">
 
345
 
346
  {/* Scraped Intelligence / Tabs */}
347
  {tenderDetails && (
348
+ <div className="mt-8 flex flex-wrap gap-3">
349
  {tenderDetails.tabs?.history?.found && (
350
+ <div className="flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/10 text-[9px] sm:text-[10px] font-bold text-slate-400">
351
  <span className="text-purple-400 text-xs">📜</span> History Available
352
  </div>
353
  )}
354
+ {tenderDetails.metadata?.question_count && tenderDetails.metadata.question_count > 0 ? (
355
+ <a
356
+ href={`https://www.mercadopublico.cl/Procurement/Modules/RFB/DetailsAcquisition.aspx?codigo=${tender?.code}&tab=4`}
357
+ target="_blank"
358
+ rel="noopener noreferrer"
359
+ className="flex items-center gap-2 px-3 py-2 rounded-xl bg-cyan/10 border border-cyan/30 text-[9px] sm:text-[10px] font-bold text-cyan hover:bg-cyan/20 transition-all animate-pulse"
360
+ >
361
+ <span className="text-xs">❓</span> View {tenderDetails.metadata.question_count} Questions in Portal 🔗
362
+ </a>
363
+ ) : (
364
+ tenderDetails.tabs?.questions?.found && (
365
+ <div className="flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/10 text-[9px] sm:text-[10px] font-bold text-slate-400">
366
+ <span className="text-cyan text-xs">❓</span> Q&A Active
367
+ </div>
368
+ )
369
  )}
370
  {tenderDetails.tabs?.opening?.found && (
371
+ <div className="flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/10 text-[9px] sm:text-[10px] font-bold text-slate-400">
372
  <span className="text-green-400 text-xs">🔓</span> Opening Log Found
373
  </div>
374
  )}
375
  {tenderDetails.metadata?.has_adjudication && (
376
+ <div className="flex items-center gap-2 px-3 py-2 rounded-xl bg-green-500/10 border border-green-500/20 text-[9px] sm:text-[10px] font-bold text-green-400">
377
  <span className="text-xs">🏆</span> Adjudicated
378
  </div>
379
  )}
 
564
  <p className="text-slate-400 mb-6">{error}</p>
565
  <button
566
  onClick={handleAnalyzeClick}
567
+ className="px-6 py-3 rounded-2xl bg-red-500/20 text-red-400 font-bold border b {/* Analysis Results & Intelligent Sections */}
 
 
 
 
 
 
 
568
  {activeAnalysis && (
569
+ <div id="analysis-results" className="grid gap-8 grid-cols-1 lg:grid-cols-12 animate-in fade-in slide-in-from-bottom-8 duration-500 scroll-mt-20">
570
  <div className="lg:col-span-8 space-y-8">
571
+ {/* Main Analysis Card */}
572
+ <div className="glass-card rounded-3xl p-6 sm:p-10 bg-white/[0.02]">
573
+ {/* Professional Print Header */}
574
+ <div className="hidden print-only mb-12 border-b-4 border-slate-900 pb-8 text-center">
575
+ <h1 className="text-4xl font-black text-slate-900 mb-2">ANDESOPS AI</h1>
576
+ <p className="text-sm font-bold uppercase tracking-[0.5em] text-slate-500">Intelligent Bidding Analysis Report</p>
577
+ </div>
578
+
579
+ <div className="flex flex-col sm:flex-row items-start justify-between gap-6 mb-8">
 
 
 
 
580
  <div>
581
  <div className="text-[11px] font-bold uppercase tracking-[0.3em] text-purple-400 mb-2">Agent Consensus</div>
582
+ <h3 className="text-4xl sm:text-6xl font-black text-white">{activeAnalysis.fit_score}% <span className="text-xl sm:text-2xl font-light text-slate-500">Fit Score</span></h3>
583
  <div className="mt-2 flex items-center gap-2">
584
  <span className="text-[10px] text-slate-500 font-mono">Analyzing:</span>
585
+ <span className="text-[10px] text-purple-300 font-bold truncate max-w-[200px]">{corral.find(a => a.id === activeAnimalId)?.file.name || tender?.name}</span>
586
  </div>
587
  </div>
588
+ <div className="flex flex-col items-end gap-3 w-full sm:w-auto">
589
+ <div className={`w-full sm:w-auto text-center 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'}`}>
590
  {activeAnalysis.decision}
591
  </div>
592
+ <div className="flex flex-wrap gap-2 w-full sm:w-auto justify-end">
593
  <button
594
  onClick={() => window.print()}
595
+ className="flex-1 sm:flex-none px-4 py-2 rounded-xl bg-white/5 border border-white/10 text-[9px] font-bold text-slate-400 hover:text-white hover:bg-white/10 transition uppercase tracking-widest"
596
  >
597
  Export PDF
598
  </button>
599
  <button
600
  onClick={generateAnnexes}
601
  disabled={isGeneratingAnnexes}
602
+ className={`flex-1 sm:flex-none px-4 py-2 rounded-xl border text-[9px] font-bold transition uppercase tracking-widest ${isGeneratingAnnexes ? 'bg-purple-500/20 border-purple-500/50 text-purple-300 animate-pulse' : 'bg-purple-500/10 border-purple-500/20 text-purple-400 hover:bg-purple-500/20'}`}
603
  >
604
+ {isGeneratingAnnexes ? 'Generating...' : '✨ Anexos'}
605
  </button>
 
 
 
 
 
 
 
606
  </div>
607
  </div>
608
  </div>
609
  <div className="prose prose-invert max-w-none">
610
+ <p className="text-slate-300 text-lg sm:text-xl leading-relaxed italic border-l-4 border-purple-500 pl-4 sm:pl-8">{activeAnalysis.executive_summary}</p>
611
  </div>
612
 
613
  {/* Requirement Q&A Section */}
 
615
  <div className="mt-12 space-y-6">
616
  <div className="flex items-center gap-3 border-b border-white/5 pb-4">
617
  <span className="text-2xl">📋</span>
618
+ <h4 className="text-[10px] sm:text-[11px] font-bold uppercase tracking-widest text-purple-400">Requirement Response</h4>
619
  </div>
620
  <div className="grid gap-4">
621
  {activeAnalysis.requirement_responses.map((item, i) => (
622
+ <div key={i} className="rounded-2xl bg-white/[0.03] border border-white/5 p-4 sm:p-6 hover:border-purple-500/30 transition-all group">
623
  <div className="flex gap-4">
624
  <span className="text-purple-500 font-bold font-mono">Q.</span>
625
+ <p className="text-white font-semibold text-xs sm:text-sm">{item.question}</p>
626
  </div>
627
+ <div className="mt-4 flex gap-4 pl-4 sm:pl-8 border-l border-white/10">
628
  <span className="text-green-400 font-bold font-mono">A.</span>
629
+ <p className="text-slate-400 text-xs sm:text-sm leading-relaxed">{item.answer}</p>
630
  </div>
631
  </div>
632
  ))}
633
  </div>
634
  </div>
635
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
  </div>
637
 
 
638
  <div className="grid gap-6 md:grid-cols-2">
639
+ <div className="glass-card rounded-3xl p-6 sm:p-8 bg-white/[0.01]">
640
  <h4 className="text-[11px] font-bold uppercase tracking-widest text-amber-400 mb-6 flex items-center gap-2">
641
+ <span>⚠️</span> Compliance Gaps
642
  </h4>
643
  <ul className="space-y-4">
644
  {activeAnalysis.compliance_gaps.map((gap, i) => (
645
+ <li key={i} className="flex gap-4 text-xs sm:text-sm text-slate-400 leading-relaxed">
646
  <span className="text-amber-500 font-bold">•</span> {gap}
647
  </li>
648
  ))}
649
  </ul>
650
  </div>
651
+ <div className="glass-card rounded-3xl p-6 sm:p-8 bg-white/[0.01]">
652
  <h4 className="text-[11px] font-bold uppercase tracking-widest text-cyan mb-6 flex items-center gap-2">
653
+ <span>💎</span> Tech Requirements
654
  </h4>
655
  <ul className="space-y-4">
656
  {activeAnalysis.key_requirements.map((req, i) => (
657
+ <li key={i} className="flex gap-4 text-xs sm:text-sm text-slate-400 leading-relaxed">
658
  <span className="text-cyan font-bold">▹</span> {req}
659
  </li>
660
  ))}
661
  </ul>
662
  </div>
663
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  </div>
665
 
666
+ {/* Audit Log / Agent Thoughts Sticky Column */}
667
+ <div className="lg:col-span-4 space-y-8">
668
+ <div className="glass-card rounded-3xl p-6 sm:p-8 bg-black/40 lg:sticky lg:top-32 max-h-[500px] lg:max-h-[700px] overflow-hidden flex flex-col">
669
+ <div className="flex items-center gap-3 mb-6 border-b border-white/5 pb-4">
670
+ <div className="h-2 w-2 rounded-full bg-purple-500 animate-pulse" />
671
+ <h4 className="text-[10px] font-bold uppercase tracking-widest text-slate-400">Agent Intel Log</h4>
672
  </div>
673
+ <div className="space-y-6 overflow-y-auto pr-2 custom-scrollbar">
674
  {activeAnalysis.audit_log?.map((log, i) => (
675
+ <div key={i} className="flex gap-4 group">
676
  <div className="flex flex-col items-center">
677
+ <div className="h-6 w-6 rounded-lg bg-white/5 flex items-center justify-center text-xs border border-white/10 group-hover:border-purple-500/50 transition-all">🤖</div>
678
+ {i < (activeAnalysis.audit_log?.length ?? 0) - 1 && <div className="w-px flex-1 bg-white/5 my-2" />}
 
 
 
679
  </div>
680
+ <p className="text-xs text-slate-500 leading-relaxed group-hover:text-slate-300 transition-colors">{log}</p>
681
  </div>
682
  ))}
683
  </div>
684
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  </div>
686
  </div>
687
  )}
688
 
689
+ {/* Compliance Anexos Section (Moved to prevent overlap with Chat) */}
690
+ {generatedAnnexes.length > 0 && (
691
+ <div id="annexes-section" className="glass-card rounded-3xl p-6 sm:p-10 bg-purple-500/[0.03] border border-purple-500/20 animate-in fade-in slide-in-from-bottom-8 duration-700">
692
+ <div className="flex flex-col sm:flex-row sm:items-center gap-4 mb-8">
693
+ <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>
694
+ <div>
695
+ <h4 className="text-xl sm:text-2xl font-black text-white tracking-tight">Compliance: Anexos Express</h4>
696
+ <p className="text-slate-500 text-xs sm:text-sm">Official annexes pre-filled with company data.</p>
697
+ </div>
698
+ </div>
699
+
700
+ <div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
701
+ {generatedAnnexes.map((annex, i) => (
702
+ <div key={i} className="group rounded-3xl bg-white/[0.02] border border-white/5 p-6 hover:border-purple-500/40 transition-all">
703
+ <div className="text-[10px] font-bold uppercase text-purple-400 mb-3 tracking-widest">Template</div>
704
+ <h5 className="text-white font-bold mb-4 line-clamp-1 text-sm">{annex.name}</h5>
705
+ <div className="bg-black/40 rounded-xl p-4 text-[9px] font-mono text-slate-500 mb-4 h-32 overflow-hidden relative">
706
+ <pre className="whitespace-pre-wrap">{annex.content}</pre>
707
+ <div className="absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/60 to-transparent" />
708
+ </div>
709
+ <button
710
+ onClick={() => {
711
+ const blob = new Blob([annex.content], { type: 'text/markdown' });
712
+ const url = window.URL.createObjectURL(blob);
713
+ const a = document.createElement('a');
714
+ a.href = url;
715
+ a.download = `${annex.name.replace(/ /g, '_')}.md`;
716
+ a.click();
717
+ }}
718
+ className="w-full py-2.5 rounded-xl bg-white/5 border border-white/10 text-[9px] font-bold text-slate-400 hover:text-white hover:bg-white/10 transition uppercase tracking-widest"
719
+ >
720
+ Download .md 📥
721
+ </button>
722
+ </div>
723
+ ))}
724
+ </div>
725
+ </div>
726
+ )}
727
+
728
+ {/* Expert Consultation Chat (Bottom Section) */}
729
  {tender && (
730
  <div className="mt-12 animate-in fade-in slide-in-from-bottom-8 duration-700">
731
+ <div className="flex flex-col sm:flex-row sm:items-center gap-4 mb-6 px-2">
732
  <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>
733
  <div>
734
+ <h3 className="text-xl sm:text-2xl font-black text-white tracking-tight">Expert Agent Consultation</h3>
735
+ <p className="text-slate-500 text-xs sm:text-sm">Deep-dive into specific questions with our AI agents.</p>
736
+ </div>
737
+ </div>
738
+ <AgentChat tender={tender} companyProfile={companyProfile} />
739
+ </div>
740
+ )}
741
+ </div>
742
+ ons with our specialized AI agents.</p>
743
  </div>
744
  </div>
745
  <AgentChat tender={tender} companyProfile={companyProfile} />
frontend/components/MarketMonitor.tsx CHANGED
@@ -118,37 +118,40 @@ export default function MarketMonitor() {
118
  <table className="w-full text-left text-xs border-collapse sticky-header">
119
  <thead className="sticky top-0 z-10">
120
  <tr className="bg-slate-900/95 backdrop-blur-md text-slate-500 uppercase font-black tracking-tighter border-b border-white/5">
121
- <th className="px-6 py-5">Order ID / Description</th>
122
- <th className="px-6 py-5">Buyer</th>
123
- <th className="px-6 py-5">Vendor</th>
124
- <th className="px-6 py-5 text-right">Total Amount</th>
125
  </tr>
126
  </thead>
127
  <tbody className="divide-y divide-white/5">
128
  {paginatedOcs.map((oc) => (
129
  <tr key={oc.code} className="hover:bg-white/[0.03] transition-colors group">
130
- <td className="px-6 py-5 max-w-md">
131
  <div className="flex items-center gap-2 mb-1">
132
- <span className="text-cyan font-bold font-mono text-[10px] bg-cyan/5 px-2 py-0.5 rounded border border-cyan/10">
133
  {oc.code}
134
  </span>
135
  </div>
136
- <div className="text-white font-bold line-clamp-1 group-hover:line-clamp-none transition-all cursor-help" title={oc.name}>
137
  {oc.name || "Orden de Compra"}
138
  </div>
 
 
 
139
  </td>
140
- <td className="px-6 py-5">
141
- <div className="text-slate-300 font-medium truncate max-w-[150px]">
142
  {oc.buyer !== "Unknown" ? oc.buyer : <span className="opacity-30">...</span>}
143
  </div>
144
  </td>
145
- <td className="px-6 py-5">
146
- <div className="text-sky-400 font-bold truncate max-w-[150px]">
147
  {oc.provider !== "Unknown" ? oc.provider : <span className="opacity-30">...</span>}
148
  </div>
149
  </td>
150
- <td className="px-6 py-5 text-right">
151
- <div className="text-white font-black text-sm">
152
  {formatCurrency(oc.total_amount, oc.currency)}
153
  </div>
154
  </td>
 
118
  <table className="w-full text-left text-xs border-collapse sticky-header">
119
  <thead className="sticky top-0 z-10">
120
  <tr className="bg-slate-900/95 backdrop-blur-md text-slate-500 uppercase font-black tracking-tighter border-b border-white/5">
121
+ <th className="px-4 sm:px-6 py-5">Order ID / Description</th>
122
+ <th className="px-6 py-5 hidden md:table-cell">Buyer</th>
123
+ <th className="px-6 py-5 hidden lg:table-cell">Vendor</th>
124
+ <th className="px-4 sm:px-6 py-5 text-right">Total</th>
125
  </tr>
126
  </thead>
127
  <tbody className="divide-y divide-white/5">
128
  {paginatedOcs.map((oc) => (
129
  <tr key={oc.code} className="hover:bg-white/[0.03] transition-colors group">
130
+ <td className="px-4 sm:px-6 py-5 max-w-md">
131
  <div className="flex items-center gap-2 mb-1">
132
+ <span className="text-cyan font-bold font-mono text-[9px] bg-cyan/5 px-2 py-0.5 rounded border border-cyan/10">
133
  {oc.code}
134
  </span>
135
  </div>
136
+ <div className="text-white font-bold line-clamp-1 group-hover:line-clamp-none transition-all cursor-help text-xs sm:text-sm" title={oc.name}>
137
  {oc.name || "Orden de Compra"}
138
  </div>
139
+ <div className="md:hidden text-[10px] text-slate-500 mt-1 truncate max-w-[200px]">
140
+ {oc.buyer}
141
+ </div>
142
  </td>
143
+ <td className="px-6 py-5 hidden md:table-cell">
144
+ <div className="text-slate-300 font-medium truncate max-w-[150px] text-[11px]">
145
  {oc.buyer !== "Unknown" ? oc.buyer : <span className="opacity-30">...</span>}
146
  </div>
147
  </td>
148
+ <td className="px-6 py-5 hidden lg:table-cell">
149
+ <div className="text-sky-400 font-bold truncate max-w-[150px] text-[11px]">
150
  {oc.provider !== "Unknown" ? oc.provider : <span className="opacity-30">...</span>}
151
  </div>
152
  </td>
153
+ <td className="px-4 sm:px-6 py-5 text-right">
154
+ <div className="text-white font-black text-xs sm:text-sm">
155
  {formatCurrency(oc.total_amount, oc.currency)}
156
  </div>
157
  </td>
frontend/components/Sidebar.tsx CHANGED
@@ -47,7 +47,7 @@ export default function Sidebar({ tabs, activeTab, onTabSelect, status, lang, fo
47
  <aside
48
  onMouseEnter={() => setIsHovered(true)}
49
  onMouseLeave={() => setIsHovered(false)}
50
- className={`glass-card rounded-3xl h-[calc(100vh-3rem)] sticky top-6 p-4 flex flex-col gap-8 transition-all duration-500 ease-in-out z-50 border-white/10 ${isExpanded ? 'w-72 shadow-2xl shadow-purple-500/10 bg-black/60' : 'w-[84px] shadow-none border-white/5 bg-white/[0.02]'}`}
51
  >
52
  <div className={`flex items-center gap-3 px-2 transition-all duration-300 ${isExpanded ? 'justify-start' : 'justify-center'}`}>
53
  <div className="w-10 h-10 premium-gradient rounded-xl flex-shrink-0 flex items-center justify-center shadow-lg shadow-purple-500/20">
 
47
  <aside
48
  onMouseEnter={() => setIsHovered(true)}
49
  onMouseLeave={() => setIsHovered(false)}
50
+ className={`glass-card rounded-2xl md:rounded-3xl h-full md:h-[calc(100vh-3rem)] md:sticky md:top-6 p-3 md:p-4 flex flex-col gap-4 md:gap-8 transition-all duration-500 ease-in-out z-50 border-white/10 ${isExpanded ? 'w-full md:w-72 shadow-2xl shadow-purple-500/10 bg-black/60' : 'w-[84px] shadow-none border-white/5 bg-white/[0.02]'}`}
51
  >
52
  <div className={`flex items-center gap-3 px-2 transition-all duration-300 ${isExpanded ? 'justify-start' : 'justify-center'}`}>
53
  <div className="w-10 h-10 premium-gradient rounded-xl flex-shrink-0 flex items-center justify-center shadow-lg shadow-purple-500/20">
frontend/components/TenderSearch.tsx CHANGED
@@ -126,7 +126,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
126
  </div>
127
 
128
  {!forceShowFollowed && (
129
- <form onSubmit={handleSearch} className="relative z-10 space-y-6">
130
  <div className="flex flex-col md:flex-row gap-4">
131
  <div className="relative flex-1">
132
  <input
@@ -159,43 +159,44 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
159
  <table className="w-full text-left text-sm table-fixed">
160
  <thead className="bg-white/5 text-slate-500 uppercase text-[10px] font-bold border-b border-white/5">
161
  <tr>
162
- <th className="px-6 py-5 w-[60px] text-center"></th>
163
- <th className="px-6 py-5 w-[120px]">ID</th>
164
- <th className="px-6 py-5">Opportunity</th>
165
- <th className="px-6 py-5 w-[200px]">Buyer</th>
166
- <th className="px-6 py-5 text-center w-[120px]">Status</th>
167
- <th className="px-6 py-5 w-[100px] text-center">Action</th>
168
  </tr>
169
  </thead>
170
  <tbody className="divide-y divide-white/5">
171
  {filteredTenders.map((item) => (
172
  <tr key={item.code} className="hover:bg-white/[0.04] transition-colors group">
173
- <td className="px-4 py-5 text-center">
174
  <button
175
  onPointerDown={(e) => {
176
  e.stopPropagation();
177
- console.log("[Portfolio] Toggling follow for:", item.code);
178
  toggleFollow(item);
179
  }}
180
- className={`text-xl transition-all hover:scale-125 active:scale-90 p-2 cursor-pointer relative z-50 ${followedCodes.includes(item.code) ? 'text-amber-400 drop-shadow-[0_0_10px_rgba(251,191,36,0.6)]' : 'text-white/30 hover:text-white/60'}`}
181
  >
182
  {followedCodes.includes(item.code) ? "★" : "☆"}
183
  </button>
184
  </td>
185
- <td className="px-6 py-5 font-mono text-purple-400 text-[10px]">{item.code}</td>
186
  <td className="px-6 py-5">
187
- <div className="font-bold text-white truncate text-xs max-w-[400px]" title={item.name}>{item.name}</div>
 
188
  </td>
189
- <td className="px-6 py-5 text-slate-400 text-[11px] truncate">{item.buyer}</td>
190
- <td className="px-6 py-5 text-center">
191
- <span className={`inline-block rounded-full px-3 py-1 text-[9px] font-black uppercase ${item.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400' : 'bg-slate-800 text-slate-500'}`}>{item.status}</span>
192
  </td>
193
- <td className="px-6 py-5 text-center">
194
  <button
195
  onClick={() => setSelectedTenderForModal(item)}
196
- className="px-4 py-2 rounded-xl bg-purple-500/10 border border-purple-500/20 text-[10px] font-black text-purple-400 hover:bg-purple-500 hover:text-white transition-all uppercase tracking-widest"
197
  >
198
- View
 
199
  </button>
200
  </td>
201
  </tr>
 
126
  </div>
127
 
128
  {!forceShowFollowed && (
129
+ <form onSubmit={handleSearch} className="relative z-10 space-y-6">
130
  <div className="flex flex-col md:flex-row gap-4">
131
  <div className="relative flex-1">
132
  <input
 
159
  <table className="w-full text-left text-sm table-fixed">
160
  <thead className="bg-white/5 text-slate-500 uppercase text-[10px] font-bold border-b border-white/5">
161
  <tr>
162
+ <th className="px-4 py-5 w-[50px] text-center"></th>
163
+ <th className="px-6 py-5 w-[110px] hidden sm:table-cell">ID</th>
164
+ <th className="px-6 py-5 min-w-[200px]">Opportunity</th>
165
+ <th className="px-6 py-5 w-[150px] hidden md:table-cell">Buyer</th>
166
+ <th className="px-6 py-5 text-center w-[100px] hidden sm:table-cell">Status</th>
167
+ <th className="px-6 py-5 w-[80px] text-center">Action</th>
168
  </tr>
169
  </thead>
170
  <tbody className="divide-y divide-white/5">
171
  {filteredTenders.map((item) => (
172
  <tr key={item.code} className="hover:bg-white/[0.04] transition-colors group">
173
+ <td className="px-2 py-5 text-center">
174
  <button
175
  onPointerDown={(e) => {
176
  e.stopPropagation();
 
177
  toggleFollow(item);
178
  }}
179
+ className={`text-lg transition-all hover:scale-125 active:scale-90 p-2 cursor-pointer relative z-50 ${followedCodes.includes(item.code) ? 'text-amber-400 drop-shadow-[0_0_10px_rgba(251,191,36,0.6)]' : 'text-white/30 hover:text-white/60'}`}
180
  >
181
  {followedCodes.includes(item.code) ? "★" : "☆"}
182
  </button>
183
  </td>
184
+ <td className="px-6 py-5 font-mono text-purple-400 text-[9px] hidden sm:table-cell">{item.code}</td>
185
  <td className="px-6 py-5">
186
+ <div className="font-bold text-white text-xs sm:text-sm line-clamp-2" title={item.name}>{item.name}</div>
187
+ <div className="text-[10px] text-slate-500 mt-1 md:hidden truncate max-w-[150px]">{item.buyer}</div>
188
  </td>
189
+ <td className="px-6 py-5 text-slate-400 text-[10px] truncate hidden md:table-cell">{item.buyer}</td>
190
+ <td className="px-6 py-5 text-center hidden sm:table-cell">
191
+ <span className={`inline-block rounded-full px-2 py-1 text-[8px] font-black uppercase ${item.status.toLowerCase().includes('publicada') ? 'bg-green-500/10 text-green-400' : 'bg-slate-800 text-slate-500'}`}>{item.status}</span>
192
  </td>
193
+ <td className="px-4 py-5 text-center">
194
  <button
195
  onClick={() => setSelectedTenderForModal(item)}
196
+ className="p-2 sm:px-4 sm:py-2 rounded-xl bg-purple-500/10 border border-purple-500/20 text-[9px] font-black text-purple-400 hover:bg-purple-500 hover:text-white transition-all uppercase tracking-widest"
197
  >
198
+ <span className="hidden sm:inline">View</span>
199
+ <span className="sm:hidden">→</span>
200
  </button>
201
  </td>
202
  </tr>
frontend/globals.css CHANGED
@@ -178,3 +178,18 @@ select {
178
  border-color: #e2e8f0 !important;
179
  }
180
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  border-color: #e2e8f0 !important;
179
  }
180
  }
181
+
182
+ @media (max-width: 640px) {
183
+ .glass-card {
184
+ padding: 1.25rem !important;
185
+ }
186
+
187
+ h2 {
188
+ font-size: 1.5rem !important;
189
+ line-height: 2rem !important;
190
+ }
191
+
192
+ .premium-gradient {
193
+ padding: 1rem !important;
194
+ }
195
+ }