add get and post methods

This commit is contained in:
2025-06-07 11:37:48 +01:00
parent 56349ccc39
commit 8311cd4f6a
14 changed files with 4394 additions and 235 deletions

View File

@@ -4,12 +4,19 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> <!-- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> -->
<script src="https://cdn.tailwindcss.com"></script> <!-- Remover link para tailwind.min.css -->
<!-- <link href="/tailwind.min.css" rel="stylesheet"> -->
<style>
body {
font-family: 'Arial', 'Helvetica', 'sans-serif'; /* Usando fontes genéricas do sistema */
}
</style>
<title>Vite + React</title> <title>Vite + React</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<!-- Adicionar o link para o seu arquivo CSS com o Tailwind configurado -->
<script type="module" src="/src/main.jsx"></script> <script type="module" src="/src/main.jsx"></script>
</body> </body>
</html> </html>

4227
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,10 +21,15 @@
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2", "@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1", "@vitejs/plugin-react": "^4.4.1",
"autoprefixer": "^10.4.21",
"eslint": "^9.25.0", "eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19", "eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0", "globals": "^16.0.0",
"live-server": "^1.2.2",
"postcss": "^8.5.4",
"postcss-cli": "^11.0.1",
"tailwindcss": "^3.4.17",
"vite": "^6.3.5" "vite": "^6.3.5"
} }
} }

