feat: update dashboard table headers
This commit is contained in:
Generated
+18
-26
@@ -8,9 +8,10 @@
|
||||
"name": "graphics-analitics",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.8.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.14.0"
|
||||
"react-router-dom": "^7.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
@@ -735,9 +736,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -755,9 +753,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -775,9 +770,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -795,9 +787,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -815,9 +804,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -835,9 +821,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2458,6 +2441,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.8.0.tgz",
|
||||
"integrity": "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -2942,9 +2934,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz",
|
||||
"integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==",
|
||||
"version": "7.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz",
|
||||
"integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
@@ -2964,12 +2956,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz",
|
||||
"integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==",
|
||||
"version": "7.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.1.tgz",
|
||||
"integrity": "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-router": "7.14.0"
|
||||
"react-router": "7.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
|
||||
+2
-1
@@ -10,9 +10,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.8.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.14.0"
|
||||
"react-router-dom": "^7.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
|
||||
@@ -4,12 +4,14 @@ import Home from "./pages/home";
|
||||
import Login from "./pages/login";
|
||||
import About from "./pages/about";
|
||||
import Header from "./components/header";
|
||||
import MainPage from "./pages/mainPage"
|
||||
|
||||
const App = () => (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/mainPage" element={<MainPage />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
</Routes>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -7,7 +7,7 @@ const Header = () => {
|
||||
<header className="sm:h-32 flex items-center z-30 w-full dark:bg-gray-800 bg-white relative overflow-hidden h-screen">
|
||||
<div className="container mx-auto px-6 flex items-center justify-between">
|
||||
<div className="uppercase text-gray-800 dark:text-white font-black text-3xl">
|
||||
Watch.ME
|
||||
Graphics Analytics
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<nav className="font-sen text-gray-800 dark:text-white uppercase text-lg lg:flex items-center hidden">
|
||||
|
||||
+52
-43
@@ -1,48 +1,57 @@
|
||||
import React from "react";
|
||||
import React from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
import graficoExemplo from "../assets/grafico-exemplo.png"
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<>
|
||||
<main className="dark:bg-gray-800 bg-white relative overflow-hidden h-screen">
|
||||
<div className="bg-white dark:bg-gray-800 flex relative z-20 items-center overflow-hidden">
|
||||
<div className="container mx-auto px-6 flex relative py-16">
|
||||
<div className="sm:w-2/3 lg:w-2/5 flex flex-col relative z-20">
|
||||
<span className="w-20 h-2 bg-gray-800 dark:bg-white mb-12"></span>
|
||||
<h1 className="font-bebas-neue uppercase text-6xl sm:text-8xl font-black flex flex-col leading-none dark:text-white text-gray-800">
|
||||
Be on
|
||||
<span className="text-5xl sm:text-7xl">Time</span>
|
||||
</h1>
|
||||
<p className="text-sm sm:text-base text-gray-700 dark:text-white">
|
||||
Dimension of reality that makes change possible and
|
||||
understandable. An indefinite and homogeneous environment in
|
||||
which natural events and human existence take place.
|
||||
</p>
|
||||
<div className="flex mt-8">
|
||||
<a
|
||||
href="#"
|
||||
className="uppercase py-2 px-4 rounded-lg bg-pink-500 border-2 border-transparent text-white text-md mr-4 hover:bg-pink-400"
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="uppercase py-2 px-4 rounded-lg bg-transparent border-2 border-pink-500 text-pink-500 dark:text-white hover:bg-pink-500 hover:text-white text-md"
|
||||
>
|
||||
Read more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden sm:block sm:w-1/3 lg:w-3/5 relative">
|
||||
<img
|
||||
src="https://www.tailwind-kit.com/images/object/10.png"
|
||||
className="max-w-xs md:max-w-sm m-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<main className="dark:bg-slate-900 bg-slate-50 relative overflow-hidden h-screen font-sans">
|
||||
<div className="bg-slate-50 dark:bg-slate-900 flex relative z-20 items-center overflow-hidden h-full">
|
||||
<div className="container mx-auto px-6 flex relative py-16">
|
||||
<div className="sm:w-2/3 lg:w-2/5 flex flex-col relative z-20">
|
||||
<span className="w-20 h-1 bg-blue-600 mb-8 rounded-full"></span>
|
||||
<h1 className="uppercase text-5xl sm:text-7xl font-black flex flex-col leading-tight dark:text-white text-slate-900 tracking-tight">
|
||||
Dados em
|
||||
<span className="text-blue-600">Tempo Real</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-sm sm:text-base text-slate-600 dark:text-slate-300 mt-6 leading-relaxed">
|
||||
A plataforma definitiva para receber, armazenar e visualizar
|
||||
dados dos seus dispositivos inteligentes via API. Gere gráficos
|
||||
instantâneos e monitore suas Trends com endpoints seguros.
|
||||
</p>
|
||||
|
||||
<div className="flex mt-10 space-x-4">
|
||||
<Link
|
||||
to="/login"
|
||||
className="uppercase py-3 px-6 rounded-lg bg-blue-600 border-2 border-transparent text-white text-sm font-bold shadow-lg shadow-blue-500/30 hover:bg-blue-700 transition-all">
|
||||
Acessar Plataforma
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className="uppercase py-3 px-6 rounded-lg bg-transparent border-2 border-slate-300 text-slate-700 dark:text-white dark:border-slate-700 hover:border-blue-600 hover:text-blue-600 dark:hover:text-blue-400 transition-all text-sm font-bold">
|
||||
Como Funciona
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden sm:block sm:w-1/3 lg:w-3/5 relative">
|
||||
<p className="text-slate-500 font-medium">
|
||||
Exemplificação do Dashboard
|
||||
</p>
|
||||
<div className="w-full h-80 bg-slate-200 dark:bg-slate-800 rounded-2xl shadow-2xl border border-slate-300 dark:border-slate-700 flex items-center justify-center">
|
||||
<img
|
||||
src={graficoExemplo}
|
||||
alt="Exemplo de Gráfico"
|
||||
className="w-full h-full rounded-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
||||
+163
-8
@@ -1,10 +1,165 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h1>Login</h1>
|
||||
<p>This is the Login page of GeeksForGeeks.</p>
|
||||
</div>
|
||||
);
|
||||
export default function Login() {
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
export default Login;
|
||||
const handleLogin = async (e) => {
|
||||
e.preventDefault()
|
||||
setIsLoading(true)
|
||||
setTimeout(() => {
|
||||
setIsLoading(false)
|
||||
console.log("Login simulado com email:", email, "e senha:", password)
|
||||
navigate("/mainPage")
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-900 flex flex-col justify-center py-12 sm:px-6 lg:px-8 font-sans">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 className="mt-6 text-center text-3xl font-extrabold text-slate-100">
|
||||
Acesse sua conta
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-slate-600">
|
||||
Ou{" "}
|
||||
<a
|
||||
href="/#"
|
||||
className="font-medium text-blue-600 hover:text-blue-500 transition-colors"
|
||||
>
|
||||
crie sua conta
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow-xl sm:rounded-xl sm:px-10 border border-slate-200">
|
||||
<form className="space-y-6" onSubmit={handleLogin}>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-slate-700"
|
||||
>
|
||||
Endereço de e-mail
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="appearance-none block w-full px-3 py-2 border border-slate-300 rounded-lg shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 sm:text-sm transition-shadow"
|
||||
placeholder="seu@email.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="block text-sm font-medium text-slate-700"
|
||||
>
|
||||
Senha
|
||||
</label>
|
||||
<div className="mt-1 relative">
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="appearance-none block w-full pr-10 px-3 py-2 border border-slate-300 rounded-lg shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 sm:text-sm transition-shadow"
|
||||
placeholder="digite sua senha"
|
||||
type={showPassword ? "text" : "password"}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword((prev) => !prev)}
|
||||
className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-500"
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-5 h-5 " />
|
||||
) : (
|
||||
<Eye className="w-5 h-5 " />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="remember-me"
|
||||
name="remember-me"
|
||||
type="checkbox"
|
||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-slate-300 rounded cursor-pointer"
|
||||
/>
|
||||
<label
|
||||
htmlFor="remember-me"
|
||||
className="ml-2 block text-sm text-slate-900 cursor-pointer">
|
||||
Lembrar-me
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<a
|
||||
href="/forgot-password"
|
||||
className="font-medium text-blue-600 hover:text-blue-500 transition-colors">
|
||||
Esqueceu a senha?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className={`w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white ${
|
||||
isLoading
|
||||
? "bg-blue-400 cursor-not-allowed"
|
||||
: "bg-blue-600 hover:bg-blue-700 focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
} transition-all`}
|
||||
>
|
||||
{isLoading ? (
|
||||
<span className="flex items-center">
|
||||
<svg
|
||||
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
|
||||
</path>
|
||||
</svg>
|
||||
Autenticando...
|
||||
</span>
|
||||
) : (
|
||||
"Entrar na plataforma"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default function Dashboard() {
|
||||
// Estado para simular o feedback de cópia da URL
|
||||
const [copiedId, setCopiedId] = useState(null);
|
||||
// Mock de dados simulando o retorno da API
|
||||
const [trends] = useState([
|
||||
{
|
||||
id: "trend_1a2b",
|
||||
name: "Sensor_Temperatura_Estufa",
|
||||
status: "online",
|
||||
lastReading: "24.5°C",
|
||||
lastUpdate: "2 min atrás",
|
||||
endpoint: "https://api.dados.com/dweet/trend_1a2b",
|
||||
},
|
||||
{
|
||||
id: "trend_3c4d",
|
||||
name: "Sensor_Umidade_Solo_B4",
|
||||
status: "offline",
|
||||
lastReading: "78% UR",
|
||||
lastUpdate: "35 min atrás",
|
||||
endpoint: "https://api.dados.com/dweet/trend_3c4d",
|
||||
},
|
||||
{
|
||||
id: "trend_5e6f",
|
||||
name: "Vazão_Água_Entrada_H8",
|
||||
status: "pending",
|
||||
lastReading: "---",
|
||||
lastUpdate: "Nunca pareado",
|
||||
endpoint: "https://api.dados.com/dweet/trend_5e6f",
|
||||
},
|
||||
]);
|
||||
// Função para copiar a URL para a área de transferência
|
||||
const handleCopyUrl = (url, id) => {
|
||||
navigator.clipboard.writeText(url);
|
||||
setCopiedId(id);
|
||||
setTimeout(() => setCopiedId(null), 2000);
|
||||
};
|
||||
const renderStatusDot = (status) => {
|
||||
switch (status) {
|
||||
case "online":
|
||||
return (
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse"></span>
|
||||
);
|
||||
case "offline":
|
||||
return <span className="w-2.5 h-2.5 rounded-full bg-amber-500"></span>;
|
||||
default:
|
||||
return <span className="w-2.5 h-2.5 rounded-full bg-slate-400"></span>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-slate-50 font-sans overflow-hidden">
|
||||
<aside className="w-64 bg-slate-900 text-slate-300 flex flex-col shadow-2xl z-20 hidden md:flex">
|
||||
<div className="h-16 flex items-center px-6 border-b border-slate-800">
|
||||
<span className="text-white text-lg font-bold tracking-wide">
|
||||
Plataforma
|
||||
</span>
|
||||
<div className="h-8 w-8 bg-blue-600 rounded flex items-center justify-center mr-3">
|
||||
<span className="text-white font-bold text-sm">IoT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 px-4 py-6 space-y-2">
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center px-4 py-3 bg-blue-600/10 text-blue-400 rounded-lg transition-colors">
|
||||
<svg
|
||||
className="w-5 h-5 mr-3"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6">
|
||||
</path>
|
||||
</svg>
|
||||
Meus Dispositivos
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center px-4 py-3 hover:bg-slate-800 hover:text-white rounded-lg transition-colors">
|
||||
<svg
|
||||
className="w-5 h-5 mr-3"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
|
||||
</path>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z">
|
||||
</path>
|
||||
</svg>
|
||||
Configurações API
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div className="p-4 border-t border-slate-800">
|
||||
<div className="flex items-center">
|
||||
<div className="w-8 h-8 rounded-full bg-slate-700 flex items-center justify-center text-white font-bold text-sm">
|
||||
U
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<p className="text-sm font-medium text-white">Usuário TI</p>
|
||||
<p className="text-xs text-slate-500">Sair da conta</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="flex-1 flex flex-col h-screen overflow-hidden relative">
|
||||
<header className="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-8 shadow-sm">
|
||||
<h1 className="text-2xl font-bold text-slate-800">
|
||||
Meus Dispositivos{" "}
|
||||
<span className="text-slate-400 font-normal text-xl">(Trends)</span>
|
||||
</h1>
|
||||
<button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors shadow-sm flex items-center">
|
||||
<svg
|
||||
className="w-4 h-4 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
></path>
|
||||
</svg>
|
||||
Nova Trend
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-8">
|
||||
{/* Widgets de Resumo Global */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm flex items-center">
|
||||
<div className="p-3 rounded-lg bg-blue-50 text-blue-600 mr-4">
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-500 font-medium">
|
||||
Total de Trends
|
||||
</p>
|
||||
<p className="text-2xl font-bold text-slate-800">
|
||||
{trends.length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm flex items-center">
|
||||
<div className="p-3 rounded-lg bg-emerald-50 text-emerald-500 mr-4">
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-500 font-medium">
|
||||
Dispositivos Online
|
||||
</p>
|
||||
<p className="text-2xl font-bold text-slate-800">
|
||||
{trends.filter((t) => t.status === "online").length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabela das trensds */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-slate-50 border-b border-slate-200 text-xs uppercase tracking-wider text-slate-500 font-semibold">
|
||||
<th className="p-4 pl-6">Status</th>
|
||||
<th className="p-4">Identificação</th>
|
||||
<th className="p-4">Última Leitura</th>
|
||||
<th className="p-4">Endpoint API</th>
|
||||
<th className="p-4 text-center">Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{trends.map((trend) => (
|
||||
<tr
|
||||
key={trend.id}
|
||||
className="hover:bg-slate-50/50 transition-colors group">
|
||||
{/* Status da trend*/}
|
||||
<td className="p-4 pl-6 align-top">
|
||||
<div className="flex items-center space-x-2 mt-1">
|
||||
{renderStatusDot(trend.status)}
|
||||
<span className="text-sm font-medium text-slate-700 capitalize">
|
||||
{trend.status}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-slate-400 block ml-4 mt-0.5">
|
||||
{trend.lastUpdate}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* Identificação da trend */}
|
||||
<td className="p-4 align-top">
|
||||
<p className="text-sm font-semibold text-slate-800">
|
||||
{trend.name}
|
||||
</p>
|
||||
<p className="text-xs text-slate-500 mt-0.5 font-mono">
|
||||
ID: {trend.id}
|
||||
</p>
|
||||
</td>
|
||||
|
||||
{/* Leitura do último dado da trend */}
|
||||
<td className="p-4 align-top">
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium ${trend.status === "pending" ? "bg-slate-100 text-slate-500" : "bg-blue-50 text-blue-700"}`}>
|
||||
{trend.lastReading}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* Endpoint da API */}
|
||||
<td className="p-4 align-top">
|
||||
<div className="flex items-center bg-slate-50 border border-slate-200 rounded-lg p-1 max-w-sm">
|
||||
<code className="text-xs text-slate-600 truncate px-2 flex-1 font-mono">
|
||||
{trend.endpoint}
|
||||
</code>
|
||||
<button
|
||||
onClick={() =>
|
||||
handleCopyUrl(trend.endpoint, trend.id)
|
||||
}
|
||||
className="p-1.5 text-slate-400 hover:text-blue-600 hover:bg-white rounded-md transition-all ml-1 border border-transparent hover:border-slate-200 shadow-sm"
|
||||
title="Copiar URL">
|
||||
{copiedId === trend.id ? (
|
||||
<svg
|
||||
className="w-4 h-4 text-emerald-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M5 13l4 4L19 7">
|
||||
</path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="p-4 align-top text-center space-x-2">
|
||||
<button
|
||||
className="p-2 text-slate-400 hover:text-blue-600 transition-colors"
|
||||
title="Visualizar Gráfico">
|
||||
<svg
|
||||
className="w-5 h-5 inline-block"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z">
|
||||
</path>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="p-2 text-slate-400 hover:text-slate-700 transition-colors"
|
||||
title="Configurações">
|
||||
<svg
|
||||
className="w-5 h-5 inline-block"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
|
||||
</path>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user