fernando-bold commited on
Commit
125f187
·
verified ·
1 Parent(s): c124179

'use client'

Browse files

import { useState } 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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Trophy, Zap, DollarSign, Shield, TrendingUp, Award } from 'lucide-react'

interface EquipmentRanking {
rank: number
brand: string
model: string
value: number
unit: string
series: string
price?: number
efficiency?: number
warranty?: number
}

interface EquipmentRankingTableProps {
panels: {
topPower: EquipmentRanking[]
bestPricePerWatt: EquipmentRanking[]
topEfficiency: EquipmentRanking[]
}
inverters: {
longestWarranty: EquipmentRanking[]
bestPricePerKw: EquipmentRanking[]
topEfficiency: EquipmentRanking[]
}
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 formatNumber = (value: number, decimals = 2) => {
return value.toFixed(decimals)
}

const getRankColor = (rank: number) => {
if (rank === 1) return 'bg-yellow-100 text-yellow-800 border-yellow-300'
if (rank === 2) return 'bg-gray-100 text-gray-800 border-gray-300'
if (rank === 3) return 'bg-orange-100 text-orange-800 border-orange-300'
return 'bg-blue-50 text-blue-800 border-blue-200'
}

const getRankIcon = (rank: number) => {
if (rank === 1) return <Trophy className="h-4 w-4 text-yellow-600" />
if (rank === 2) return <Award className="h-4 w-4 text-gray-600" />
if (rank === 3) return <Award className="h-4 w-4 text-orange-600" />
return <span className="text-sm font-bold text-blue-600">#{rank}</span>
}

function RankingTable({
title,
data,
icon,
valueLabel,
onInteraction
}: {
title: string
data: EquipmentRanking[]
icon: React.ReactNode
valueLabel: string
onInteraction?: (action: string, data?: any) => void
}) {
return (
<Card className="border-gradient">
<CardHeader>
<CardTitle className="flex items-center gap-2">
{icon}
{title}
</CardTitle>
<CardDescription>
Top 10 equipamentos ordenados por {valueLabel.toLowerCase()}
</CardDescription>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>Posição</TableHead>
<TableHead>Marca</TableHead>
<TableHead>Modelo</TableHead>
<TableHead>Série</TableHead>
<TableHead>{valueLabel}</TableHead>
<TableHead>Preço Médio</TableHead>
<TableHead>Eficiência</TableHead>
<TableHead>Garantia</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((item) => (
<TableRow
key={`${item.brand}-${item.model}`}
className="hover:bg-[var(--glass-light)] hover:backdrop-blur-sm transition-all duration-200 cursor-pointer"
onClick={() => onInteraction?.("equipment_click", { item })}
>
<TableCell>
<div className="flex items-center gap-2">
<Badge className={getRankColor(item.rank)}>
{getRankIcon(item.rank)}
</Badge>
</div>
</TableCell>
<TableCell className="font-medium">{item.brand}</TableCell>
<TableCell className="font-mono text-sm">{item.model}</TableCell>
<TableCell className="text-sm text-gray-600">{item.series}</TableCell>
<TableCell className="font-semibold text-blue-600">
{item.unit.includes('R$') ? formatCurrency(item.value) : `${formatNumber(item.value)} ${item.unit}`}
</TableCell>
<TableCell>
{item.price ? formatCurrency(item.price) : '-'}
</TableCell>
<TableCell>
{item.efficiency ? `${formatNumber(item.efficiency)}%` : '-'}
</TableCell>
<TableCell>
{item.warranty ? `${item.warranty} anos` : '-'}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
)
}

export function EquipmentRankingTable({
panels,
inverters,
isLoading,
onInteraction,
}: Readonly<EquipmentRankingTableProps>) {
const [activeTab, setActiveTab] = useState("panels")

if (isLoading) {
return (
<Card className="border-gradient">
<CardHeader>
<CardTitle>Carregando Rankings de Equipamentos...</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 (
<div className="space-y-6">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="panels" className="flex items-center gap-2">
<Zap className="h-4 w-4" />
Painéis Solares
</TabsTrigger>
<TabsTrigger value="inverters" className="flex items-center gap-2">
<TrendingUp className="h-4 w-4" />
Inversores
</TabsTrigger>
</TabsList>

<TabsContent value="panels" className="space-y-6">
<RankingTable
title="Maior Potência - Painéis Solares"
data={panels.topPower}
icon={<Zap className="h-5 w-5 text-yellow-500" />}
valueLabel="Potência (Wp)"
onInteraction={onInteraction}
/>

<RankingTable
title="Melhor Preço por Watt - Painéis Solares"
data={panels.bestPricePerWatt}
icon={<DollarSign className="h-5 w-5 text-green-500" />}
valueLabel="Preço/W (R$/W)"
onInteraction={onInteraction}
/>

<RankingTable
title="Top Eficiência - Painéis Solares"
data={panels.topEfficiency}
icon={<TrendingUp className="h-5 w-5 text-blue-500" />}
valueLabel="Eficiência (%)"
onInteraction={onInteraction}
/>
</TabsContent>

<TabsContent value="inverters" className="space-y-6">
<RankingTable
title="Maior Garantia - Inversores"
data={inverters.longestWarranty}
icon={<Shield className="h-5 w-5 text-purple-500" />}
valueLabel="Garantia (anos)"
onInteraction={onInteraction}
/>

<RankingTable
title="Melhor Preço por kW - Inversores"
data={inverters.bestPricePerKw}
icon={<DollarSign className="h-5 w-5 text-green-500" />}
valueLabel="Preço/kW (R$/kW)"
onInteraction={onInteraction}
/>

<RankingTable
title="Top Eficiência - Inversores"
data={inverters.topEfficiency}
icon={<TrendingUp className="h-5 w-5 text-blue-500" />}
valueLabel="Eficiência (%)"
onInteraction={onInteraction}
/>
</TabsContent>
</Tabs>
</div>
)
}

Files changed (6) hide show
  1. README.md +7 -4
  2. components/footer.js +97 -0
  3. components/header.js +76 -0
  4. index.html +59 -19
  5. script.js +172 -0
  6. style.css +24 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Solar Equipment Showdown
3
- emoji: 📉
4
  colorFrom: pink
5
- colorTo: yellow
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: Solar Equipment Showdown
 
3
  colorFrom: pink
4
+ colorTo: red
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/footer.js ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomFooter extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ footer {
7
+ background-color: #1e293b;
8
+ color: white;
9
+ padding: 2rem 0;
10
+ margin-top: 3rem;
11
+ }
12
+
13
+ .container {
14
+ max-width: 1200px;
15
+ margin: 0 auto;
16
+ padding: 0 1rem;
17
+ display: grid;
18
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
19
+ gap: 2rem;
20
+ }
21
+
22
+ .footer-section h3 {
23
+ font-size: 1.125rem;
24
+ font-weight: 600;
25
+ margin-bottom: 1rem;
26
+ color: #e2e8f0;
27
+ }
28
+
29
+ .footer-section ul {
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 0.5rem;
33
+ }
34
+
35
+ .footer-section a {
36
+ color: #94a3b8;
37
+ transition: color 0.2s;
38
+ }
39
+
40
+ .footer-section a:hover {
41
+ color: #e2e8f0;
42
+ }
43
+
44
+ .copyright {
45
+ text-align: center;
46
+ margin-top: 2rem;
47
+ padding-top: 2rem;
48
+ border-top: 1px solid #334155;
49
+ color: #94a3b8;
50
+ font-size: 0.875rem;
51
+ }
52
+
53
+ @media (max-width: 640px) {
54
+ .container {
55
+ grid-template-columns: 1fr;
56
+ }
57
+ }
58
+ </style>
59
+ <footer>
60
+ <div class="container">
61
+ <div class="footer-section">
62
+ <h3>Solar Showdown</h3>
63
+ <p class="text-slate-400">Comparando os melhores equipamentos solares para você tomar a melhor decisão.</p>
64
+ </div>
65
+ <div class="footer-section">
66
+ <h3>Links</h3>
67
+ <ul>
68
+ <li><a href="#panels">Painéis Solares</a></li>
69
+ <li><a href="#inverters">Inversores</a></li>
70
+ <li><a href="#comparison">Comparador</a></li>
71
+ </ul>
72
+ </div>
73
+ <div class="footer-section">
74
+ <h3>Recursos</h3>
75
+ <ul>
76
+ <li><a href="#blog">Blog</a></li>
77
+ <li><a href="#faq">FAQ</a></li>
78
+ <li><a href="#contact">Contato</a></li>
79
+ </ul>
80
+ </div>
81
+ <div class="footer-section">
82
+ <h3>Legal</h3>
83
+ <ul>
84
+ <li><a href="#privacy">Privacidade</a></li>
85
+ <li><a href="#terms">Termos</a></li>
86
+ <li><a href="#cookies">Cookies</a></li>
87
+ </ul>
88
+ </div>
89
+ </div>
90
+ <div class="copyright">
91
+ &copy; ${new Date().getFullYear()} Solar Showdown. Todos os direitos reservados.
92
+ </div>
93
+ </footer>
94
+ `;
95
+ }
96
+ }
97
+ customElements.define('custom-footer', CustomFooter);
components/header.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomHeader extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ header {
7
+ background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);
8
+ color: white;
9
+ padding: 1.5rem 0;
10
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
11
+ }
12
+
13
+ .container {
14
+ max-width: 1200px;
15
+ margin: 0 auto;
16
+ padding: 0 1rem;
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: center;
20
+ }
21
+
22
+ .logo {
23
+ font-size: 1.5rem;
24
+ font-weight: bold;
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 0.5rem;
28
+ }
29
+
30
+ nav ul {
31
+ display: flex;
32
+ gap: 1.5rem;
33
+ }
34
+
35
+ nav a {
36
+ color: white;
37
+ font-weight: 500;
38
+ transition: opacity 0.2s;
39
+ }
40
+
41
+ nav a:hover {
42
+ opacity: 0.8;
43
+ }
44
+
45
+ @media (max-width: 768px) {
46
+ .container {
47
+ flex-direction: column;
48
+ gap: 1rem;
49
+ }
50
+
51
+ nav ul {
52
+ flex-wrap: wrap;
53
+ justify-content: center;
54
+ }
55
+ }
56
+ </style>
57
+ <header>
58
+ <div class="container">
59
+ <a href="/" class="logo">
60
+ <i data-feather="zap"></i>
61
+ Solar Showdown
62
+ </a>
63
+ <nav>
64
+ <ul>
65
+ <li><a href="#panels">Painéis</a></li>
66
+ <li><a href="#inverters">Inversores</a></li>
67
+ <li><a href="#comparison">Comparar</a></li>
68
+ <li><a href="#about">Sobre</a></li>
69
+ </ul>
70
+ </nav>
71
+ </div>
72
+ </header>
73
+ `;
74
+ }
75
+ }
76
+ customElements.define('custom-header', CustomHeader);
index.html CHANGED
@@ -1,19 +1,59 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Solar Equipment Showdown</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script>
12
+ tailwind.config = {
13
+ theme: {
14
+ extend: {
15
+ colors: {
16
+ primary: {
17
+ 50: '#f0f9ff',
18
+ 100: '#e0f2fe',
19
+ 200: '#bae6fd',
20
+ 300: '#7dd3fc',
21
+ 400: '#38bdf8',
22
+ 500: '#0ea5e9',
23
+ 600: '#0284c7',
24
+ 700: '#0369a1',
25
+ 800: '#075985',
26
+ 900: '#0c4a6e',
27
+ },
28
+ secondary: {
29
+ 50: '#f5f3ff',
30
+ 100: '#ede9fe',
31
+ 200: '#ddd6fe',
32
+ 300: '#c4b5fd',
33
+ 400: '#a78bfa',
34
+ 500: '#8b5cf6',
35
+ 600: '#7c3aed',
36
+ 700: '#6d28d9',
37
+ 800: '#5b21b6',
38
+ 900: '#4c1d95',
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ </script>
45
+ </head>
46
+ <body class="bg-gray-50">
47
+ <custom-header></custom-header>
48
+ <main class="container mx-auto px-4 py-8">
49
+ <div id="app" class="space-y-8"></div>
50
+ </main>
51
+ <custom-footer></custom-footer>
52
+
53
+ <script src="components/header.js"></script>
54
+ <script src="components/footer.js"></script>
55
+ <script src="script.js"></script>
56
+ <script>feather.replace();</script>
57
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
58
+ </body>
59
+ </html>
script.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ // Mock data for demonstration
3
+ const mockData = {
4
+ panels: {
5
+ topPower: [
6
+ { rank: 1, brand: "SunPower", model: "X22-370", value: 370, unit: "Wp", series: "X-Series", price: 899, efficiency: 22.8, warranty: 25 },
7
+ { rank: 2, brand: "LG", model: "LG370Q1C-A5", value: 370, unit: "Wp", series: "NeON R", price: 799, efficiency: 21.7, warranty: 25 },
8
+ { rank: 3, brand: "Panasonic", model: "VBHN330SA16", value: 330, unit: "Wp", series: "HIT", price: 699, efficiency: 19.7, warranty: 25 },
9
+ ],
10
+ bestPricePerWatt: [
11
+ { rank: 1, brand: "Canadian Solar", model: "CS3K-395MS", value: 2.15, unit: "R$/W", series: "HiKu", price: 850, efficiency: 19.9, warranty: 12 },
12
+ { rank: 2, brand: "Trina Solar", model: "TSM-395DE15", value: 2.20, unit: "R$/W", series: "Vertex S", price: 869, efficiency: 20.3, warranty: 15 },
13
+ { rank: 3, brand: "Jinko Solar", model: "JKM395M-72HL4", value: 2.25, unit: "R$/W", series: "Tiger Pro", price: 889, efficiency: 20.4, warranty: 15 },
14
+ ],
15
+ topEfficiency: [
16
+ { rank: 1, brand: "SunPower", model: "Maxeon 3", value: 22.6, unit: "%", series: "Maxeon", price: 999, efficiency: 22.6, warranty: 25 },
17
+ { rank: 2, brand: "LG", model: "LG380Q1C-A5", value: 21.7, unit: "%", series: "NeON R", price: 849, efficiency: 21.7, warranty: 25 },
18
+ { rank: 3, brand: "REC", model: "REC370AA", value: 21.3, unit: "%", series: "Alpha Pure", price: 799, efficiency: 21.3, warranty: 25 },
19
+ ]
20
+ },
21
+ inverters: {
22
+ longestWarranty: [
23
+ { rank: 1, brand: "Fronius", model: "Symo 10.0-3-M", value: 10, unit: "anos", series: "Symo", price: 12500, efficiency: 98.1 },
24
+ { rank: 2, brand: "SMA", model: "Sunny Tripower 10.0", value: 10, unit: "anos", series: "Tripower", price: 11900, efficiency: 98.3 },
25
+ { rank: 3, brand: "Huawei", model: "SUN2000-10KTL-M1", value: 10, unit: "anos", series: "SUN2000", price: 10900, efficiency: 98.4 },
26
+ ],
27
+ bestPricePerKw: [
28
+ { rank: 1, brand: "Growatt", model: "MIN 10000TL-X", value: 850, unit: "R$/kW", series: "MIN", price: 8500, efficiency: 97.8 },
29
+ { rank: 2, brand: "Solis", model: "S6-GR1P10K", value: 900, unit: "R$/kW", series: "S6", price: 9000, efficiency: 98.0 },
30
+ { rank: 3, brand: "GoodWe", model: "GW10KN-DT", value: 950, unit: "R$/kW", series: "DNS", price: 9500, efficiency: 98.2 },
31
+ ],
32
+ topEfficiency: [
33
+ { rank: 1, brand: "Huawei", model: "SUN2000-10KTL-M1", value: 98.4, unit: "%", series: "SUN2000", price: 10900, warranty: 10 },
34
+ { rank: 2, brand: "SMA", model: "Sunny Tripower 10.0", value: 98.3, unit: "%", series: "Tripower", price: 11900, warranty: 10 },
35
+ { rank: 3, brand: "Fronius", model: "Symo 10.0-3-M", value: 98.1, unit: "%", series: "Symo", price: 12500, warranty: 10 },
36
+ ]
37
+ }
38
+ };
39
+
40
+ // Render the equipment ranking table
41
+ const app = document.getElementById('app');
42
+ app.innerHTML = `
43
+ <div class="space-y-6">
44
+ <div class="tabs">
45
+ <div class="tabs-list grid w-full grid-cols-2 gap-2 mb-6">
46
+ <button class="tab-trigger active flex items-center gap-2 py-3 px-4 rounded-lg bg-primary-500 text-white" data-tab="panels">
47
+ <i data-feather="zap"></i>
48
+ Painéis Solares
49
+ </button>
50
+ <button class="tab-trigger flex items-center gap-2 py-3 px-4 rounded-lg bg-gray-200 text-gray-700" data-tab="inverters">
51
+ <i data-feather="trending-up"></i>
52
+ Inversores
53
+ </button>
54
+ </div>
55
+
56
+ <div class="tab-content active" data-tab="panels">
57
+ ${renderRankingTable("Maior Potência - Painéis Solares", mockData.panels.topPower, "zap", "Potência (Wp)")}
58
+ ${renderRankingTable("Melhor Preço por Watt - Painéis Solares", mockData.panels.bestPricePerWatt, "dollar-sign", "Preço/W (R$/W)")}
59
+ ${renderRankingTable("Top Eficiência - Painéis Solares", mockData.panels.topEfficiency, "trending-up", "Eficiência (%)")}
60
+ </div>
61
+
62
+ <div class="tab-content hidden" data-tab="inverters">
63
+ ${renderRankingTable("Maior Garantia - Inversores", mockData.inverters.longestWarranty, "shield", "Garantia (anos)")}
64
+ ${renderRankingTable("Melhor Preço por kW - Inversores", mockData.inverters.bestPricePerKw, "dollar-sign", "Preço/kW (R$/kW)")}
65
+ ${renderRankingTable("Top Eficiência - Inversores", mockData.inverters.topEfficiency, "trending-up", "Eficiência (%)")}
66
+ </div>
67
+ </div>
68
+ </div>
69
+ `;
70
+
71
+ // Tab functionality
72
+ document.querySelectorAll('.tab-trigger').forEach(trigger => {
73
+ trigger.addEventListener('click', () => {
74
+ const tabId = trigger.dataset.tab;
75
+
76
+ // Update active tab button
77
+ document.querySelectorAll('.tab-trigger').forEach(t => {
78
+ t.classList.remove('active', 'bg-primary-500', 'text-white');
79
+ t.classList.add('bg-gray-200', 'text-gray-700');
80
+ });
81
+ trigger.classList.add('active', 'bg-primary-500', 'text-white');
82
+ trigger.classList.remove('bg-gray-200', 'text-gray-700');
83
+
84
+ // Show active tab content
85
+ document.querySelectorAll('.tab-content').forEach(content => {
86
+ content.classList.add('hidden');
87
+ content.classList.remove('active');
88
+ });
89
+ document.querySelector(`.tab-content[data-tab="${tabId}"]`).classList.remove('hidden');
90
+ document.querySelector(`.tab-content[data-tab="${tabId}"]`).classList.add('active');
91
+
92
+ feather.replace();
93
+ });
94
+ });
95
+
96
+ feather.replace();
97
+ });
98
+
99
+ function renderRankingTable(title, data, icon, valueLabel) {
100
+ return `
101
+ <div class="border-gradient hover-scale mb-6">
102
+ <div class="card-header p-6">
103
+ <h3 class="text-xl font-bold flex items-center gap-2">
104
+ <i data-feather="${icon}" class="text-primary-500"></i>
105
+ ${title}
106
+ </h3>
107
+ <p class="text-gray-600 mt-1">Top 10 equipamentos ordenados por ${valueLabel.toLowerCase()}</p>
108
+ </div>
109
+ <div class="card-content px-6 pb-6 overflow-x-auto">
110
+ <table class="w-full">
111
+ <thead>
112
+ <tr class="text-left border-b">
113
+ <th class="pb-3">Posição</th>
114
+ <th class="pb-3">Marca</th>
115
+ <th class="pb-3">Modelo</th>
116
+ <th class="pb-3">Série</th>
117
+ <th class="pb-3">${valueLabel}</th>
118
+ <th class="pb-3">Preço Médio</th>
119
+ <th class="pb-3">Eficiência</th>
120
+ <th class="pb-3">Garantia</th>
121
+ </tr>
122
+ </thead>
123
+ <tbody>
124
+ ${data.map(item => `
125
+ <tr class="border-b hover:bg-primary-50 transition-colors cursor-pointer" data-id="${item.brand}-${item.model}">
126
+ <td class="py-4">
127
+ <span class="rank-badge ${getRankColor(item.rank)}">
128
+ ${getRankIcon(item.rank)}
129
+ </span>
130
+ </td>
131
+ <td class="font-medium">${item.brand}</td>
132
+ <td class="font-mono text-sm">${item.model}</td>
133
+ <td class="text-sm text-gray-600">${item.series}</td>
134
+ <td class="font-semibold text-primary-600">
135
+ ${item.unit.includes('R$') ? formatCurrency(item.value) : `${formatNumber(item.value)} ${item.unit}`}
136
+ </td>
137
+ <td>${item.price ? formatCurrency(item.price) : '-'}</td>
138
+ <td>${item.efficiency ? `${formatNumber(item.efficiency)}%` : '-'}</td>
139
+ <td>${item.warranty ? `${item.warranty} anos` : '-'}</td>
140
+ </tr>
141
+ `).join('')}
142
+ </tbody>
143
+ </table>
144
+ </div>
145
+ </div>
146
+ `;
147
+ }
148
+
149
+ function formatCurrency(value) {
150
+ return new Intl.NumberFormat('pt-BR', {
151
+ style: 'currency',
152
+ currency: 'BRL'
153
+ }).format(value);
154
+ }
155
+
156
+ function formatNumber(value, decimals = 2) {
157
+ return value.toFixed(decimals);
158
+ }
159
+
160
+ function getRankColor(rank) {
161
+ if (rank === 1) return 'bg-yellow-100 text-yellow-800 border-yellow-300';
162
+ if (rank === 2) return 'bg-gray-100 text-gray-800 border-gray-300';
163
+ if (rank === 3) return 'bg-orange-100 text-orange-800 border-orange-300';
164
+ return 'bg-blue-50 text-blue-800 border-blue-200';
165
+ }
166
+
167
+ function getRankIcon(rank) {
168
+ if (rank === 1) return '<i data-feather="award" class="text-yellow-600"></i>';
169
+ if (rank === 2) return '<i data-feather="award" class="text-gray-600"></i>';
170
+ if (rank === 3) return '<i data-feather="award" class="text-orange-600"></i>';
171
+ return `<span class="text-sm font-bold text-blue-600">#${rank}</span>`;
172
+ }
style.css CHANGED
@@ -1,28 +1,33 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
1
+ .border-gradient {
2
+ border: 1px solid transparent;
3
+ background: linear-gradient(white, white) padding-box,
4
+ linear-gradient(to right, #0ea5e9, #8b5cf6) border-box;
5
+ border-radius: 0.5rem;
6
  }
7
 
8
+ .glass-effect {
9
+ background: rgba(255, 255, 255, 0.7);
10
+ backdrop-filter: blur(10px);
11
+ -webkit-backdrop-filter: blur(10px);
12
  }
13
 
14
+ .hover-scale {
15
+ transition: transform 0.2s ease-in-out;
 
 
 
16
  }
17
 
18
+ .hover-scale:hover {
19
+ transform: scale(1.02);
 
 
 
 
20
  }
21
 
22
+ .animate-pulse {
23
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
24
  }
25
+
26
+ @keyframes pulse {
27
+ 0%, 100% {
28
+ opacity: 1;
29
+ }
30
+ 50% {
31
+ opacity: 0.5;
32
+ }
33
+ }