Merge pull request #6 from PlxEV/codex/refatorar-projeto-para-usar-tailwind-css
Use Tailwind CSS
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
<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">
|
||||||
<link rel="stylesheet" href="/src/index.css" />
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<title>Vite + React</title>
|
<title>Vite + React</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -10,20 +10,19 @@ const Navbar = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar">
|
<nav className="bg-gradient-to-r from-green-700 to-green-600 text-white p-4 shadow">
|
||||||
<div className="navbar-container">
|
<div className="flex items-center justify-between">
|
||||||
<div className="navbar-logo">
|
<div className="font-bold text-xl">EVSE</div>
|
||||||
</div>
|
<ul className={`flex-col md:flex-row md:flex gap-4 ${isMenuOpen ? 'flex' : 'hidden'} md:!flex`}>
|
||||||
<ul className={`navbar-links ${isMenuOpen ? 'active' : ''}`}>
|
<li><Link className="hover:underline" to="/dashboard">Dashboard</Link></li>
|
||||||
<li><Link to="/dashboard">Dashboard</Link></li>
|
<li><Link className="hover:underline" to="/settings">Settings</Link></li>
|
||||||
<li><Link to="/settings">Settings</Link></li>
|
<li><Link className="hover:underline" to="/security">Security</Link></li>
|
||||||
<li><Link to="/security">Security</Link></li>
|
<li><Link className="hover:underline" to="/connectivity">Connectivity</Link></li>
|
||||||
<li><Link to="/connectivity">Connectivity</Link></li>
|
<li><Link className="hover:underline" to="/ocpp">OCPP</Link></li>
|
||||||
<li><Link to="/ocpp">OCPP</Link></li>
|
<li><Link className="hover:underline" to="/electrical-network">Rede Elétrica</Link></li>
|
||||||
<li><Link to="/electrical-network">Rede Elétrica</Link></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<button className="menu-icon" onClick={toggleMenu}>
|
<button className="md:hidden text-3xl" onClick={toggleMenu}>
|
||||||
☰ {/* Ícone do menu hamburguer */}
|
☰
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// src/components/PageLayout.jsx
|
// src/components/PageLayout.jsx
|
||||||
export default function PageLayout({ title, children }) {
|
export default function PageLayout({ title, children }) {
|
||||||
return (
|
return (
|
||||||
<div className="page-container">
|
<div className="max-w-3xl mx-auto p-5 bg-white rounded shadow">
|
||||||
<h1>{title}</h1>
|
<h1 className="text-2xl font-bold mb-5">{title}</h1>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
676
src/index.css
676
src/index.css
@@ -1,676 +0,0 @@
|
|||||||
/* index.css */
|
|
||||||
|
|
||||||
/* Paleta de cores e variáveis globais */
|
|
||||||
:root {
|
|
||||||
--color-primary: #4CAF50;
|
|
||||||
--color-primary-dark: #45a049;
|
|
||||||
--color-dark: #333333;
|
|
||||||
--color-light: #ffffff;
|
|
||||||
--color-background: #f4f4f9;
|
|
||||||
--color-background-dark: #e8e8ef;
|
|
||||||
--color-surface: #ffffff;
|
|
||||||
--color-table-header: #f2f2f2;
|
|
||||||
--color-border: #dddddd;
|
|
||||||
--color-info: #2196F3;
|
|
||||||
--color-info-dark: #1e88e5;
|
|
||||||
--color-alert: #f44336;
|
|
||||||
--color-alert-dark: #e53935;
|
|
||||||
--color-text-muted: #555555;
|
|
||||||
--font-base: 'Roboto', Arial, sans-serif;
|
|
||||||
--border-radius: 5px;
|
|
||||||
--transition-fast: 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resetando margens e padding */
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fonte padrão para o projeto */
|
|
||||||
body {
|
|
||||||
font-family: var(--font-base);
|
|
||||||
background: linear-gradient(var(--color-background), var(--color-background-dark));
|
|
||||||
color: var(--color-dark);
|
|
||||||
line-height: 1.6;
|
|
||||||
font-size: 16px; /* Tamanho de fonte confortável */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Definindo um fundo geral para o layout */
|
|
||||||
.container {
|
|
||||||
width: 90%;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos básicos de links */
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit; /* Cor do link será herdada do texto */
|
|
||||||
transition: color var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos para os títulos */
|
|
||||||
h1, h2, h3 {
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo básico de botões */
|
|
||||||
button {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: var(--color-light);
|
|
||||||
border: none;
|
|
||||||
padding: 10px 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
transition: background-color var(--transition-fast), transform var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: var(--color-primary-dark);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tabelas */
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background-color: var(--color-table-header);
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo de caixas de alerta */
|
|
||||||
.alert {
|
|
||||||
background-color: var(--color-alert);
|
|
||||||
color: white;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos para o Dashboard */
|
|
||||||
.dashboard-container {
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Estilos para Settings */
|
|
||||||
.settings-container {
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item label {
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item input[type="range"] {
|
|
||||||
width: 100%;
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item input[type="number"] {
|
|
||||||
width: 100px;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-left: 10px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo do container do slider */
|
|
||||||
.slider-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-container span {
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Botão de salvar */
|
|
||||||
button.save-button {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 10px 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.save-button:hover {
|
|
||||||
background-color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsividade para telas pequenas */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.settings-container {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-title {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-item input[type="number"] {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.save-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo para o Título */
|
|
||||||
.settings-title {
|
|
||||||
font-size: 32px; /* Tamanho maior para maior destaque */
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center; /* Centralizado */
|
|
||||||
color: var(--color-dark);
|
|
||||||
text-transform: uppercase; /* Texto em maiúsculo para chamar atenção */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ajustando os parâmetros do título no mobile */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.settings-title {
|
|
||||||
font-size: 28px;
|
|
||||||
margin-bottom: 20px; /* Menor espaçamento em telas pequenas */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navbar Estilo */
|
|
||||||
.navbar {
|
|
||||||
background: linear-gradient(90deg, var(--color-primary-dark), var(--color-primary));
|
|
||||||
color: var(--color-light);
|
|
||||||
padding: 15px 20px; /* Mais espaçamento para uma navegação mais confortável */
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-logo h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links li {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links a {
|
|
||||||
color: var(--color-light);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 18px;
|
|
||||||
padding: 8px 15px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
transition: background-color var(--transition-fast), color var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links a:hover {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: var(--color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links a.active {
|
|
||||||
background-color: var(--color-primary-dark);
|
|
||||||
color: var(--color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ícone do menu hamburguer para telas pequenas */
|
|
||||||
.menu-icon {
|
|
||||||
display: none;
|
|
||||||
font-size: 30px;
|
|
||||||
color: white;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsividade para telas pequenas */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.navbar-links {
|
|
||||||
display: none; /* Inicialmente oculta os links */
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links.active {
|
|
||||||
display: flex; /* Exibe os links quando o menu estiver ativo */
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links li {
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-links a {
|
|
||||||
padding: 10px 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exibe o ícone do menu hamburguer */
|
|
||||||
.menu-icon {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos para a Página de Segurança */
|
|
||||||
.security-container {
|
|
||||||
background-color: var(--color-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-title {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item label {
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item input[type="checkbox"] {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item ul {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item ul li {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item select {
|
|
||||||
margin-left: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 10px 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Estilos para a Página de Segurança */
|
|
||||||
.security-container {
|
|
||||||
background-color: var(--color-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-title {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item label {
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item input[type="checkbox"] {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-methods label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item select {
|
|
||||||
margin-left: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 10px 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-user button {
|
|
||||||
background-color: var(--color-info); /* Cor azul para adicionar */
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-user button:hover {
|
|
||||||
background-color: var(--color-info-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilos para a lista de usuários */
|
|
||||||
.security-item ul {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item ul li {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item button {
|
|
||||||
background-color: var(--color-alert); /* Cor vermelha para remover */
|
|
||||||
padding: 5px 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.security-item button:hover {
|
|
||||||
background-color: var(--color-alert-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Estilos para o Dashboard */
|
|
||||||
.dashboard-container {
|
|
||||||
background-color: var(--color-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 20px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-title {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-summary {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
width: 30%;
|
|
||||||
text-align: center;
|
|
||||||
transition: transform var(--transition-fast), box-shadow var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h3 {
|
|
||||||
font-size: 20px;
|
|
||||||
color: var(--color-dark);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card p {
|
|
||||||
font-size: 18px;
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerts {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerts h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-item {
|
|
||||||
font-size: 16px;
|
|
||||||
background-color: var(--color-alert);
|
|
||||||
color: white;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chargers-table {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chargers-table h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background-color: var(--color-table-header);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.dashboard-summary {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Layout comum para páginas */
|
|
||||||
.page-container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Formulários */
|
|
||||||
.form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input,
|
|
||||||
.form-group select,
|
|
||||||
.form-group textarea {
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
width: 100%;
|
|
||||||
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
|
||||||
}
|
|
||||||
.form-group input:focus,
|
|
||||||
.form-group select:focus,
|
|
||||||
.form-group textarea:focus {
|
|
||||||
border-color: var(--color-primary);
|
|
||||||
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Alinhamento de texto e checkbox */
|
|
||||||
.checkbox-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label input[type="checkbox"] {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
accent-color: var(--color-primary);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-grid {
|
|
||||||
margin-top: 10px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mensagens de feedback */
|
|
||||||
.message {
|
|
||||||
padding: 10px 15px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
background-color: var(--color-table-header);
|
|
||||||
color: var(--color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.success {
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: var(--color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.error {
|
|
||||||
background-color: var(--color-alert);
|
|
||||||
color: var(--color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caixa de log */
|
|
||||||
.log-box {
|
|
||||||
background-color: var(--color-surface);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
max-height: 400px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
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'; // Importe o CSS unificado aqui
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@@ -78,41 +78,43 @@ const Connectivity = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h2>Configuração Wi-Fi</h2>
|
<h2 className="text-xl font-semibold mt-4">Configuração Wi-Fi</h2>
|
||||||
{wifiMsg && <div className="message">{wifiMsg}</div>}
|
{wifiMsg && <div className="p-2 bg-gray-200 rounded mb-2">{wifiMsg}</div>}
|
||||||
<form className="form" onSubmit={e => { e.preventDefault(); saveWifi(); }}>
|
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); saveWifi(); }}>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="wifi-ssid">SSID:</label>
|
<label className="block mb-1" htmlFor="wifi-ssid">SSID:</label>
|
||||||
<input
|
<input
|
||||||
id="wifi-ssid"
|
id="wifi-ssid"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={wifiConfig.ssid}
|
value={wifiConfig.ssid}
|
||||||
onChange={e => setWifiConfig({ ...wifiConfig, ssid: e.target.value })}
|
onChange={e => setWifiConfig({ ...wifiConfig, ssid: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="wifi-password">Palavra-passe:</label>
|
<label className="block mb-1" htmlFor="wifi-password">Palavra-passe:</label>
|
||||||
<input
|
<input
|
||||||
id="wifi-password"
|
id="wifi-password"
|
||||||
type="password"
|
type="password"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={wifiConfig.password}
|
value={wifiConfig.password}
|
||||||
onChange={e => setWifiConfig({ ...wifiConfig, password: e.target.value })}
|
onChange={e => setWifiConfig({ ...wifiConfig, password: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div>
|
||||||
<button 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>
|
||||||
|
|
||||||
<h2>Configuração MQTT</h2>
|
<h2 className="text-xl font-semibold mt-6">Configuração MQTT</h2>
|
||||||
{mqttMsg && <div className="message">{mqttMsg}</div>}
|
{mqttMsg && <div className="p-2 bg-gray-200 rounded mb-2">{mqttMsg}</div>}
|
||||||
<form className="form" onSubmit={e => { e.preventDefault(); saveMqtt(); }}>
|
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); saveMqtt(); }}>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Ativar MQTT
|
Ativar MQTT
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -122,58 +124,62 @@ const Connectivity = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="mqtt-host">Host:</label>
|
<label className="block mb-1" htmlFor="mqtt-host">Host:</label>
|
||||||
<input
|
<input
|
||||||
id="mqtt-host"
|
id="mqtt-host"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={mqttConfig.host}
|
value={mqttConfig.host}
|
||||||
onChange={e => setMqttConfig({ ...mqttConfig, host: e.target.value })}
|
onChange={e => setMqttConfig({ ...mqttConfig, host: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="mqtt-port">Porta:</label>
|
<label className="block mb-1" htmlFor="mqtt-port">Porta:</label>
|
||||||
<input
|
<input
|
||||||
id="mqtt-port"
|
id="mqtt-port"
|
||||||
type="number"
|
type="number"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={mqttConfig.port}
|
value={mqttConfig.port}
|
||||||
onChange={e => setMqttConfig({ ...mqttConfig, port: parseInt(e.target.value || 0) })}
|
onChange={e => setMqttConfig({ ...mqttConfig, port: parseInt(e.target.value || 0) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="mqtt-username">Utilizador:</label>
|
<label className="block mb-1" htmlFor="mqtt-username">Utilizador:</label>
|
||||||
<input
|
<input
|
||||||
id="mqtt-username"
|
id="mqtt-username"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={mqttConfig.username}
|
value={mqttConfig.username}
|
||||||
onChange={e => setMqttConfig({ ...mqttConfig, username: e.target.value })}
|
onChange={e => setMqttConfig({ ...mqttConfig, username: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="mqtt-password">Palavra-passe:</label>
|
<label className="block mb-1" htmlFor="mqtt-password">Palavra-passe:</label>
|
||||||
<input
|
<input
|
||||||
id="mqtt-password"
|
id="mqtt-password"
|
||||||
type="password"
|
type="password"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={mqttConfig.password}
|
value={mqttConfig.password}
|
||||||
onChange={e => setMqttConfig({ ...mqttConfig, password: e.target.value })}
|
onChange={e => setMqttConfig({ ...mqttConfig, password: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="mqtt-topic">Tópico:</label>
|
<label className="block mb-1" htmlFor="mqtt-topic">Tópico:</label>
|
||||||
<input
|
<input
|
||||||
id="mqtt-topic"
|
id="mqtt-topic"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={mqttConfig.topic}
|
value={mqttConfig.topic}
|
||||||
onChange={e => setMqttConfig({ ...mqttConfig, topic: e.target.value })}
|
onChange={e => setMqttConfig({ ...mqttConfig, topic: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div className="button-grid">
|
<button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Guardar</button>
|
||||||
<button type="submit">Guardar</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -16,31 +16,31 @@ const Dashboard = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-container">
|
<div className="max-w-3xl mx-auto p-5">
|
||||||
<h1 className="dashboard-title">Visão Geral</h1>
|
<h1 className="text-2xl font-bold mb-5">Visão Geral</h1>
|
||||||
|
|
||||||
{/* Cards com informações resumidas */}
|
{/* Cards com informações resumidas */}
|
||||||
<div className="dashboard-summary">
|
<div className="flex flex-wrap gap-4 mb-6">
|
||||||
<div className="card">
|
<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>{mockDashboardData.status}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<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>{mockDashboardData.energyConsumed} kWh</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<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>{mockDashboardData.chargingTime} minutos</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Indicadores de falhas ou alertas */}
|
{/* Indicadores de falhas ou alertas */}
|
||||||
<div className="alerts">
|
<div className="mb-6">
|
||||||
<h2>Alertas</h2>
|
<h2 className="text-xl font-semibold mb-2">Alertas</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{mockDashboardData.alerts.map((alert, index) => (
|
{mockDashboardData.alerts.map((alert, index) => (
|
||||||
<li key={index} className="alert-item">
|
<li key={index} className="p-2 bg-red-500 text-white rounded mb-2">
|
||||||
<span>⚠️ {alert}</span>
|
<span>⚠️ {alert}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -48,24 +48,24 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tabela de Carregadores */}
|
{/* Tabela de Carregadores */}
|
||||||
<div className="chargers-table">
|
<div>
|
||||||
<h2>Carregadores</h2>
|
<h2 className="text-xl font-semibold mb-2">Carregadores</h2>
|
||||||
<table>
|
<table className="min-w-full border border-gray-300 text-left">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th className="border-b p-2">ID</th>
|
||||||
<th>Status</th>
|
<th className="border-b p-2">Status</th>
|
||||||
<th>Corrente (A)</th>
|
<th className="border-b p-2">Corrente (A)</th>
|
||||||
<th>Potência (W)</th>
|
<th className="border-b p-2">Potência (W)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{mockDashboardData.chargers.map((charger) => (
|
{mockDashboardData.chargers.map((charger) => (
|
||||||
<tr key={charger.id}>
|
<tr key={charger.id}>
|
||||||
<td>{charger.id}</td>
|
<td className="border-b p-2">{charger.id}</td>
|
||||||
<td>{charger.status}</td>
|
<td className="border-b p-2">{charger.status}</td>
|
||||||
<td>{charger.current}</td>
|
<td className="border-b p-2">{charger.current}</td>
|
||||||
<td>{charger.power}</td>
|
<td className="border-b p-2">{charger.power}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -45,40 +45,43 @@ export default function ElectricalNetwork() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Rede Elétrica">
|
<PageLayout title="Rede Elétrica">
|
||||||
{msg && <div className="message success">{msg}</div>}
|
{msg && <div className="p-2 mb-2 bg-green-600 text-white rounded">{msg}</div>}
|
||||||
{error && <div className="message error">{error}</div>}
|
{error && <div className="p-2 mb-2 bg-red-600 text-white rounded">{error}</div>}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p>A carregar...</p>
|
<p>A carregar...</p>
|
||||||
) : (
|
) : (
|
||||||
<form className="form" onSubmit={e => { e.preventDefault(); save(); }}>
|
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); save(); }}>
|
||||||
<h2>Monitoramento da Rede Elétrica</h2>
|
<h2 className="text-xl font-semibold">Monitoramento da Rede Elétrica</h2>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label>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"
|
||||||
value={monitor.voltage}
|
value={monitor.voltage}
|
||||||
onChange={e => setMonitor({ ...monitor, voltage: e.target.value })}
|
onChange={e => setMonitor({ ...monitor, voltage: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label>Corrente de Entrada (A):</label>
|
<label className="block mb-1">Corrente de Entrada (A):</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
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 })}
|
onChange={e => setMonitor({ ...monitor, current: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label>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"
|
||||||
value={monitor.quality}
|
value={monitor.quality}
|
||||||
onChange={e => setMonitor({ ...monitor, quality: e.target.value })}
|
onChange={e => setMonitor({ ...monitor, quality: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Alertas de Falha na Rede
|
Alertas de Falha na Rede
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -88,9 +91,9 @@ export default function ElectricalNetwork() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Proteção de Segurança Elétrica</h2>
|
<h2 className="text-xl font-semibold">Proteção de Segurança Elétrica</h2>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Detecção de Falha de Aterramento
|
Detecção de Falha de Aterramento
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -99,8 +102,8 @@ export default function ElectricalNetwork() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Proteção RCM
|
Proteção RCM
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -110,9 +113,9 @@ export default function ElectricalNetwork() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Balanceamento de Carga</h2>
|
<h2 className="text-xl font-semibold">Balanceamento de Carga</h2>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Habilitar Balanceamento de Carga
|
Habilitar Balanceamento de Carga
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -121,28 +124,30 @@ export default function ElectricalNetwork() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="lb-current">Limite de Corrente (A):</label>
|
<label className="block mb-1" htmlFor="lb-current">Limite de Corrente (A):</label>
|
||||||
<input
|
<input
|
||||||
id="lb-current"
|
id="lb-current"
|
||||||
type="number"
|
type="number"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={loadBalancing.currentLimit}
|
value={loadBalancing.currentLimit}
|
||||||
onChange={e => setLoadBalancing({ ...loadBalancing, currentLimit: e.target.value })}
|
onChange={e => setLoadBalancing({ ...loadBalancing, currentLimit: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Energia Solar</h2>
|
<h2 className="text-xl font-semibold">Energia Solar</h2>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="solar-capacity">Capacidade Solar (kW):</label>
|
<label className="block mb-1" htmlFor="solar-capacity">Capacidade Solar (kW):</label>
|
||||||
<input
|
<input
|
||||||
id="solar-capacity"
|
id="solar-capacity"
|
||||||
type="number"
|
type="number"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={solar.capacity}
|
value={solar.capacity}
|
||||||
onChange={e => setSolar({ ...solar, capacity: e.target.value })}
|
onChange={e => setSolar({ ...solar, capacity: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Direcionar Energia Solar para o EVSE
|
Direcionar Energia Solar para o EVSE
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -151,8 +156,8 @@ export default function ElectricalNetwork() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Gerenciamento de Excesso de Energia Solar
|
Gerenciamento de Excesso de Energia Solar
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -161,9 +166,8 @@ export default function ElectricalNetwork() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div className="button-grid">
|
<button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Guardar</button>
|
||||||
<button type="submit">Guardar</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ export default function LoadBalancing() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Configuração de Load Balancing">
|
<PageLayout title="Configuração de Load Balancing">
|
||||||
{msg && <div className="message success">{msg}</div>}
|
{msg && <div className="p-2 mb-2 bg-green-600 text-white rounded">{msg}</div>}
|
||||||
{error && <div className="message error">{error}</div>}
|
{error && <div className="p-2 mb-2 bg-red-600 text-white rounded">{error}</div>}
|
||||||
|
|
||||||
<form className="form" onSubmit={saveConfig}>
|
<form className="flex flex-col gap-4" onSubmit={saveConfig}>
|
||||||
{/* Controle de Ativação/Desativação */}
|
{/* Controle de Ativação/Desativação */}
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Ativar Load Balancing
|
Ativar Load Balancing
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -85,8 +85,8 @@ export default function LoadBalancing() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Configuração de Corrente Máxima */}
|
{/* Configuração de Corrente Máxima */}
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="maxChargingCurrent">
|
<label className="block mb-1" htmlFor="maxChargingCurrent">
|
||||||
Corrente Máxima para Balanceamento (A):
|
Corrente Máxima para Balanceamento (A):
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -95,16 +95,17 @@ export default function LoadBalancing() {
|
|||||||
value={maxChargingCurrent}
|
value={maxChargingCurrent}
|
||||||
min="1"
|
min="1"
|
||||||
max="32"
|
max="32"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
onChange={(e) => setMaxChargingCurrent(e.target.value)}
|
onChange={(e) => setMaxChargingCurrent(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Seleção de Dispositivos */}
|
{/* Seleção de Dispositivos */}
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label>Dispositivos a Balancear:</label>
|
<label className="block mb-1">Dispositivos a Balancear:</label>
|
||||||
{devices.map((device) => (
|
{devices.map((device) => (
|
||||||
<div key={device.id}>
|
<div key={device.id}>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
{device.name}
|
{device.name}
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -117,8 +118,8 @@ export default function LoadBalancing() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div>
|
||||||
<button type="submit">Salvar Configuração</button>
|
<button className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Salvar Configuração</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|||||||
@@ -18,31 +18,33 @@ export default function Login({ setAuthData }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Início de Sessão">
|
<PageLayout title="Início de Sessão">
|
||||||
{error && <div className="message error">{error}</div>}
|
{error && <div className="p-2 mb-4 bg-red-500 text-white rounded">{error}</div>}
|
||||||
|
|
||||||
<form className="form" onSubmit={submit}>
|
<form className="flex flex-col gap-4" onSubmit={submit}>
|
||||||
<div className="form-group">
|
<div className="mb-4">
|
||||||
<label htmlFor="user">Utilizador:</label>
|
<label className="block mb-1" htmlFor="user">Utilizador:</label>
|
||||||
<input
|
<input
|
||||||
id="user"
|
id="user"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={user}
|
value={user}
|
||||||
onChange={e => setUser(e.target.value)}
|
onChange={e => setUser(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="mb-4">
|
||||||
<label htmlFor="pass">Palavra-passe:</label>
|
<label className="block mb-1" htmlFor="pass">Palavra-passe:</label>
|
||||||
<input
|
<input
|
||||||
id="pass"
|
id="pass"
|
||||||
type="password"
|
type="password"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={pass}
|
value={pass}
|
||||||
onChange={e => setPass(e.target.value)}
|
onChange={e => setPass(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div className="mt-4">
|
||||||
<button type="submit">Entrar</button>
|
<button className="bg-green-600 text-white px-4 py-2 rounded w-full hover:bg-green-700" type="submit">Entrar</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ export default function Logs() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Registos do Sistema">
|
<PageLayout title="Registos do Sistema">
|
||||||
{error && <div className="message error">{error}</div>}
|
{error && <div className="p-2 mb-2 bg-red-600 text-white rounded">{error}</div>}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p>A carregar...</p>
|
<p>A carregar...</p>
|
||||||
) : (
|
) : (
|
||||||
<pre className="log-box">{logs || 'Sem dados.'}</pre>
|
<pre className="bg-white border border-gray-300 p-3 rounded max-h-96 overflow-auto">{logs || 'Sem dados.'}</pre>
|
||||||
)}
|
)}
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ export default function Mqtt() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Configuração MQTT">
|
<PageLayout title="Configuração MQTT">
|
||||||
{msg && <div className="message success">{msg}</div>}
|
{msg && <div className="p-2 mb-2 bg-green-600 text-white rounded">{msg}</div>}
|
||||||
{error && <div className="message error">{error}</div>}
|
{error && <div className="p-2 mb-2 bg-red-600 text-white rounded">{error}</div>}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p>A carregar...</p>
|
<p>A carregar...</p>
|
||||||
) : (
|
) : (
|
||||||
<form className="form" onSubmit={e => { e.preventDefault(); save(); }}>
|
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); save(); }}>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Ativar MQTT
|
Ativar MQTT
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -54,58 +54,63 @@ export default function Mqtt() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="host">Host:</label>
|
<label className="block mb-1" htmlFor="host">Host:</label>
|
||||||
<input
|
<input
|
||||||
id="host"
|
id="host"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.host}
|
value={config.host}
|
||||||
onChange={e => setConfig({ ...config, host: e.target.value })}
|
onChange={e => setConfig({ ...config, host: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="port">Porta:</label>
|
<label className="block mb-1" htmlFor="port">Porta:</label>
|
||||||
<input
|
<input
|
||||||
id="port"
|
id="port"
|
||||||
type="number"
|
type="number"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.port}
|
value={config.port}
|
||||||
onChange={e => setConfig({ ...config, port: parseInt(e.target.value || 0) })}
|
onChange={e => setConfig({ ...config, port: parseInt(e.target.value || 0) })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="username">Utilizador:</label>
|
<label className="block mb-1" htmlFor="username">Utilizador:</label>
|
||||||
<input
|
<input
|
||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.username}
|
value={config.username}
|
||||||
onChange={e => setConfig({ ...config, username: e.target.value })}
|
onChange={e => setConfig({ ...config, username: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="password">Palavra-passe:</label>
|
<label className="block mb-1" htmlFor="password">Palavra-passe:</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.password}
|
value={config.password}
|
||||||
onChange={e => setConfig({ ...config, password: e.target.value })}
|
onChange={e => setConfig({ ...config, password: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="topic">Tópico:</label>
|
<label className="block mb-1" htmlFor="topic">Tópico:</label>
|
||||||
<input
|
<input
|
||||||
id="topic"
|
id="topic"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.topic}
|
value={config.topic}
|
||||||
onChange={e => setConfig({ ...config, topic: e.target.value })}
|
onChange={e => setConfig({ ...config, topic: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div>
|
||||||
<button 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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -55,48 +55,52 @@ const OCPP = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{msg && <div className="message">{msg}</div>}
|
{msg && <div className="p-2 mb-2 bg-gray-200 rounded">{msg}</div>}
|
||||||
<form className="form" onSubmit={e => { e.preventDefault(); save(); }}>
|
<form className="flex flex-col gap-4" onSubmit={e => { e.preventDefault(); save(); }}>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="ocpp-url">Servidor:</label>
|
<label className="block mb-1" htmlFor="ocpp-url">Servidor:</label>
|
||||||
<input
|
<input
|
||||||
id="ocpp-url"
|
id="ocpp-url"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.url}
|
value={config.url}
|
||||||
onChange={e => setConfig({ ...config, url: e.target.value })}
|
onChange={e => setConfig({ ...config, url: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="ocpp-id">Charge Box ID:</label>
|
<label className="block mb-1" htmlFor="ocpp-id">Charge Box ID:</label>
|
||||||
<input
|
<input
|
||||||
id="ocpp-id"
|
id="ocpp-id"
|
||||||
type="text"
|
type="text"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.chargeBoxId}
|
value={config.chargeBoxId}
|
||||||
onChange={e => setConfig({ ...config, chargeBoxId: e.target.value })}
|
onChange={e => setConfig({ ...config, chargeBoxId: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="ocpp-cert">Certificado:</label>
|
<label className="block mb-1" htmlFor="ocpp-cert">Certificado:</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="ocpp-cert"
|
id="ocpp-cert"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.certificate}
|
value={config.certificate}
|
||||||
onChange={e => setConfig({ ...config, certificate: e.target.value })}
|
onChange={e => setConfig({ ...config, certificate: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="ocpp-key">Chave Privada:</label>
|
<label className="block mb-1" htmlFor="ocpp-key">Chave Privada:</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="ocpp-key"
|
id="ocpp-key"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.privateKey}
|
value={config.privateKey}
|
||||||
onChange={e => setConfig({ ...config, privateKey: e.target.value })}
|
onChange={e => setConfig({ ...config, privateKey: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div>
|
||||||
<button 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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ const Security = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="security-container">
|
<div className="max-w-xl mx-auto p-5 bg-white rounded shadow">
|
||||||
<h1 className="security-title">Segurança</h1>
|
<h1 className="text-2xl font-bold mb-5 text-center">Segurança</h1>
|
||||||
|
|
||||||
{/* Métodos de Autorização */}
|
{/* Métodos de Autorização */}
|
||||||
<div className="security-item">
|
<div className="mb-5">
|
||||||
<h2>Métodos de Autorização</h2>
|
<h2 className="text-xl font-semibold mb-2">Métodos de Autorização</h2>
|
||||||
<div className="auth-methods">
|
<div className="flex flex-col gap-2">
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
RFID
|
RFID
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -46,7 +46,7 @@ const Security = () => {
|
|||||||
onChange={() => handleAuthMethodChange('RFID')}
|
onChange={() => handleAuthMethodChange('RFID')}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Aplicativo
|
Aplicativo
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -54,7 +54,7 @@ const Security = () => {
|
|||||||
onChange={() => handleAuthMethodChange('App')}
|
onChange={() => handleAuthMethodChange('App')}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="checkbox-label">
|
<label className="flex items-center gap-2">
|
||||||
Senha
|
Senha
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -66,18 +66,18 @@ const Security = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Usuários */}
|
{/* Usuários */}
|
||||||
<div className="security-item">
|
<div className="mb-5">
|
||||||
<h2>Usuários</h2>
|
<h2 className="text-xl font-semibold mb-2">Usuários</h2>
|
||||||
<ul>
|
<ul className="mb-3 space-y-2">
|
||||||
{users.map((user, index) => (
|
{users.map((user, index) => (
|
||||||
<li key={index}>
|
<li key={index} className="flex items-center justify-between">
|
||||||
<span>{user.username} - {user.role}</span>
|
<span>{user.username} - {user.role}</span>
|
||||||
<button onClick={() => removeUser(user.username)}>Remover</button>
|
<button className="text-red-600" onClick={() => removeUser(user.username)}>Remover</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="add-user">
|
<div>
|
||||||
<button onClick={() => addUser('newuser', 'User')}>Adicionar Novo Usuário</button>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,73 +16,78 @@ const Settings = () => {
|
|||||||
const handleTemperatureLimitChange = (e) => setTemperatureLimit(e.target.value);
|
const handleTemperatureLimitChange = (e) => setTemperatureLimit(e.target.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-container">
|
<div className="max-w-xl mx-auto p-5 bg-white rounded shadow">
|
||||||
<h1 className="settings-title">Configurações Gerais</h1>
|
<h1 className="text-2xl font-bold mb-5 text-center">Configurações Gerais</h1>
|
||||||
|
|
||||||
<div className="settings-item">
|
<div className="mb-5">
|
||||||
<label>Corrente Máxima de Carregamento (A):</label>
|
<label className="block mb-2">Corrente Máxima de Carregamento (A):</label>
|
||||||
<div className="slider-container">
|
<div className="flex items-center justify-between">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="5"
|
min="5"
|
||||||
max="32"
|
max="32"
|
||||||
value={currentLimit}
|
className="flex-1"
|
||||||
onChange={handleCurrentLimitChange}
|
value={currentLimit}
|
||||||
|
onChange={handleCurrentLimitChange}
|
||||||
/>
|
/>
|
||||||
<span>{currentLimit} A</span>
|
<span>{currentLimit} A</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<div className="mb-5">
|
||||||
<label>Limite de Potência Máxima (W):</label>
|
<label className="block mb-2">Limite de Potência Máxima (W):</label>
|
||||||
<div className="slider-container">
|
<div className="flex items-center justify-between">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="1000"
|
min="1000"
|
||||||
max="10000"
|
max="10000"
|
||||||
value={powerLimit}
|
className="flex-1"
|
||||||
onChange={handlePowerLimitChange}
|
value={powerLimit}
|
||||||
|
onChange={handlePowerLimitChange}
|
||||||
/>
|
/>
|
||||||
<span>{powerLimit} W</span>
|
<span>{powerLimit} W</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<div className="mb-5">
|
||||||
<label>Limite de Consumo Total de Energia (kWh):</label>
|
<label className="block mb-2">Limite de Consumo Total de Energia (kWh):</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
value={energyLimit}
|
className="w-full p-2 border rounded"
|
||||||
onChange={handleEnergyLimitChange}
|
value={energyLimit}
|
||||||
|
onChange={handleEnergyLimitChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<div className="mb-5">
|
||||||
<label>Limite de Tempo de Carregamento (h):</label>
|
<label className="block mb-2">Limite de Tempo de Carregamento (h):</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
max="24"
|
max="24"
|
||||||
value={chargingTimeLimit}
|
className="w-full p-2 border rounded"
|
||||||
onChange={handleChargingTimeLimitChange}
|
value={chargingTimeLimit}
|
||||||
|
onChange={handleChargingTimeLimitChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-item">
|
<div className="mb-5">
|
||||||
<label>Limite de Temperatura Máxima do EVSE (ºC):</label>
|
<label className="block mb-2">Limite de Temperatura Máxima do EVSE (ºC):</label>
|
||||||
<div className="slider-container">
|
<div className="flex items-center justify-between">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="60"
|
min="60"
|
||||||
max="80"
|
max="80"
|
||||||
value={temperatureLimit}
|
className="flex-1"
|
||||||
onChange={handleTemperatureLimitChange}
|
value={temperatureLimit}
|
||||||
|
onChange={handleTemperatureLimitChange}
|
||||||
/>
|
/>
|
||||||
<span>{temperatureLimit} ºC</span>
|
<span>{temperatureLimit} ºC</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="save-button">Salvar Configurações</button>
|
<button className="bg-green-600 text-white w-full mt-4 p-2 rounded hover:bg-green-700">Salvar Configurações</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,16 +44,17 @@ export default function Wifi() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Configuração Wi-Fi">
|
<PageLayout title="Configuração Wi-Fi">
|
||||||
{msg && <div className="message">{msg}</div>}
|
{msg && <div className="p-2 mb-2 bg-gray-200 rounded">{msg}</div>}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p>A carregar...</p>
|
<p>A carregar...</p>
|
||||||
) : (
|
) : (
|
||||||
<form className="form" onSubmit={(e) => { e.preventDefault(); save(); }}>
|
<form className="flex flex-col gap-4" onSubmit={(e) => { e.preventDefault(); save(); }}>
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="ssid">SSID:</label>
|
<label className="block mb-1" htmlFor="ssid">SSID:</label>
|
||||||
<select
|
<select
|
||||||
id="ssid"
|
id="ssid"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.ssid}
|
value={config.ssid}
|
||||||
onChange={e => setConfig({ ...config, ssid: e.target.value })}
|
onChange={e => setConfig({ ...config, ssid: e.target.value })}
|
||||||
>
|
>
|
||||||
@@ -64,18 +65,19 @@ export default function Wifi() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div>
|
||||||
<label htmlFor="password">Palavra-passe:</label>
|
<label className="block mb-1" htmlFor="password">Palavra-passe:</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
|
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||||
value={config.password}
|
value={config.password}
|
||||||
onChange={e => setConfig({ ...config, password: e.target.value })}
|
onChange={e => setConfig({ ...config, password: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-grid">
|
<div>
|
||||||
<button 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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user