6
postcss.config.cjs Executable file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -9,17 +9,21 @@ const Navbar = () => {
setIsMenuOpen(!isMenuOpen); // Alterna o estado do menu (aberto/fechado) setIsMenuOpen(!isMenuOpen); // Alterna o estado do menu (aberto/fechado)
}; };
const handleLinkClick = () => {
setIsMenuOpen(false); // Fecha o menu quando um link é clicado
};
return ( return (
<nav className="bg-gradient-to-r from-green-700 to-green-600 text-white p-4 shadow"> <nav className="bg-gradient-to-r from-green-700 to-green-600 text-white p-4 shadow">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="font-bold text-xl">EVSE</div> <div className="font-bold text-xl">EVSE</div>
<ul className={`flex-col md:flex-row md:flex gap-4 ${isMenuOpen ? 'flex' : 'hidden'} md:!flex`}> <ul className={`flex-col md:flex-row md:flex gap-4 ${isMenuOpen ? 'flex' : 'hidden'} md:!flex`}>
<li><Link className="hover:underline" to="/dashboard">Dashboard</Link></li> <li><Link className="hover:underline" to="/dashboard" onClick={handleLinkClick}>Dashboard</Link></li>
<li><Link className="hover:underline" to="/settings">Settings</Link></li> <li><Link className="hover:underline" to="/settings" onClick={handleLinkClick}>Settings</Link></li>
<li><Link className="hover:underline" to="/security">Security</Link></li> <li><Link className="hover:underline" to="/security" onClick={handleLinkClick}>Security</Link></li>
<li><Link className="hover:underline" to="/connectivity">Connectivity</Link></li> <li><Link className="hover:underline" to="/connectivity" onClick={handleLinkClick}>Connectivity</Link></li>
<li><Link className="hover:underline" to="/ocpp">OCPP</Link></li> <li><Link className="hover:underline" to="/ocpp" onClick={handleLinkClick}>OCPP</Link></li>
<li><Link className="hover:underline" to="/electrical-network">Rede Elétrica</Link></li> <li><Link className="hover:underline" to="/electrical-network" onClick={handleLinkClick}>Rede Elétrica</Link></li>
</ul> </ul>
<button className="md:hidden text-3xl" onClick={toggleMenu}> <button className="md:hidden text-3xl" onClick={toggleMenu}>
&#9776; &#9776;

3
src/index.css Executable file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -2,6 +2,8 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import App from './App'; import App from './App';
import './index.css'; // ou 'styles.css'
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode> <React.StrictMode>

View File

@@ -1,14 +1,11 @@
// src/pages/Connectivity.jsx
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { get, post } from '../api'; import { get, post } from '../api';
import PageLayout from '../components/PageLayout'; import PageLayout from '../components/PageLayout';
const Connectivity = () => { const Connectivity = () => {
const [status, setStatus] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [wifiConfig, setWifiConfig] = useState({ ssid: '', password: '' }); const [wifiConfig, setWifiConfig] = useState({ enabled: false, ssid: '', password: '' });
const [wifiNetworks, setWifiNetworks] = useState([]);
const [wifiMsg, setWifiMsg] = useState(''); const [wifiMsg, setWifiMsg] = useState('');
const [mqttConfig, setMqttConfig] = useState({ const [mqttConfig, setMqttConfig] = useState({
@@ -21,45 +18,46 @@ const Connectivity = () => {
}); });
const [mqttMsg, setMqttMsg] = useState(''); const [mqttMsg, setMqttMsg] = useState('');
// Carregar as configurações Wi-Fi e MQTT
useEffect(() => { useEffect(() => {
const load = async () => { const load = async () => {
try {
const conn = await get('/api/v1/connectivity');
setStatus(conn);
} catch {
// ignore errors in demo
}
try { try {
const wifi = await get('/api/v1/config/wifi'); const wifi = await get('/api/v1/config/wifi');
setWifiConfig(wifi); setWifiConfig(wifi); // Atualiza as configurações Wi-Fi
} catch {} } catch (error) {
try { console.error('Erro ao carregar configurações Wi-Fi:', error);
const list = await get('/api/v1/config/wifi/scan'); }
setWifiNetworks(list.networks || []);
} catch {}
try { try {
const mqtt = await get('/api/v1/config/mqtt'); const mqtt = await get('/api/v1/config/mqtt');
setMqttConfig(mqtt); setMqttConfig(mqtt); // Atualiza as configurações MQTT
} catch {} } catch (error) {
setLoading(false); console.error('Erro ao carregar configurações MQTT:', error);
}
setLoading(false); // Finaliza o carregamento
}; };
load(); load();
}, []); }, []);
// Salvar configuração Wi-Fi
const saveWifi = async () => { const saveWifi = async () => {
try { try {
await post('/api/v1/config/wifi', wifiConfig); await post('/api/v1/config/wifi', wifiConfig); // Envia as configurações de Wi-Fi para o servidor
setWifiMsg('Configuração Wi-Fi gravada!'); setWifiMsg('Configuração Wi-Fi gravada!');
} catch { } catch (error) {
setWifiMsg('Erro ao gravar Wi-Fi.'); setWifiMsg('Erro ao gravar Wi-Fi.');
} }
}; };
// Salvar configuração MQTT
const saveMqtt = async () => { const saveMqtt = async () => {
try { try {
await post('/api/v1/config/mqtt', mqttConfig); await post('/api/v1/config/mqtt', mqttConfig); // Envia as configurações de MQTT para o servidor
setMqttMsg('Configuração MQTT gravada!'); setMqttMsg('Configuração MQTT gravada!');
} catch { } catch (error) {
setMqttMsg('Erro ao gravar MQTT.'); setMqttMsg('Erro ao gravar MQTT.');
} }
}; };
@@ -69,19 +67,24 @@ const Connectivity = () => {
{loading ? ( {loading ? (
<p>A carregar...</p> <p>A carregar...</p>
) : ( ) : (
<>
{status && (
<div>
<h2>Status Atual</h2>
<p>Wi-Fi: {status.wifi.status} ({status.wifi.ssid})</p>
<p>MQTT: {status.mqtt.status} - {status.mqtt.broker}:{status.mqtt.port}</p>
</div>
)}
<h2 className="text-xl font-semibold mt-4">Configuração Wi-Fi</h2> <h2 className="text-xl font-semibold mt-4">Configuração Wi-Fi</h2>
{wifiMsg && <div className="p-2 bg-gray-200 rounded mb-2">{wifiMsg}</div>} {wifiMsg && <div className="p-2 bg-gray-200 rounded mb-2">{wifiMsg}</div>}
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); saveWifi(); }}> <form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); saveWifi(); }}>
<div>
<label className="flex items-center gap-2">
Ativar WIFI
<input
type="checkbox"
checked={wifiConfig.enabled}
onChange={e => setMqttConfig({ ...wifiConfig, enabled: e.target.checked })}
/>
</label>
</div>
<div> <div>
<label className="block mb-1" htmlFor="wifi-ssid">SSID:</label> <label className="block mb-1" htmlFor="wifi-ssid">SSID:</label>
<input <input
@@ -93,7 +96,6 @@ const Connectivity = () => {
/> />
</div> </div>
<div> <div>
<label className="block mb-1" htmlFor="wifi-password">Palavra-passe:</label> <label className="block mb-1" htmlFor="wifi-password">Palavra-passe:</label>
<input <input
@@ -178,6 +180,7 @@ const Connectivity = () => {
onChange={e => setMqttConfig({ ...mqttConfig, topic: e.target.value })} onChange={e => setMqttConfig({ ...mqttConfig, topic: e.target.value })}
/> />
</div> </div>
<div> <div>
<button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Guardar</button> <button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Guardar</button>
</div> </div>

View File

@@ -1,20 +1,38 @@
// src/pages/Dashboard.jsx import React, { useState, useEffect } from 'react';
import React from 'react';
const Dashboard = () => { const Dashboard = () => {
// Mock data (substitua pelos dados reais) // Estados para armazenar os dados do dashboard
const mockDashboardData = { const [dashboardData, setDashboardData] = useState({
status: "Ativo", status: "Ativo",
chargers: [ chargers: [
{ id: 1, status: "Ativo", current: 12, power: 2200 },
{ id: 2, status: "Inativo", current: 0, power: 0 },
{ id: 3, status: "Erro", current: 0, power: 0 }, { id: 3, status: "Erro", current: 0, power: 0 },
], ],
energyConsumed: 50.3, energyConsumed: 0,
chargingTime: 120, chargingTime: 0,
alerts: ["Aviso: Carregador 1 está com erro."], alerts: [],
});
// Função para obter os dados do dashboard
const fetchDashboardData = async () => {
try {
const response = await fetch('/api/v1/dashboard');
if (response.ok) {
const data = await response.json();
setDashboardData(data); // Atualiza o estado com os dados recebidos
} else {
alert('Erro ao obter os dados do dashboard');
}
} catch (error) {
console.error('Erro ao buscar dados do dashboard:', error);
alert('Erro de conexão');
}
}; };
// Chamar a função fetchDashboardData quando o componente for montado
useEffect(() => {
fetchDashboardData();
}, []);
return ( return (
<div className="max-w-3xl mx-auto p-5"> <div className="max-w-3xl mx-auto p-5">
<h1 className="text-2xl font-bold mb-5">Visão Geral</h1> <h1 className="text-2xl font-bold mb-5">Visão Geral</h1>
@@ -23,15 +41,15 @@ const Dashboard = () => {
<div className="flex flex-wrap gap-4 mb-6"> <div className="flex flex-wrap gap-4 mb-6">
<div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]"> <div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]">
<h3>Status do Sistema</h3> <h3>Status do Sistema</h3>
<p>{mockDashboardData.status}</p> <p>{dashboardData.status}</p>
</div> </div>
<div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]"> <div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]">
<h3>Consumo de Energia</h3> <h3>Consumo de Energia</h3>
<p>{mockDashboardData.energyConsumed} kWh</p> <p>{dashboardData.energyConsumed} kWh</p>
</div> </div>
<div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]"> <div className="bg-white p-4 rounded shadow flex-1 min-w-[150px]">
<h3>Tempo de Carregamento</h3> <h3>Tempo de Carregamento</h3>
<p>{mockDashboardData.chargingTime} minutos</p> <p>{dashboardData.chargingTime} minutos</p>
</div> </div>
</div> </div>
@@ -39,7 +57,7 @@ const Dashboard = () => {
<div className="mb-6"> <div className="mb-6">
<h2 className="text-xl font-semibold mb-2">Alertas</h2> <h2 className="text-xl font-semibold mb-2">Alertas</h2>
<ul> <ul>
{mockDashboardData.alerts.map((alert, index) => ( {dashboardData.alerts.map((alert, index) => (
<li key={index} className="p-2 bg-red-500 text-white rounded mb-2"> <li key={index} className="p-2 bg-red-500 text-white rounded mb-2">
<span> {alert}</span> <span> {alert}</span>
</li> </li>
@@ -60,7 +78,7 @@ const Dashboard = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{mockDashboardData.chargers.map((charger) => ( {dashboardData.chargers.map((charger) => (
<tr key={charger.id}> <tr key={charger.id}>
<td className="border-b p-2">{charger.id}</td> <td className="border-b p-2">{charger.id}</td>
<td className="border-b p-2">{charger.status}</td> <td className="border-b p-2">{charger.status}</td>

View File

@@ -7,12 +7,13 @@ export default function ElectricalNetwork() {
const [msg, setMsg] = useState(''); const [msg, setMsg] = useState('');
const [error, setError] = useState(''); const [error, setError] = useState('');
const [monitor, setMonitor] = useState({ voltage: '', current: '', quality: '' }); const [monitor, setMonitor] = useState({ voltage: '0', current: '0', quality: '0' });
const [alerts, setAlerts] = useState(false); const [alerts, setAlerts] = useState(false);
const [security, setSecurity] = useState({ earthFault: false, rcm: false }); const [security, setSecurity] = useState({ earthFault: false, rcm: false });
const [loadBalancing, setLoadBalancing] = useState({ enabled: false, currentLimit: 32 }); const [loadBalancing, setLoadBalancing] = useState({ enabled: false, currentLimit: 0 });
const [solar, setSolar] = useState({ capacity: 0, useSolar: false, handleExcess: false }); const [solar, setSolar] = useState({ capacity: 0, useSolar: false, handleExcess: false });
// Função para carregar as configurações do backend
useEffect(() => { useEffect(() => {
const load = async () => { const load = async () => {
try { try {
@@ -22,8 +23,9 @@ export default function ElectricalNetwork() {
if (cfg.security) setSecurity(cfg.security); if (cfg.security) setSecurity(cfg.security);
if (cfg.loadBalancing) setLoadBalancing(cfg.loadBalancing); if (cfg.loadBalancing) setLoadBalancing(cfg.loadBalancing);
if (cfg.solar) setSolar(cfg.solar); if (cfg.solar) setSolar(cfg.solar);
} catch { } catch (err) {
// endpoint opcional console.error('Erro ao carregar configurações:', err);
// Endpoint opcional, sem ações adicionais
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -31,6 +33,7 @@ export default function ElectricalNetwork() {
load(); load();
}, []); }, []);
// Função para salvar as configurações no backend
const save = async () => { const save = async () => {
setMsg(''); setMsg('');
setError(''); setError('');
@@ -38,7 +41,8 @@ export default function ElectricalNetwork() {
const body = { monitor, alerts, security, loadBalancing, solar }; const body = { monitor, alerts, security, loadBalancing, solar };
await post('/api/v1/config/electrical', body); await post('/api/v1/config/electrical', body);
setMsg('Configuração gravada com sucesso!'); setMsg('Configuração gravada com sucesso!');
} catch { } catch (err) {
console.error('Erro ao salvar configuração:', err);
setError('Erro ao gravar configuração.'); setError('Erro ao gravar configuração.');
} }
}; };
@@ -53,33 +57,37 @@ export default function ElectricalNetwork() {
) : ( ) : (
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); save(); }}> <form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); save(); }}>
<h2 className="text-xl font-semibold">Monitoramento da Rede Elétrica</h2> <h2 className="text-xl font-semibold">Monitoramento da Rede Elétrica</h2>
<div> <div>
<label className="block mb-1">Tensão de Entrada (V):</label> <label className="block mb-1">Tensão de Entrada (V):</label>
<input <input
type="number" type="number"
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 w-full"
value={monitor.voltage} value={monitor.voltage}
onChange={e => setMonitor({ ...monitor, voltage: e.target.value })} disabled
/> />
</div> </div>
<div> <div>
<label className="block mb-1">Corrente de Entrada (A):</label> <label className="block mb-1">Corrente de Entrada (A):</label>
<input <input
type="number" type="text"
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 w-full"
value={monitor.current} value={monitor.current}
onChange={e => setMonitor({ ...monitor, current: e.target.value })} disabled
/> />
</div> </div>
<div> <div>
<label className="block mb-1">Qualidade de Energia:</label> <label className="block mb-1">Qualidade de Energia:</label>
<input <input
type="text" type="text"
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 w-full"
value={monitor.quality} value={monitor.quality}
onChange={e => setMonitor({ ...monitor, quality: e.target.value })} disabled
/> />
</div> </div>
<div> <div>
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
Alertas de Falha na Rede Alertas de Falha na Rede
@@ -124,6 +132,7 @@ export default function ElectricalNetwork() {
/> />
</label> </label>
</div> </div>
<div> <div>
<label className="block mb-1" htmlFor="lb-current">Limite de Corrente (A):</label> <label className="block mb-1" htmlFor="lb-current">Limite de Corrente (A):</label>
<input <input
@@ -166,12 +175,14 @@ export default function ElectricalNetwork() {
/> />
</label> </label>
</div> </div>
<div> <div>
<button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Guardar</button> <button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">
Guardar
</button>
</div> </div>
</form> </form>
)} )}
</PageLayout> </PageLayout>
); );
} }

View File

@@ -65,21 +65,35 @@ const Security = () => {
</div> </div>
</div> </div>
{/* Usuários */} {/* Utilizador */}
<div className="mb-5"> <div className="overflow-x-auto mb-5">
<h2 className="text-xl font-semibold mb-2">Usuários</h2> <h2 className="text-xl font-semibold mb-4">Utilizador</h2>
<ul className="mb-3 space-y-2"> <table className="min-w-full border border-gray-300 text-left table-auto">
{users.map((user, index) => ( <thead className="bg-gray-100">
<li key={index} className="flex items-center justify-between"> <tr>
<span>{user.username} - {user.role}</span> <th className="border-b p-2 text-sm font-medium text-gray-700">Nome de Usuário</th>
<button className="text-red-600" onClick={() => removeUser(user.username)}>Remover</button> <th className="border-b p-2 text-sm font-medium text-gray-700">Ações</th>
</li> </tr>
))} </thead>
</ul> <tbody>
<div> {users.map((user, index) => (
<button className="bg-green-600 text-white px-4 py-2 rounded" onClick={() => addUser('newuser', 'User')}>Adicionar Novo Usuário</button> <tr key={index} className="hover:bg-gray-50">
</div> <td className="border-b p-2 text-sm">{user.username}</td>
</div> <td className="border-b p-2 text-sm text-red-600">
<button onClick={() => removeUser(user.username)}>Remover</button>
</td>
</tr>
))}
</tbody>
</table>
<div className="mt-4">
<button className="bg-green-600 text-white px-4 py-2 rounded" onClick={() => addUser('newuser', 'User')}>Adicionar Novo Usuário</button>
</div>
</div>
</div> </div>
); );
}; };

View File

@@ -1,5 +1,4 @@
// src/pages/Settings.jsx import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
const Settings = () => { const Settings = () => {
// Estados para armazenar os valores dos sliders e caixas de entrada // Estados para armazenar os valores dos sliders e caixas de entrada
@@ -9,85 +8,104 @@ const Settings = () => {
const [chargingTimeLimit, setChargingTimeLimit] = useState(0); const [chargingTimeLimit, setChargingTimeLimit] = useState(0);
const [temperatureLimit, setTemperatureLimit] = useState(60); const [temperatureLimit, setTemperatureLimit] = useState(60);
// Função para preencher os campos com os dados do servidor
const fetchSettings = async () => {
const response = await fetch('/api/v1/config/settings');
if (response.ok) {
const data = await response.json();
setCurrentLimit(data.currentLimit);
setPowerLimit(data.powerLimit);
setEnergyLimit(data.energyLimit);
setChargingTimeLimit(data.chargingTimeLimit);
setTemperatureLimit(data.temperatureLimit);
} else {
alert('Erro ao obter as configurações');
}
};
// Carregar as configurações ao montar o componente
useEffect(() => {
fetchSettings();
}, []);
const handleCurrentLimitChange = (e) => setCurrentLimit(e.target.value); const handleCurrentLimitChange = (e) => setCurrentLimit(e.target.value);
const handlePowerLimitChange = (e) => setPowerLimit(e.target.value); const handlePowerLimitChange = (e) => setPowerLimit(e.target.value);
const handleEnergyLimitChange = (e) => setEnergyLimit(e.target.value); const handleEnergyLimitChange = (e) => setEnergyLimit(e.target.value);
const handleChargingTimeLimitChange = (e) => setChargingTimeLimit(e.target.value); const handleChargingTimeLimitChange = (e) => setChargingTimeLimit(e.target.value);
const handleTemperatureLimitChange = (e) => setTemperatureLimit(e.target.value); const handleTemperatureLimitChange = (e) => setTemperatureLimit(e.target.value);
const handleSubmit = async () => {
const settingsData = {
currentLimit,
powerLimit,
energyLimit,
chargingTimeLimit,
temperatureLimit,
};
const response = await fetch('/api/v1/config/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(settingsData),
});
if (response.ok) {
alert('Configurações de energia atualizadas com sucesso!');
} else {
alert('Erro ao atualizar configurações');
}
};
return ( return (
<div className="max-w-xl mx-auto p-5 bg-white rounded shadow"> <div>
<h1 className="text-2xl font-bold mb-5 text-center">Configurações Gerais</h1> <label>
Current Limit:
<div className="mb-5"> <input
<label className="block mb-2">Corrente Máxima de Carregamento (A):</label> type="number"
<div className="flex items-center justify-between"> value={currentLimit}
<input onChange={handleCurrentLimitChange}
type="range" />
min="5" </label>
max="32" <br />
className="flex-1" <label>
value={currentLimit} Power Limit:
onChange={handleCurrentLimitChange} <input
/> type="number"
<span>{currentLimit} A</span> value={powerLimit}
</div> onChange={handlePowerLimitChange}
</div> />
</label>
<div className="mb-5"> <br />
<label className="block mb-2">Limite de Potência Máxima (W):</label> <label>
<div className="flex items-center justify-between"> Energy Limit:
<input
type="range"
min="1000"
max="10000"
className="flex-1"
value={powerLimit}
onChange={handlePowerLimitChange}
/>
<span>{powerLimit} W</span>
</div>
</div>
<div className="mb-5">
<label className="block mb-2">Limite de Consumo Total de Energia (kWh):</label>
<input <input
type="number" type="number"
min="0"
className="w-full p-2 border rounded"
value={energyLimit} value={energyLimit}
onChange={handleEnergyLimitChange} onChange={handleEnergyLimitChange}
/> />
</div> </label>
<br />
<div className="mb-5"> <label>
<label className="block mb-2">Limite de Tempo de Carregamento (h):</label> Charging Time Limit:
<input <input
type="number" type="number"
min="1"
max="24"
className="w-full p-2 border rounded"
value={chargingTimeLimit} value={chargingTimeLimit}
onChange={handleChargingTimeLimitChange} onChange={handleChargingTimeLimitChange}
/> />
</div> </label>
<br />
<div className="mb-5"> <label>
<label className="block mb-2">Limite de Temperatura Máxima do EVSE (ºC):</label> Temperature Limit:
<div className="flex items-center justify-between"> <input
<input type="number"
type="range" value={temperatureLimit}
min="60" onChange={handleTemperatureLimitChange}
max="80" />
className="flex-1" </label>
value={temperatureLimit} <br />
onChange={handleTemperatureLimitChange} <button onClick={handleSubmit}>Save Settings</button>
/>
<span>{temperatureLimit} ºC</span>
</div>
</div>
<button className="bg-green-600 text-white w-full mt-4 p-2 rounded hover:bg-green-700">Salvar Configurações</button>
</div> </div>
); );
}; };

11
tailwind.config.js Executable file
View File

@@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

View File

@@ -7,10 +7,16 @@ export default defineConfig({
port: 5173, // ou outro, se necessário port: 5173, // ou outro, se necessário
proxy: { proxy: {
'/api': { '/api': {
target: 'http://127.0.0.1:8080', target: 'http://127.0.0.1:8080', // Alvo para redirecionar as requisições
changeOrigin: true, changeOrigin: true, // Garante que a origem seja alterada para o alvo do proxy
secure: false secure: false // Desabilita a validação de SSL (útil para desenvolvimento em HTTP)
} }
} }
},
build: {
outDir: 'dist', // Defina o diretório de saída (padrão é 'dist')
minify: 'esbuild', // Usa o esbuild para minimizar o código para produção
sourcemap: false, // Desativa a criação de mapas de fonte (se não for necessário)
// Outras opções de otimização de produção podem ser configuradas aqui, se necessário
} }
}); });