feat: update dashboard table headers
This commit is contained in:
Generated
+18
-26
@@ -8,9 +8,10 @@
|
|||||||
"name": "graphics-analitics",
|
"name": "graphics-analitics",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"lucide-react": "^1.8.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-router-dom": "^7.14.0"
|
"react-router-dom": "^7.14.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.4",
|
"@eslint/js": "^9.39.4",
|
||||||
@@ -735,9 +736,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -755,9 +753,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -775,9 +770,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -795,9 +787,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -815,9 +804,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -835,9 +821,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2458,6 +2441,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"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": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
@@ -2942,9 +2934,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.14.0",
|
"version": "7.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz",
|
||||||
"integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==",
|
"integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
@@ -2964,12 +2956,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "7.14.0",
|
"version": "7.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.1.tgz",
|
||||||
"integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==",
|
"integrity": "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-router": "7.14.0"
|
"react-router": "7.14.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
|
|||||||
+2
-1
@@ -10,9 +10,10 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"lucide-react": "^1.8.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-router-dom": "^7.14.0"
|
"react-router-dom": "^7.14.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.4",
|
"@eslint/js": "^9.39.4",
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import Home from "./pages/home";
|
|||||||
import Login from "./pages/login";
|
import Login from "./pages/login";
|
||||||
import About from "./pages/about";
|
import About from "./pages/about";
|
||||||
import Header from "./components/header";
|
import Header from "./components/header";
|
||||||
|
import MainPage from "./pages/mainPage"
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<Header />
|
<Header />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/mainPage" element={<MainPage />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/about" element={<About />} />
|
<Route path="/about" element={<About />} />
|
||||||
</Routes>
|
</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">
|
<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="container mx-auto px-6 flex items-center justify-between">
|
||||||
<div className="uppercase text-gray-800 dark:text-white font-black text-3xl">
|
<div className="uppercase text-gray-800 dark:text-white font-black text-3xl">
|
||||||
Watch.ME
|
Graphics Analytics
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<nav className="font-sen text-gray-800 dark:text-white uppercase text-lg lg:flex items-center hidden">
|
<nav className="font-sen text-gray-800 dark:text-white uppercase text-lg lg:flex items-center hidden">
|
||||||
|
|||||||
+36
-27
@@ -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() {
|
function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<main className="dark:bg-gray-800 bg-white relative overflow-hidden h-screen">
|
<main className="dark:bg-slate-900 bg-slate-50 relative overflow-hidden h-screen font-sans">
|
||||||
<div className="bg-white dark:bg-gray-800 flex relative z-20 items-center overflow-hidden">
|
<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="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">
|
<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>
|
<span className="w-20 h-1 bg-blue-600 mb-8 rounded-full"></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">
|
<h1 className="uppercase text-5xl sm:text-7xl font-black flex flex-col leading-tight dark:text-white text-slate-900 tracking-tight">
|
||||||
Be on
|
Dados em
|
||||||
<span className="text-5xl sm:text-7xl">Time</span>
|
<span className="text-blue-600">Tempo Real</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm sm:text-base text-gray-700 dark:text-white">
|
|
||||||
Dimension of reality that makes change possible and
|
<p className="text-sm sm:text-base text-slate-600 dark:text-slate-300 mt-6 leading-relaxed">
|
||||||
understandable. An indefinite and homogeneous environment in
|
A plataforma definitiva para receber, armazenar e visualizar
|
||||||
which natural events and human existence take place.
|
dados dos seus dispositivos inteligentes via API. Gere gráficos
|
||||||
|
instantâneos e monitore suas Trends com endpoints seguros.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex mt-8">
|
|
||||||
<a
|
<div className="flex mt-10 space-x-4">
|
||||||
href="#"
|
<Link
|
||||||
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"
|
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">
|
||||||
Get started
|
Acessar Plataforma
|
||||||
</a>
|
</Link>
|
||||||
<a
|
<Link
|
||||||
href="#"
|
to="/about"
|
||||||
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"
|
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
|
||||||
Read more
|
</Link>
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden sm:block sm:w-1/3 lg:w-3/5 relative">
|
<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
|
<img
|
||||||
src="https://www.tailwind-kit.com/images/object/10.png"
|
src={graficoExemplo}
|
||||||
className="max-w-xs md:max-w-sm m-auto"
|
alt="Exemplo de Gráfico"
|
||||||
|
className="w-full h-full rounded-2xl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|||||||
+162
-7
@@ -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 = () => (
|
export default function Login() {
|
||||||
<div>
|
const [email, setEmail] = useState("")
|
||||||
<h1>Login</h1>
|
const [password, setPassword] = useState("")
|
||||||
<p>This is the Login page of GeeksForGeeks.</p>
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
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>
|
||||||
);
|
|
||||||
|
|
||||||
export default Login;
|
<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