fernando-bold commited on
Commit
79f0b74
·
verified ·
1 Parent(s): 882af21

'use client'

Browse files

import { useState, useEffect } from 'react'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { ArrowUpDown, TrendingUp, DollarSign, Calendar, Zap } from 'lucide-react'

interface KPIMetrics {
roi: number
paybackYears: number
cet: number
npv: number
irr: number
lcoe: number
}

interface FinancingDetails {
monthlyPayment: number
totalFinancingCost: number
breakEvenYears: number
}

interface BestFinancingOption {
institution: string
product: string
interestRate: number
maxTerm: number
ltvMax: number
}

interface LeaderboardEntry {
projectCategory: string
projectTier: string
systemSizeKw: number
annualEnergyGeneration: number
electricityTariff: number
bestFinancingOption: BestFinancingOption
kpis: KPIMetrics
financingDetails: FinancingDetails
}

interface KPITableProps {
data: LeaderboardEntry[]
isLoading?: boolean
onInteraction?: (action: string, data?: any) => void
}

const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(value)
}

const formatPercent = (value: number) => {
return `${value.toFixed(2)}%`
}

const formatNumber = (value: number, decimals = 2) => {
return value.toFixed(decimals)
}

const getCategoryColor = (category: string) => {
const colors = {
XPP: 'bg-blue-100 text-blue-800',
PP: 'bg-green-100 text-green-800',
P: 'bg-yellow-100 text-yellow-800',
M: 'bg-orange-100 text-orange-800',
G: 'bg-red-100 text-red-800',
GG: 'bg-purple-100 text-purple-800'
}
return colors[category as keyof typeof colors] || 'bg-gray-100 text-gray-800'
}

const getTierColor = (tier: string) => {
const colors = {
PADRAO: 'bg-gray-100 text-gray-800',
CONSCIENTE: 'bg-emerald-100 text-emerald-800',
MODERADO: 'bg-amber-100 text-amber-800',
ACELERADO: 'bg-rose-100 text-rose-800'
}
return colors[tier as keyof typeof colors] || 'bg-gray-100 text-gray-800'
}

