脕lvaro Valenzuela Valdes commited on
Commit
850319d
1 Parent(s): a81ee34

feat: Multi-language support (EN/ES) and ESG Compliance Monitor

Browse files
frontend/app/page.tsx CHANGED
@@ -13,6 +13,7 @@ import GlobalSync from "../components/GlobalSync";
13
  import SystemInfo from "../components/SystemInfo";
14
  import { analyzeTender, fetchAnalysisHistory, fetchCompanyProfile, healthCheck, saveCompanyProfile, searchTenders } from "../lib/api";
15
  import type { AnalysisHistoryItem, AnalysisResult, CompanyProfile as CompanyProfileType, Tender } from "../lib/types";
 
16
 
17
  const tabs = [
18
  "Dashboard",
@@ -47,6 +48,10 @@ export default function HomePage() {
47
  const [analysisHistory, setAnalysisHistory] = useState<AnalysisHistoryItem[]>([]);
48
  const [status, setStatus] = useState("listening");
49
  const [searchKeyword, setSearchKeyword] = useState("");
 
 
 
 
50
 
51
  // Scroll to top when tab changes
52
  useEffect(() => {
@@ -169,6 +174,7 @@ export default function HomePage() {
169
  setIsMobileMenuOpen(false);
170
  }}
171
  status={status}
 
172
  />
173
  </div>
174
  </div>
@@ -186,17 +192,32 @@ export default function HomePage() {
186
  {activeTab === "My Portfolio" && "Manage your followed opportunities."}
187
  </p>
188
  </div>
189
- <div className="hidden md:flex items-center gap-4">
190
- <div className="flex -space-x-3">
191
- {[1, 2, 3].map(i => (
192
- <div key={i} className="w-8 h-8 rounded-full border-2 border-[#030303] bg-slate-800 flex items-center justify-center text-[10px] font-bold text-slate-400">
193
- A{i}
 
 
 
 
 
 
 
 
 
 
 
194
  </div>
195
- ))}
196
  </div>
197
- <div className="h-8 w-px bg-white/10" />
198
- <button className="bg-white/5 hover:bg-white/10 text-white px-4 py-2 rounded-xl text-xs font-bold border border-white/10 transition-all">
199
- Help Center
 
 
 
 
200
  </button>
201
  </div>
202
  </header>
@@ -211,6 +232,7 @@ export default function HomePage() {
211
  reportsGenerated={analysisResult ? 1 : 0}
212
  tenders={tenders}
213
  onFilterClick={handleFilterClick}
 
214
  />
215
  )}
216
  {(activeTab === "Tender Search" || activeTab === "My Portfolio") && (
@@ -220,6 +242,7 @@ export default function HomePage() {
220
  onAnalyze={handleTenderSelect}
221
  forceShowFollowed={activeTab === "My Portfolio"}
222
  initialKeyword={searchKeyword}
 
223
  />
224
  )}
225
  {activeTab === "Company Profile" && (
 
13
  import SystemInfo from "../components/SystemInfo";
14
  import { analyzeTender, fetchAnalysisHistory, fetchCompanyProfile, healthCheck, saveCompanyProfile, searchTenders } from "../lib/api";
15
  import type { AnalysisHistoryItem, AnalysisResult, CompanyProfile as CompanyProfileType, Tender } from "../lib/types";
16
+ import { translations, Language } from "../lib/translations";
17
 
18
  const tabs = [
19
  "Dashboard",
 
48
  const [analysisHistory, setAnalysisHistory] = useState<AnalysisHistoryItem[]>([]);
49
  const [status, setStatus] = useState("listening");
50
  const [searchKeyword, setSearchKeyword] = useState("");
51
+ const [lang, setLang] = useState<Language>("en");
52
+
53
+ const t = translations[lang];
54
+
55
 
56
  // Scroll to top when tab changes
57
  useEffect(() => {
 
174
  setIsMobileMenuOpen(false);
175
  }}
176
  status={status}
177
+ lang={lang}
178
  />
179
  </div>
180
  </div>
 
192
  {activeTab === "My Portfolio" && "Manage your followed opportunities."}
193
  </p>
194
  </div>
195
+ <div className="hidden md:flex items-center gap-6">
196
+ {/* ESG Monitor */}
197
+ <div className="flex flex-col items-end">
198
+ <span className="text-[9px] font-black uppercase tracking-widest text-slate-500 mb-1">{t.esgScore}</span>
199
+ <div className="flex gap-2">
200
+ <div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-green-500/10 border border-green-500/20">
201
+ <span className="text-[10px] font-bold text-green-400">E</span>
202
+ <span className="text-[10px] font-mono text-white">92</span>
203
+ </div>
204
+ <div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-blue-500/10 border border-blue-500/20">
205
+ <span className="text-[10px] font-bold text-blue-400">S</span>
206
+ <span className="text-[10px] font-mono text-white">85</span>
207
+ </div>
208
+ <div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-purple-500/10 border border-purple-500/20">
209
+ <span className="text-[10px] font-bold text-purple-400">G</span>
210
+ <span className="text-[10px] font-mono text-white">96</span>
211
  </div>
212
+ </div>
213
  </div>
214
+ <div className="h-10 w-px bg-white/10" />
215
+ <button
216
+ onClick={() => setLang(lang === "en" ? "es" : "en")}
217
+ className="bg-white/5 hover:bg-white/10 text-white px-4 py-2 rounded-xl text-xs font-bold border border-white/10 transition-all flex items-center gap-2"
218
+ >
219
+ <span>{lang === "en" ? "馃嚭馃嚫" : "馃嚜馃嚫"}</span>
220
+ <span>{lang === "en" ? "English" : "Espa帽ol"}</span>
221
  </button>
222
  </div>
223
  </header>
 
232
  reportsGenerated={analysisResult ? 1 : 0}
233
  tenders={tenders}
234
  onFilterClick={handleFilterClick}
235
+ lang={lang}
236
  />
237
  )}
238
  {(activeTab === "Tender Search" || activeTab === "My Portfolio") && (
 
242
  onAnalyze={handleTenderSelect}
243
  forceShowFollowed={activeTab === "My Portfolio"}
244
  initialKeyword={searchKeyword}
245
+ lang={lang}
246
  />
247
  )}
248
  {activeTab === "Company Profile" && (
frontend/components/Dashboard.tsx CHANGED
@@ -4,6 +4,8 @@ import { useEffect, useMemo, useState } from "react";
4
  import BrandLoader from "./BrandLoader";
5
  import { searchTenders, fetchDbStatus } from "../lib/api";
6
 
 
 
7
  type Props = {
8
  tendersFound: number;
9
  recommendedOpportunities: number;
@@ -11,6 +13,7 @@ type Props = {
11
  reportsGenerated: number;
12
  tenders: Tender[];
13
  onFilterClick?: (type: "sector" | "region", value: string) => void;
 
14
  };
15
 
16
  export default function Dashboard({
@@ -19,8 +22,10 @@ export default function Dashboard({
19
  highRiskItems,
20
  reportsGenerated,
21
  tenders,
22
- onFilterClick
 
23
  }: Props) {
 
24
  const [isSyncing, setIsSyncing] = useState(false);
25
  const [dbStatus, setDbStatus] = useState<any>(null);
26
 
@@ -35,11 +40,9 @@ export default function Dashboard({
35
  const handleGlobalSync = async () => {
36
  setIsSyncing(true);
37
  try {
38
- // We trigger a search for "software" or empty to populate initial DB
39
  await searchTenders({ keyword: "" });
40
- // Small delay for the "wow" effect duration
41
  await new Promise(r => setTimeout(r, 2500));
42
- window.location.reload(); // Refresh to show new counts
43
  } catch (e) {
44
  console.error(e);
45
  } finally {
@@ -110,33 +113,33 @@ export default function Dashboard({
110
  {isSyncing && <BrandLoader />}
111
  <div className="flex items-center justify-between gap-4">
112
  <div>
113
- <p className="text-sm uppercase tracking-[0.35em] text-cyan/80">Resumen ejecutivo</p>
114
  <h2 className="mt-3 text-4xl font-semibold text-white">AndesOps AI</h2>
115
  <p className="mt-4 max-w-2xl text-slate-300">
116
- Inteligencia de mercado y an谩lisis ag茅ntico para licitaciones p煤blicas en Chile.
117
  </p>
118
  </div>
119
  <button
120
  onClick={handleGlobalSync}
121
  className="group relative flex items-center gap-3 overflow-hidden rounded-2xl bg-cyan px-6 py-4 font-bold text-slate-950 transition hover:bg-sky hover:scale-[1.02] active:scale-[0.98]"
122
  >
123
- <span className="relative z-10">Sync Global Pipeline</span>
124
  <span className="text-xl group-hover:rotate-180 transition-transform duration-700">馃攧</span>
125
  <div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
126
  </button>
127
  </div>
128
 
129
  <div className="grid gap-5 md:grid-cols-2 xl:grid-cols-4">
130
- <StatCard title="Tenders Found" value={tendersFound} subtitle="Oportunidades activas" />
131
- <StatCard title="Recommended" value={recommendedOpportunities} subtitle="Fit score > 80%" />
132
- <StatCard title="High Risk" value={highRiskItems} subtitle="Riesgos cr铆ticos" />
133
- <StatCard title="Total Pipeline" value={formatAmount(totalAmount)} subtitle="Monto total proyectado" />
134
  </div>
135
 
136
  <div className="grid gap-6 lg:grid-cols-3">
137
  {/* Sector Distribution */}
138
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
139
- <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">Sectores (Rubros)</h3>
140
  <div className="space-y-4">
141
  {sectorDistribution.length > 0 ? (
142
  sectorDistribution.map(([sector, count]) => (
@@ -165,7 +168,7 @@ export default function Dashboard({
165
 
166
  {/* Region Distribution */}
167
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
168
- <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">Distribuci贸n Regional</h3>
169
  <div className="space-y-4">
170
  {regionDistribution.length > 0 ? (
171
  regionDistribution.map(([region, count]) => (
@@ -194,7 +197,7 @@ export default function Dashboard({
194
 
195
  {/* Deadline Status */}
196
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
197
- <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">Estado de Plazos</h3>
198
  <div className="space-y-6 pt-2">
199
  <div className="flex items-center gap-4">
200
  <div className="h-12 w-1.5 bg-red-500 rounded-full" />
@@ -223,7 +226,7 @@ export default function Dashboard({
223
  {/* Database Status Table (New) */}
224
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6 flex flex-col justify-between">
225
  <div>
226
- <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">Data Integrity Monitor</h3>
227
  <div className="overflow-hidden rounded-2xl border border-slate-800 bg-slate-900/30">
228
  <table className="w-full text-left text-[10px]">
229
  <thead className="bg-slate-800/50 text-slate-500 uppercase font-bold">
 
4
  import BrandLoader from "./BrandLoader";
5
  import { searchTenders, fetchDbStatus } from "../lib/api";
6
 
7
+ import { translations, Language } from "../lib/translations";
8
+
9
  type Props = {
10
  tendersFound: number;
11
  recommendedOpportunities: number;
 
13
  reportsGenerated: number;
14
  tenders: Tender[];
15
  onFilterClick?: (type: "sector" | "region", value: string) => void;
16
+ lang: Language;
17
  };
18
 
19
  export default function Dashboard({
 
22
  highRiskItems,
23
  reportsGenerated,
24
  tenders,
25
+ onFilterClick,
26
+ lang
27
  }: Props) {
28
+ const t = translations[lang];
29
  const [isSyncing, setIsSyncing] = useState(false);
30
  const [dbStatus, setDbStatus] = useState<any>(null);
31
 
 
40
  const handleGlobalSync = async () => {
41
  setIsSyncing(true);
42
  try {
 
43
  await searchTenders({ keyword: "" });
 
44
  await new Promise(r => setTimeout(r, 2500));
45
+ window.location.reload();
46
  } catch (e) {
47
  console.error(e);
48
  } finally {
 
113
  {isSyncing && <BrandLoader />}
114
  <div className="flex items-center justify-between gap-4">
115
  <div>
116
+ <p className="text-sm uppercase tracking-[0.35em] text-cyan/80">{t.resumenEjecutivo}</p>
117
  <h2 className="mt-3 text-4xl font-semibold text-white">AndesOps AI</h2>
118
  <p className="mt-4 max-w-2xl text-slate-300">
119
+ {t.andesOpsDesc}
120
  </p>
121
  </div>
122
  <button
123
  onClick={handleGlobalSync}
124
  className="group relative flex items-center gap-3 overflow-hidden rounded-2xl bg-cyan px-6 py-4 font-bold text-slate-950 transition hover:bg-sky hover:scale-[1.02] active:scale-[0.98]"
125
  >
126
+ <span className="relative z-10">{t.syncPipeline}</span>
127
  <span className="text-xl group-hover:rotate-180 transition-transform duration-700">馃攧</span>
128
  <div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
129
  </button>
130
  </div>
131
 
132
  <div className="grid gap-5 md:grid-cols-2 xl:grid-cols-4">
133
+ <StatCard title={t.tendersFound} value={tendersFound} subtitle={t.activeOpps} />
134
+ <StatCard title={t.recommended} value={recommendedOpportunities} subtitle="Fit score > 80%" />
135
+ <StatCard title={t.highRisk} value={highRiskItems} subtitle="Riesgos cr铆ticos" />
136
+ <StatCard title={t.totalPipeline} value={formatAmount(totalAmount)} subtitle="Monto total proyectado" />
137
  </div>
138
 
139
  <div className="grid gap-6 lg:grid-cols-3">
140
  {/* Sector Distribution */}
141
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
142
+ <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">{t.sectors}</h3>
143
  <div className="space-y-4">
144
  {sectorDistribution.length > 0 ? (
145
  sectorDistribution.map(([sector, count]) => (
 
168
 
169
  {/* Region Distribution */}
170
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
171
+ <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">{t.regionalDist}</h3>
172
  <div className="space-y-4">
173
  {regionDistribution.length > 0 ? (
174
  regionDistribution.map(([region, count]) => (
 
197
 
198
  {/* Deadline Status */}
199
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6">
200
+ <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">{t.deadlines}</h3>
201
  <div className="space-y-6 pt-2">
202
  <div className="flex items-center gap-4">
203
  <div className="h-12 w-1.5 bg-red-500 rounded-full" />
 
226
  {/* Database Status Table (New) */}
227
  <div className="rounded-3xl border border-slate-800 bg-slate-950/80 p-6 flex flex-col justify-between">
228
  <div>
229
+ <h3 className="text-sm uppercase tracking-widest text-slate-400 mb-6 font-semibold">{t.integrityMonitor}</h3>
230
  <div className="overflow-hidden rounded-2xl border border-slate-800 bg-slate-900/30">
231
  <table className="w-full text-left text-[10px]">
232
  <thead className="bg-slate-800/50 text-slate-500 uppercase font-bold">
frontend/components/Sidebar.tsx CHANGED
@@ -1,4 +1,5 @@
1
  "use client";
 
2
 
3
  import type { Dispatch, SetStateAction } from "react";
4
 
@@ -18,9 +19,26 @@ type Props = {
18
  activeTab: SidebarTab;
19
  onTabSelect: Dispatch<SetStateAction<SidebarTab>>;
20
  status: string;
 
21
  };
22
 
23
- export default function Sidebar({ tabs, activeTab, onTabSelect, status }: Props) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  return (
25
  <aside className="w-72 glass-card rounded-3xl h-[calc(100vh-3rem)] sticky top-6 p-6 flex flex-col gap-8">
26
  <div className="flex items-center gap-3 px-2">
@@ -51,7 +69,7 @@ export default function Sidebar({ tabs, activeTab, onTabSelect, status }: Props)
51
  <div className={`w-1.5 h-1.5 rounded-full transition-all duration-300 ${
52
  isActive ? "bg-purple-500 scale-125 shadow-[0_0_8px_rgba(168,85,247,0.8)]" : "bg-transparent group-hover:bg-slate-600"
53
  }`} />
54
- <span className="font-medium text-sm">{tab}</span>
55
  </button>
56
  );
57
  })}
 
1
  "use client";
2
+ import { translations, Language } from "../lib/translations";
3
 
4
  import type { Dispatch, SetStateAction } from "react";
5
 
 
19
  activeTab: SidebarTab;
20
  onTabSelect: Dispatch<SetStateAction<SidebarTab>>;
21
  status: string;
22
+ lang: Language;
23
  };
24
 
25
+ export default function Sidebar({ tabs, activeTab, onTabSelect, status, lang }: Props) {
26
+ const t = translations[lang];
27
+
28
+ const getTabLabel = (tab: SidebarTab) => {
29
+ switch(tab) {
30
+ case "Dashboard": return t.dashboard;
31
+ case "Tender Search": return t.tenderSearch;
32
+ case "My Portfolio": return t.myPortfolio;
33
+ case "Company Profile": return t.companyProfile;
34
+ case "Agent Analysis": return t.agentAnalysis;
35
+ case "Proposal Draft": return t.proposalDraft;
36
+ case "Reports": return t.reports;
37
+ case "History": return t.history;
38
+ case "About": return t.about;
39
+ default: return tab;
40
+ }
41
+ };
42
  return (
43
  <aside className="w-72 glass-card rounded-3xl h-[calc(100vh-3rem)] sticky top-6 p-6 flex flex-col gap-8">
44
  <div className="flex items-center gap-3 px-2">
 
69
  <div className={`w-1.5 h-1.5 rounded-full transition-all duration-300 ${
70
  isActive ? "bg-purple-500 scale-125 shadow-[0_0_8px_rgba(168,85,247,0.8)]" : "bg-transparent group-hover:bg-slate-600"
71
  }`} />
72
+ <span className="font-medium text-sm">{getTabLabel(tab)}</span>
73
  </button>
74
  );
75
  })}
frontend/components/TenderSearch.tsx CHANGED
@@ -10,9 +10,11 @@ type Props = {
10
  onAnalyze: (tender: Tender) => void;
11
  forceShowFollowed?: boolean;
12
  initialKeyword?: string;
 
13
  };
14
 
15
- export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFollowed = false, initialKeyword = "" }: Props) {
 
16
  const [keyword, setKeyword] = useState(initialKeyword);
17
  const [buyerCode, setBuyerCode] = useState("");
18
  const [date, setDate] = useState("");
@@ -195,10 +197,10 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
195
  <table className="w-full text-left text-sm table-fixed border-collapse">
196
  <thead className="bg-white/5 text-slate-500 uppercase text-[10px] tracking-widest font-bold border-b border-white/5">
197
  <tr>
198
- <th className="px-4 py-5 w-[100px]">ID / Select</th>
199
- <th className="px-4 py-5 w-[250px]">Opportunity</th>
200
- <th className="px-4 py-5 w-[180px]">Buyer</th>
201
- <th className="px-4 py-5 text-center w-[100px]">Status</th>
202
  </tr>
203
  </thead>
204
  <tbody className="divide-y divide-white/5">
@@ -365,7 +367,7 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
365
  }}
366
  className="premium-gradient text-white px-8 py-3 rounded-xl font-bold text-sm shadow-xl shadow-purple-500/20 active:scale-95 transition-all"
367
  >
368
- Run Agent Analysis
369
  </button>
370
  </div>
371
  </div>
 
10
  onAnalyze: (tender: Tender) => void;
11
  forceShowFollowed?: boolean;
12
  initialKeyword?: string;
13
+ lang: Language;
14
  };
15
 
16
+ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFollowed = false, initialKeyword = "", lang }: Props) {
17
+ const t = translations[lang];
18
  const [keyword, setKeyword] = useState(initialKeyword);
19
  const [buyerCode, setBuyerCode] = useState("");
20
  const [date, setDate] = useState("");
 
197
  <table className="w-full text-left text-sm table-fixed border-collapse">
198
  <thead className="bg-white/5 text-slate-500 uppercase text-[10px] tracking-widest font-bold border-b border-white/5">
199
  <tr>
200
+ <th className="px-4 py-5 w-[100px]">{t.idSelect}</th>
201
+ <th className="px-4 py-5 w-[250px]">{t.opportunity}</th>
202
+ <th className="px-4 py-5 w-[180px]">{t.buyer}</th>
203
+ <th className="px-4 py-5 text-center w-[100px]">{t.status}</th>
204
  </tr>
205
  </thead>
206
  <tbody className="divide-y divide-white/5">
 
367
  }}
368
  className="premium-gradient text-white px-8 py-3 rounded-xl font-bold text-sm shadow-xl shadow-purple-500/20 active:scale-95 transition-all"
369
  >
370
+ {t.analyze}
371
  </button>
372
  </div>
373
  </div>
frontend/lib/translations.ts ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const translations = {
2
+ en: {
3
+ dashboard: "Dashboard",
4
+ tenderSearch: "Tender Search",
5
+ myPortfolio: "My Portfolio",
6
+ companyProfile: "Company Profile",
7
+ agentAnalysis: "Agent Analysis",
8
+ proposalDraft: "Proposal Draft",
9
+ reports: "Reports",
10
+ history: "History",
11
+ about: "About",
12
+ resumenEjecutivo: "Executive Summary",
13
+ andesOpsDesc: "Market intelligence and agentic analysis for public tenders.",
14
+ syncPipeline: "Sync Global Pipeline",
15
+ tendersFound: "Tenders Found",
16
+ activeOpps: "Active opportunities",
17
+ recommended: "Recommended",
18
+ highRisk: "High Risk",
19
+ totalPipeline: "Total Pipeline",
20
+ sectors: "Market Sectors",
21
+ regionalDist: "Regional Distribution",
22
+ deadlines: "Deadline Status",
23
+ integrityMonitor: "Data Integrity Monitor",
24
+ recentActivity: "Recent Pipeline Activity",
25
+ idSelect: "ID / Select",
26
+ opportunity: "Opportunity",
27
+ buyer: "Buyer",
28
+ status: "Status",
29
+ analyze: "Analyze",
30
+ esgScore: "ESG Compliance Rating",
31
+ environmental: "Environmental",
32
+ social: "Social",
33
+ governance: "Governance",
34
+ language: "Language",
35
+ ingesting: "INGESTING DOCUMENTS...",
36
+ analyzeSelected: "ANALYZE SELECTED",
37
+ },
38
+ es: {
39
+ dashboard: "Panel de Control",
40
+ tenderSearch: "Buscador de Licitaciones",
41
+ myPortfolio: "Mi Portafolio",
42
+ companyProfile: "Perfil de Empresa",
43
+ agentAnalysis: "An谩lisis Ag茅ntico",
44
+ proposalDraft: "Borrador de Propuesta",
45
+ reports: "Reportes",
46
+ history: "Historial",
47
+ about: "Sistema",
48
+ resumenEjecutivo: "Resumen Ejecutivo",
49
+ andesOpsDesc: "Inteligencia de mercado y an谩lisis de agentes para licitaciones p煤blicas.",
50
+ syncPipeline: "Sincronizar Pipeline Global",
51
+ tendersFound: "Licitaciones Halladas",
52
+ activeOpps: "Oportunidades activas",
53
+ recommended: "Recomendadas",
54
+ highRisk: "Riesgo Alto",
55
+ totalPipeline: "Pipeline Total",
56
+ sectors: "Sectores de Mercado",
57
+ regionalDist: "Distribuci贸n Regional",
58
+ deadlines: "Estado de Plazos",
59
+ integrityMonitor: "Monitor de Integridad de Datos",
60
+ recentActivity: "Actividad Reciente",
61
+ idSelect: "ID / Selecci贸n",
62
+ opportunity: "Oportunidad",
63
+ buyer: "Comprador",
64
+ status: "Estado",
65
+ analyze: "Analizar",
66
+ esgScore: "Calificaci贸n de Cumplimiento ESG",
67
+ environmental: "Ambiental",
68
+ social: "Social",
69
+ governance: "Gobernanza",
70
+ language: "Idioma",
71
+ ingesting: "INGIRIENDO DOCUMENTOS...",
72
+ analyzeSelected: "ANALIZAR SELECCIONADOS",
73
+ }
74
+ };
75
+
76
+ export type Language = "en" | "es";