export function KPITable({
data,
isLoading,
onInteraction,
}: Readonly<KPITableProps>) {
const [sortBy, setSortBy] = useState<string>("roi")
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc")
const [filteredData, setFilteredData] = useState<LeaderboardEntry[]>(data)

useEffect(() => {
const sorted = [...data].sort((a, b) => {
const aValue = a.kpis[sortBy as keyof KPIMetrics] as number
const bValue = b.kpis[sortBy as keyof KPIMetrics] as number
return sortOrder === "desc" ? bValue - aValue : aValue - bValue
})
setFilteredData(sorted)
}, [data, sortBy, sortOrder])

const handleSort = (column: string) => {
if (sortBy === column) {
setSortOrder(sortOrder === "asc" ? "desc" : "asc")
} else {
setSortBy(column)
setSortOrder("desc")
}
onInteraction?.("sort_change", {
column,
sortOrder: sortOrder === "asc" ? "desc" : "asc",
})
}

if (isLoading) {
return (
<Card className="border-gradient">
<CardHeader>
<CardTitle>Carregando Leaderboard KPI...</CardTitle>
</CardHeader>
<CardContent>
<div className="animate-pulse space-y-4">
{[1, 2, 3, 4, 5].map((num) => (
<div
key={`loading-${num}`}
className="h-16 bg-gray-200 rounded"
></div>
))}
</div>
</CardContent>
</Card>
)
}

return (
<Card className="border-gradient">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<TrendingUp className="h-5 w-5" />
Leaderboard KPI - Projetos Solares vs Financiamento
</CardTitle>
<CardDescription>
Comparação de indicadores financeiros por categoria e tier de projeto
</CardDescription>
<div className="flex gap-4">
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-48">
<SelectValue placeholder="Ordenar por" />
</SelectTrigger>
<SelectContent>
<SelectItem value="roi">ROI (%)</SelectItem>
<SelectItem value="paybackYears">Payback (anos)</SelectItem>
<SelectItem value="cet">CET (%)</SelectItem>
<SelectItem value="npv">NPV (R$)</SelectItem>
<SelectItem value="irr">IRR (%)</SelectItem>
<SelectItem value="lcoe">LCOE (R$/kWh)</SelectItem>
</SelectContent>
</Select>
<Button
variant="outline"
onClick={() => setSortOrder(sortOrder === "asc" ? "desc" : "asc")}
>
<ArrowUpDown className="h-4 w-4 mr-2" />
{sortOrder === "desc" ? "Decrescente" : "Crescente"}
</Button>
</div>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>Projeto</TableHead>
<TableHead>Tamanho</TableHead>
<TableHead>Geração Anual</TableHead>
<TableHead>Tarifa</TableHead>
<TableHead>Instituição</TableHead>
<TableHead>Produto</TableHead>
<TableHead>Taxa (%)</TableHead>
<TableHead>Prazo (meses)</TableHead>
<TableHead>Parcela Mensal</TableHead>
<TableHead
className="cursor-pointer"
onClick={() => handleSort("roi")}
>
ROI{" "}
{sortBy === "roi" && (
<ArrowUpDown className="inline h-4 w-4 ml-1" />
)}
</TableHead>
<TableHead
className="cursor-pointer"
onClick={() => handleSort("paybackYears")}
>
Payback{" "}
{sortBy === "paybackYears" && (
<ArrowUpDown className="inline h-4 w-4 ml-1" />
)}
</TableHead>
<TableHead
className="cursor-pointer"
onClick={() => handleSort("cet")}
>
CET{" "}
{sortBy === "cet" && (
<ArrowUpDown className="inline h-4 w-4 ml-1" />
)}
</TableHead>
<TableHead
className="cursor-pointer"
onClick={() => handleSort("npv")}
>
NPV{" "}
{sortBy === "npv" && (
<ArrowUpDown className="inline h-4 w-4 ml-1" />
)}
</TableHead>
<TableHead
className="cursor-pointer"
onClick={() => handleSort("lcoe")}
>
LCOE{" "}
{sortBy === "lcoe" && (
<ArrowUpDown className="inline h-4 w-4 ml-1" />
)}
</TableHead>
<TableHead>Break-even</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredData.map((entry, index) => (
<TableRow
key={`${entry.projectCategory}-${entry.projectTier}-${index}`}
className="hover:bg-[var(--glass-light)] hover:backdrop-blur-sm transition-all duration-200 cursor-pointer"
onClick={() => onInteraction?.("row_click", { entry, index })}
>
<TableCell>
<div className="flex gap-2">
<Badge
className={getCategoryColor(entry.projectCategory)}
>
{entry.projectCategory}
</Badge>
<Badge className={getTierColor(entry.projectTier)}>
{entry.projectTier}
</Badge>
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
<Zap className="h-4 w-4 text-yellow-500" />
{formatNumber(entry.systemSizeKw)} kWp
</div>
</TableCell>
<TableCell>
{formatNumber(entry.annualEnergyGeneration)} kWh/ano
</TableCell>
<TableCell>
{formatCurrency(entry.electricityTariff)}
</TableCell>
<TableCell className="font-medium">
{entry.bestFinancingOption.institution}
</TableCell>
<TableCell>{entry.bestFinancingOption.product}</TableCell>
<TableCell>
{formatPercent(entry.bestFinancingOption.interestRate)}
</TableCell>
<TableCell>
{entry.bestFinancingOption.maxTerm} meses
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
<DollarSign className="h-4 w-4 text-green-500" />
{formatCurrency(entry.financingDetails.monthlyPayment)}
</div>
</TableCell>
<TableCell className="font-semibold text-green-600">
{formatPercent(entry.kpis.roi)}
</TableCell>
<TableCell>
<div className="flex items-center gap-1">
<Calendar className="h-4 w-4 text-blue-500" />
{formatNumber(entry.kpis.paybackYears)} anos
</div>
</TableCell>
<TableCell>{formatPercent(entry.kpis.cet)}</Ta

Files changed (2) hide show
  1. script.js +228 -8
  2. style.css +96 -1
script.js CHANGED
@@ -26,11 +26,93 @@ function setupThemeToggle() {
26
 
27
  document.addEventListener('DOMContentLoaded', () => {
28
  setupThemeToggle();
29
-
30
- // Mock data for demonstration
31
  const mockData = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  panels: {
33
- topPower: [
34
  { rank: 1, brand: "SunPower", model: "X22-370", value: 370, unit: "Wp", series: "X-Series", price: 899, efficiency: 22.8, warranty: 25 },
35
  { rank: 2, brand: "LG", model: "LG370Q1C-A5", value: 370, unit: "Wp", series: "NeON R", price: 799, efficiency: 21.7, warranty: 25 },
36
  { rank: 3, brand: "Panasonic", model: "VBHN330SA16", value: 330, unit: "Wp", series: "HIT", price: 699, efficiency: 19.7, warranty: 25 },
@@ -64,12 +146,11 @@ const mockData = {
64
  ]
65
  }
66
  };
67
-
68
- // Render the equipment ranking table
69
  const app = document.getElementById('app');
70
  app.innerHTML = `
71
- <div class="space-y-6">
72
- <div class="tabs">
73
  <div class="tabs-list grid w-full grid-cols-2 gap-2 mb-6">
74
  <button class="tab-trigger active flex items-center gap-2 py-3 px-4 rounded-lg bg-primary-500 text-white" data-tab="panels">
75
  <i data-feather="zap"></i>
@@ -93,10 +174,149 @@ const mockData = {
93
  ${renderRankingTable("Top Eficiência - Inversores", mockData.inverters.topEfficiency, "trending-up", "Eficiência (%)")}
94
  </div>
95
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </div>
97
  `;
98
 
99
- // Tab functionality
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  document.querySelectorAll('.tab-trigger').forEach(trigger => {
101
  trigger.addEventListener('click', () => {
102
  const tabId = trigger.dataset.tab;
 
26
 
27
  document.addEventListener('DOMContentLoaded', () => {
28
  setupThemeToggle();
29
+ // Mock data for solar projects and financing
 
30
  const mockData = {
31
+ projects: [
32
+ {
33
+ projectCategory: "XPP",
34
+ projectTier: "ACELERADO",
35
+ systemSizeKw: 10.5,
36
+ annualEnergyGeneration: 15000,
37
+ electricityTariff: 0.85,
38
+ bestFinancingOption: {
39
+ institution: "Banco Solar",
40
+ product: "Financiamento Verde",
41
+ interestRate: 8.5,
42
+ maxTerm: 120,
43
+ ltvMax: 80
44
+ },
45
+ kpis: {
46
+ roi: 25.8,
47
+ paybackYears: 4.2,
48
+ cet: 12.3,
49
+ npv: 45000,
50
+ irr: 18.7,
51
+ lcoe: 0.32
52
+ },
53
+ financingDetails: {
54
+ monthlyPayment: 850,
55
+ totalFinancingCost: 102000,
56
+ breakEvenYears: 5.1
57
+ }
58
+ },
59
+ {
60
+ projectCategory: "PP",
61
+ projectTier: "MODERADO",
62
+ systemSizeKw: 7.2,
63
+ annualEnergyGeneration: 10500,
64
+ electricityTariff: 0.75,
65
+ bestFinancingOption: {
66
+ institution: "Banco Verde",
67
+ product: "Crédito Sustentável",
68
+ interestRate: 9.2,
69
+ maxTerm: 96,
70
+ ltvMax: 75
71
+ },
72
+ kpis: {
73
+ roi: 21.3,
74
+ paybackYears: 5.1,
75
+ cet: 13.5,
76
+ npv: 32000,
77
+ irr: 15.2,
78
+ lcoe: 0.38
79
+ },
80
+ financingDetails: {
81
+ monthlyPayment: 620,
82
+ totalFinancingCost: 59520,
83
+ breakEvenYears: 6.3
84
+ }
85
+ },
86
+ {
87
+ projectCategory: "P",
88
+ projectTier: "CONSCIENTE",
89
+ systemSizeKw: 5.0,
90
+ annualEnergyGeneration: 7200,
91
+ electricityTariff: 0.68,
92
+ bestFinancingOption: {
93
+ institution: "Banco Eco",
94
+ product: "Linha Solar",
95
+ interestRate: 10.5,
96
+ maxTerm: 84,
97
+ ltvMax: 70
98
+ },
99
+ kpis: {
100
+ roi: 18.7,
101
+ paybackYears: 6.8,
102
+ cet: 14.8,
103
+ npv: 21000,
104
+ irr: 12.5,
105
+ lcoe: 0.42
106
+ },
107
+ financingDetails: {
108
+ monthlyPayment: 480,
109
+ totalFinancingCost: 40320,
110
+ breakEvenYears: 7.5
111
+ }
112
+ }
113
+ ],
114
  panels: {
115
+ topPower: [
116
  { rank: 1, brand: "SunPower", model: "X22-370", value: 370, unit: "Wp", series: "X-Series", price: 899, efficiency: 22.8, warranty: 25 },
117
  { rank: 2, brand: "LG", model: "LG370Q1C-A5", value: 370, unit: "Wp", series: "NeON R", price: 799, efficiency: 21.7, warranty: 25 },
118
  { rank: 3, brand: "Panasonic", model: "VBHN330SA16", value: 330, unit: "Wp", series: "HIT", price: 699, efficiency: 19.7, warranty: 25 },
 
146
  ]
147
  }
148
  };
149
+ // Render the equipment ranking and KPI tables
 
150
  const app = document.getElementById('app');
151
  app.innerHTML = `
152
+ <div class="space-y-12">
153
+ <div class="tabs">
154
  <div class="tabs-list grid w-full grid-cols-2 gap-2 mb-6">
155
  <button class="tab-trigger active flex items-center gap-2 py-3 px-4 rounded-lg bg-primary-500 text-white" data-tab="panels">
156
  <i data-feather="zap"></i>
 
174
  ${renderRankingTable("Top Eficiência - Inversores", mockData.inverters.topEfficiency, "trending-up", "Eficiência (%)")}
175
  </div>
176
  </div>
177
+ <div class="space-y-6">
178
+ <div class="tabs">
179
+ <div class="tabs-list grid w-full grid-cols-2 gap-2 mb-6">
180
+ <button class="tab-trigger active flex items-center gap-2 py-3 px-4 rounded-lg bg-primary-500 text-white" data-tab="panels">
181
+ <i data-feather="zap"></i>
182
+ Painéis Solares
183
+ </button>
184
+ <button class="tab-trigger flex items-center gap-2 py-3 px-4 rounded-lg bg-gray-200 text-gray-700" data-tab="inverters">
185
+ <i data-feather="trending-up"></i>
186
+ Inversores
187
+ </button>
188
+ </div>
189
+
190
+ <div class="tab-content active" data-tab="panels">
191
+ ${renderRankingTable("Maior Potência - Painéis Solares", mockData.panels.topPower, "zap", "Potência (Wp)")}
192
+ ${renderRankingTable("Melhor Preço por Watt - Painéis Solares", mockData.panels.bestPricePerWatt, "dollar-sign", "Preço/W (R$/W)")}
193
+ ${renderRankingTable("Top Eficiência - Painéis Solares", mockData.panels.topEfficiency, "trending-up", "Eficiência (%)")}
194
+ </div>
195
+
196
+ <div class="tab-content hidden" data-tab="inverters">
197
+ ${renderRankingTable("Maior Garantia - Inversores", mockData.inverters.longestWarranty, "shield", "Garantia (anos)")}
198
+ ${renderRankingTable("Melhor Preço por kW - Inversores", mockData.inverters.bestPricePerKw, "dollar-sign", "Preço/kW (R$/kW)")}
199
+ ${renderRankingTable("Top Eficiência - Inversores", mockData.inverters.topEfficiency, "trending-up", "Eficiência (%)")}
200
+ </div>
201
+ </div>
202
+ </div>
203
+
204
+ <div class="border-gradient">
205
+ <div class="card-header p-6">
206
+ <h3 class="text-xl font-bold flex items-center gap-2">
207
+ <i data-feather="trending-up" class="text-primary-500"></i>
208
+ Leaderboard KPI - Projetos Solares vs Financiamento
209
+ </h3>
210
+ <p class="text-gray-600 mt-1">Comparação de indicadores financeiros por categoria e tier de projeto</p>
211
+ </div>
212
+ <div class="card-content px-6 pb-6">
213
+ <div class="flex gap-4 mb-4">
214
+ <select class="w-48 p-2 rounded border border-gray-300">
215
+ <option value="roi">ROI (%)</option>
216
+ <option value="paybackYears">Payback (anos)</option>
217
+ <option value="cet">CET (%)</option>
218
+ <option value="npv">NPV (R$)</option>
219
+ <option value="irr">IRR (%)</option>
220
+ <option value="lcoe">LCOE (R$/kWh)</option>
221
+ </select>
222
+ <button class="flex items-center gap-2 px-4 py-2 bg-gray-100 rounded hover:bg-gray-200">
223
+ <i data-feather="arrow-up-down" class="h-4 w-4"></i>
224
+ Ordenar
225
+ </button>
226
+ </div>
227
+ <div class="overflow-x-auto">
228
+ <table class="w-full">
229
+ <thead>
230
+ <tr class="text-left border-b">
231
+ <th class="pb-3">Projeto</th>
232
+ <th class="pb-3">Tamanho</th>
233
+ <th class="pb-3">Geração Anual</th>
234
+ <th class="pb-3">Tarifa</th>
235
+ <th class="pb-3">Instituição</th>
236
+ <th class="pb-3">Produto</th>
237
+ <th class="pb-3">Taxa (%)</th>
238
+ <th class="pb-3">Prazo (meses)</th>
239
+ <th class="pb-3">Parcela Mensal</th>
240
+ <th class="pb-3 cursor-pointer">ROI</th>
241
+ <th class="pb-3 cursor-pointer">Payback</th>
242
+ <th class="pb-3 cursor-pointer">CET</th>
243
+ <th class="pb-3 cursor-pointer">NPV</th>
244
+ <th class="pb-3 cursor-pointer">LCOE</th>
245
+ <th class="pb-3">Break-even</th>
246
+ </tr>
247
+ </thead>
248
+ <tbody>
249
+ ${mockData.projects.map(project => `
250
+ <tr class="border-b hover:bg-primary-50 transition-colors cursor-pointer">
251
+ <td class="py-4">
252
+ <div class="flex gap-2">
253
+ <span class="px-2 py-1 rounded text-xs ${getCategoryColor(project.projectCategory)}">
254
+ ${project.projectCategory}
255
+ </span>
256
+ <span class="px-2 py-1 rounded text-xs ${getTierColor(project.projectTier)}">
257
+ ${project.projectTier}
258
+ </span>
259
+ </div>
260
+ </td>
261
+ <td>
262
+ <div class="flex items-center gap-1">
263
+ <i data-feather="zap" class="h-4 w-4 text-yellow-500"></i>
264
+ ${formatNumber(project.systemSizeKw)} kWp
265
+ </div>
266
+ </td>
267
+ <td>${formatNumber(project.annualEnergyGeneration)} kWh/ano</td>
268
+ <td>${formatCurrency(project.electricityTariff)}</td>
269
+ <td class="font-medium">${project.bestFinancingOption.institution}</td>
270
+ <td>${project.bestFinancingOption.product}</td>
271
+ <td>${formatPercent(project.bestFinancingOption.interestRate)}</td>
272
+ <td>${project.bestFinancingOption.maxTerm} meses</td>
273
+ <td>
274
+ <div class="flex items-center gap-1">
275
+ <i data-feather="dollar-sign" class="h-4 w-4 text-green-500"></i>
276
+ ${formatCurrency(project.financingDetails.monthlyPayment)}
277
+ </div>
278
+ </td>
279
+ <td class="font-semibold text-green-600">${formatPercent(project.kpis.roi)}</td>
280
+ <td>
281
+ <div class="flex items-center gap-1">
282
+ <i data-feather="calendar" class="h-4 w-4 text-blue-500"></i>
283
+ ${formatNumber(project.kpis.paybackYears)} anos
284
+ </div>
285
+ </td>
286
+ <td>${formatPercent(project.kpis.cet)}</td>
287
+ <td>${formatCurrency(project.kpis.npv)}</td>
288
+ <td>${formatCurrency(project.kpis.lcoe)}</td>
289
+ <td>${formatNumber(project.financingDetails.breakEvenYears)} anos</td>
290
+ </tr>
291
+ `).join('')}
292
+ </tbody>
293
+ </table>
294
+ </div>
295
+ </div>
296
+ </div>
297
  </div>
298
  `;
299
 
300
+ // Add event listeners for sorting
301
+ document.querySelectorAll('th.cursor-pointer').forEach(th => {
302
+ th.addEventListener('click', () => {
303
+ const column = th.textContent.trim().toLowerCase();
304
+ console.log(`Sorting by ${column}`);
305
+ // Implement sorting logic here
306
+ });
307
+ });
308
+
309
+ document.querySelector('select').addEventListener('change', (e) => {
310
+ const sortBy = e.target.value;
311
+ console.log(`Sorting by ${sortBy}`);
312
+ // Implement sorting logic here
313
+ });
314
+
315
+ document.querySelector('button').addEventListener('click', () => {
316
+ console.log('Toggling sort order');
317
+ // Implement sort order toggle here
318
+ });
319
+ // Tab functionality
320
  document.querySelectorAll('.tab-trigger').forEach(trigger => {
321
  trigger.addEventListener('click', () => {
322
  const tabId = trigger.dataset.tab;
style.css CHANGED
@@ -33,10 +33,105 @@
33
  .dark-theme table td {
34
  border-color: #334155;
35
  }
36
-
37
  .dark-theme .hover-scale:hover {
38
  background-color: #1e293b;
39
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  .glass-effect {
41
  background: rgba(255, 255, 255, 0.7);
42
  backdrop-filter: blur(10px);
 
33
  .dark-theme table td {
34
  border-color: #334155;
35
  }
 
36
  .dark-theme .hover-scale:hover {
37
  background-color: #1e293b;
38
  }
39
+
40
+ /* KPI Table Styles */
41
+ .kpi-table {
42
+ width: 100%;
43
+ border-collapse: collapse;
44
+ }
45
+
46
+ .kpi-table th {
47
+ text-align: left;
48
+ padding: 0.75rem;
49
+ border-bottom: 1px solid #e5e7eb;
50
+ font-weight: 600;
51
+ color: #4b5563;
52
+ }
53
+
54
+ .kpi-table td {
55
+ padding: 0.75rem;
56
+ border-bottom: 1px solid #e5e7eb;
57
+ }
58
+
59
+ .kpi-table tr:hover {
60
+ background-color: #f9fafb;
61
+ }
62
+
63
+ .dark-theme .kpi-table th {
64
+ color: #9ca3af;
65
+ border-color: #374151;
66
+ }
67
+
68
+ .dark-theme .kpi-table td {
69
+ border-color: #374151;
70
+ }
71
+
72
+ .dark-theme .kpi-table tr:hover {
73
+ background-color: #1f2937;
74
+ }
75
+
76
+ /* Badge Styles */
77
+ .badge {
78
+ display: inline-block;
79
+ padding: 0.25rem 0.5rem;
80
+ border-radius: 0.25rem;
81
+ font-size: 0.75rem;
82
+ font-weight: 600;
83
+ text-transform: uppercase;
84
+ }
85
+
86
+ .badge-blue {
87
+ background-color: #dbeafe;
88
+ color: #1e40af;
89
+ }
90
+
91
+ .badge-green {
92
+ background-color: #d1fae5;
93
+ color: #065f46;
94
+ }
95
+
96
+ .badge-yellow {
97
+ background-color: #fef3c7;
98
+ color: #92400e;
99
+ }
100
+
101
+ .badge-orange {
102
+ background-color: #ffedd5;
103
+ color: #9a3412;
104
+ }
105
+
106
+ .badge-red {
107
+ background-color: #fee2e2;
108
+ color: #991b1b;
109
+ }
110
+
111
+ .badge-purple {
112
+ background-color: #f3e8ff;
113
+ color: #6b21a8;
114
+ }
115
+
116
+ .badge-gray {
117
+ background-color: #f3f4f6;
118
+ color: #4b5563;
119
+ }
120
+
121
+ .badge-emerald {
122
+ background-color: #d1fae5;
123
+ color: #047857;
124
+ }
125
+
126
+ .badge-amber {
127
+ background-color: #fef3c7;
128
+ color: #b45309;
129
+ }
130
+
131
+ .badge-rose {
132
+ background-color: #ffe4e6;
133
+ color: #be123c;
134
+ }
135
  .glass-effect {
136
  background: rgba(255, 255, 255, 0.7);
137
  backdrop-filter: blur(10px);