feat(): Criação Dashboard clientes
This commit is contained in:
parent
b6a5dffdec
commit
46ccbd2b53
18 changed files with 1022 additions and 393 deletions
32
package-lock.json
generated
32
package-lock.json
generated
|
|
@ -33,6 +33,7 @@
|
|||
"date-fns": "^4.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"jose": "^6.1.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
|
|
@ -2704,7 +2705,6 @@
|
|||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
|
|
@ -2715,7 +2715,6 @@
|
|||
"integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
|
|
@ -3243,7 +3242,6 @@
|
|||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -4343,7 +4341,6 @@
|
|||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
|
|
@ -4428,7 +4425,6 @@
|
|||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
|
|
@ -4530,7 +4526,6 @@
|
|||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
|
|
@ -6021,6 +6016,15 @@
|
|||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.1.tgz",
|
||||
"integrity": "sha512-GWSqjfOPf4cWOkBzw5THBjtGPhXKqYnfRBzh4Ni+ArTrQQ9unvmsA3oFLqaYKoKe5sjWmGu5wVKg9Ft1i+LQfg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-cookie": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||
|
|
@ -6665,7 +6669,6 @@
|
|||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz",
|
||||
"integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@next/env": "15.5.4",
|
||||
"@swc/helpers": "0.5.15",
|
||||
|
|
@ -7075,7 +7078,6 @@
|
|||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
|
|
@ -7233,7 +7235,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -7243,7 +7244,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
|
|
@ -7256,7 +7256,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz",
|
||||
"integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
|
|
@ -7272,8 +7271,7 @@
|
|||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-masked-text": {
|
||||
"version": "1.0.5",
|
||||
|
|
@ -7286,7 +7284,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
|
|
@ -7405,8 +7402,7 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
|
|
@ -8200,7 +8196,6 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -8212,8 +8207,7 @@
|
|||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-8.1.2.tgz",
|
||||
"integrity": "sha512-KITxHEEHRlxC5xOnxA123eAJ67NgsWxNphtItWt9TRu07DiTZrWIqJeIKRX9euE51/l3kJO4WQiqoBXKTJJGsA==",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"peer": true
|
||||
"license": "GPL-2.0-or-later"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
"date-fns": "^4.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"jose": "^6.1.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React from "react";
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
|
|
@ -12,13 +13,16 @@ import { convertMBtoGB } from '@/shared/utils/convertMBtoGB';
|
|||
import { useLogServerHook } from '@/packages/administrativo/hooks/Log/useLogServerHook';
|
||||
import { useLogDatabaseHook } from '@/packages/administrativo/hooks/Log/useLogDatabaseHook';
|
||||
import { useLogGedHook } from '@/packages/administrativo/hooks/Log/useLogGedHook';
|
||||
import { useLogDiskHook } from '@/packages/administrativo/hooks/Log/useLogDiskHook';
|
||||
import { useLogBackupHook } from '@/packages/administrativo/hooks/Log/useLogBackupHook';
|
||||
|
||||
// Componentes do Shadcn/UI
|
||||
import {
|
||||
Card, CardHeader, CardTitle, CardContent
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Table, TableHeader, TableRow, TableHead, TableCell, TableBody
|
||||
Table, TableHeader, TableRow, TableHead, TableCell, TableBody,
|
||||
TableFooter
|
||||
} from "@/components/ui/table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
|
|
@ -27,6 +31,8 @@ import {
|
|||
} from "@/components/ui/tabs";
|
||||
import { PartitionBarChart } from '@/shared/components/charts/PartitionBarChart';
|
||||
import { renderPartitionPieChart } from '@/shared/components/charts/PartitionPieChart';
|
||||
import { renderDiskPieChartsFromJson } from '@/shared/components/charts/PartitionPieChartDisk';
|
||||
import { renderDiskBarChartsFromJson } from '@/shared/components/charts/PartitionBarChartDisk';
|
||||
|
||||
export default function ClientePage() {
|
||||
const { id } = useParams();
|
||||
|
|
@ -35,6 +41,8 @@ export default function ClientePage() {
|
|||
const { logServer, fetchLogServer } = useLogServerHook();
|
||||
const { logDatabase, fetchLogDatabase } = useLogDatabaseHook();
|
||||
const { logGed, fetchLogGed } = useLogGedHook();
|
||||
const { logDisk, fetchLogDisk } = useLogDiskHook();
|
||||
const { logBackup, fetchLogBackup } = useLogBackupHook();
|
||||
|
||||
// Efeito responsável por buscar logs de forma sequencial
|
||||
useEffect(() => {
|
||||
|
|
@ -73,6 +81,29 @@ export default function ClientePage() {
|
|||
await fetchLogGed(Number(id));
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar log do GED:", error);
|
||||
|
||||
} finally {
|
||||
// E SOMENTE após a conclusão da busca do GED
|
||||
// (mesmo que dê erro ou traga 0 registros),
|
||||
// executa a busca no Disk
|
||||
try {
|
||||
|
||||
await fetchLogDisk(Number(id));
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar log do Disk:", error);
|
||||
|
||||
} finally {
|
||||
// E SOMENTE após a conclusão da busca do Disk
|
||||
// (mesmo que dê erro ou traga 0 registros),
|
||||
// executa a busca no Backup
|
||||
try {
|
||||
|
||||
await fetchLogBackup(Number(id));
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar log do Backup:", error);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,15 +112,6 @@ export default function ClientePage() {
|
|||
fetchSequentially();
|
||||
}, [id]);
|
||||
|
||||
// Caso não haja log do servidor, mostra mensagem simples
|
||||
if (!logServer) {
|
||||
return (
|
||||
<p className="p-4 text-gray-500">
|
||||
Nenhum dado encontrado para o cliente {id}.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
// Formata data e hora do log do servidor
|
||||
const { formattedDate, formattedTime, isOutdated } = formatLogDateTime(
|
||||
logServer?.data,
|
||||
|
|
@ -102,257 +124,266 @@ export default function ClientePage() {
|
|||
return (
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-semibold mb-6">
|
||||
{logServer.cns} - {logServer.cartorio}
|
||||
{logServer?.cns ?? 'CNS não disponível'} - {logServer?.cartorio ?? 'Cartório não disponível'}
|
||||
</h1>
|
||||
|
||||
<Tabs defaultValue="server" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsList className="grid w-full grid-cols-5">
|
||||
<TabsTrigger className='cursor-pointer' value="server">Informações do Servidor</TabsTrigger>
|
||||
<TabsTrigger className='cursor-pointer' value="database">Banco de Dados</TabsTrigger>
|
||||
<TabsTrigger className='cursor-pointer' value="ged">GED</TabsTrigger>
|
||||
<TabsTrigger className='cursor-pointer' value="config">Configurações</TabsTrigger>
|
||||
<TabsTrigger className='cursor-pointer' value="disk">Unidades de Disco</TabsTrigger>
|
||||
<TabsTrigger className='cursor-pointer' value="backup">Backup</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* ===================================================== */}
|
||||
{/* Aba: Informação do servidor */}
|
||||
{/* ===================================================== */}
|
||||
<TabsContent value="server">
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Badge com data e alerta */}
|
||||
<div>
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
{logServer ? (
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Badge com data e alerta */}
|
||||
<div>
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do servidor desatualizado
|
||||
</span>
|
||||
)}
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do servidor desatualizado
|
||||
</span>
|
||||
)}
|
||||
|
||||
</Badge>
|
||||
</Badge>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Linha com dois cards lado a lado */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Sistema Operacional */}
|
||||
{/* Linha com dois cards lado a lado */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Sistema Operacional */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Sistema Operacional</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>SO:</strong> {logServer?.server?.operacional_system?.so}</p>
|
||||
<p><strong>Versão:</strong> {logServer?.server?.operacional_system?.versao}</p>
|
||||
<p><strong>Release:</strong> {logServer?.server?.operacional_system?.release}</p>
|
||||
<p>
|
||||
<strong>Arquitetura:</strong>
|
||||
<Badge variant="outline" className="ml-2">
|
||||
{logServer?.server?.operacional_system?.arquitetura}
|
||||
</Badge>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Memória */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Memória</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<p><strong>Total:</strong> {logServer?.server?.memory.total_MB} MB</p>
|
||||
<p><strong>Usada:</strong> {logServer?.server?.memory.usada_MB} MB</p>
|
||||
<p><strong>Livre:</strong> {logServer?.server?.memory.livre_MB} MB</p>
|
||||
<p><strong>Buffer:</strong> {logServer?.server?.memory.buffer_MB} MB</p>
|
||||
<p><strong>Swap Total:</strong> {logServer?.server?.memory.swap_total_MB} MB</p>
|
||||
<p><strong>Swap Usada:</strong> {logServer?.server?.memory.swap_usada_MB} MB</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Processadores */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Sistema Operacional</CardTitle>
|
||||
<CardTitle>Processadores</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>SO:</strong> {logServer.server.operacional_system.so}</p>
|
||||
<p><strong>Versão:</strong> {logServer.server.operacional_system.versao}</p>
|
||||
<p><strong>Release:</strong> {logServer.server.operacional_system.release}</p>
|
||||
<p>
|
||||
<strong>Arquitetura:</strong>
|
||||
<Badge variant="outline" className="ml-2">
|
||||
{logServer.server.operacional_system.arquitetura}
|
||||
</Badge>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Memória */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Memória</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<p><strong>Total:</strong> {logServer.server.memory.total_MB} MB</p>
|
||||
<p><strong>Usada:</strong> {logServer.server.memory.usada_MB} MB</p>
|
||||
<p><strong>Livre:</strong> {logServer.server.memory.livre_MB} MB</p>
|
||||
<p><strong>Buffer:</strong> {logServer.server.memory.buffer_MB} MB</p>
|
||||
<p><strong>Swap Total:</strong> {logServer.server.memory.swap_total_MB} MB</p>
|
||||
<p><strong>Swap Usada:</strong> {logServer.server.memory.swap_usada_MB} MB</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Processadores */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Processadores</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[250px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Núcleo</TableHead>
|
||||
<TableHead className="text-center">Modelo</TableHead>
|
||||
<TableHead className="text-center">Clock (MHz)</TableHead>
|
||||
<TableHead className="text-center">Cache</TableHead>
|
||||
<TableHead className="text-center">Threads</TableHead>
|
||||
<TableHead className="text-center">Núcleos</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{logServer.server.cpu.map((cpuItem: any, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="text-center">{cpuItem.núcleo}</TableCell>
|
||||
<TableCell className="text-center text-sm">{cpuItem.modelo}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.clock_mhz}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.cache}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.threads}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.núcleos}</TableCell>
|
||||
<ScrollArea className="h-[250px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Núcleo</TableHead>
|
||||
<TableHead className="text-center">Modelo</TableHead>
|
||||
<TableHead className="text-center">Clock (MHz)</TableHead>
|
||||
<TableHead className="text-center">Cache</TableHead>
|
||||
<TableHead className="text-center">Threads</TableHead>
|
||||
<TableHead className="text-center">Núcleos</TableHead>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{logServer?.server?.cpu.map((cpuItem: any, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="text-center">{cpuItem.núcleo}</TableCell>
|
||||
<TableCell className="text-center text-sm">{cpuItem.modelo}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.clock_mhz}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.cache}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.threads}</TableCell>
|
||||
<TableCell className="text-center">{cpuItem.núcleos}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<p className="p-4 text-gray-500 italic">
|
||||
Nenhum log do servidor disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
{/* ===================================================== */}
|
||||
{/* Aba: Banco de Dados */}
|
||||
{/* ===================================================== */}
|
||||
<TabsContent value="database">
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Verifica se há dados disponíveis do banco */}
|
||||
{logDatabase?.data ? (
|
||||
<>
|
||||
{/* Badge com data e hora do log do banco */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logDatabase.data.data, logDatabase.data.hora);
|
||||
{/* Verifica se há dados disponíveis do banco */}
|
||||
{logDatabase?.data ? (
|
||||
<div className="mt-4 space-y-6">
|
||||
|
||||
{/* Badge com data e hora do log do banco */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logDatabase.data.data, logDatabase.data.hora);
|
||||
|
||||
return (
|
||||
<>
|
||||
return (
|
||||
<>
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do banco de dados desatualizado
|
||||
</span>
|
||||
)}
|
||||
</Badge>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Linha de cards principais */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Card: Partição do Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Partição do Banco</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Unidade:</strong> {logDatabase?.data?.database?.partition.unit}</p>
|
||||
<p><strong>Espaço Total:</strong> {logDatabase?.data.database?.partition.total_disk_space}</p>
|
||||
<p><strong>Espaço Usado:</strong> {logDatabase?.data.database?.partition.total_used_disk_space}</p>
|
||||
<p><strong>Espaço Livre:</strong> {logDatabase?.data.database?.partition.total_free_disk_space}</p>
|
||||
<p>
|
||||
<strong>Uso (%):</strong>{" "}
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
variant={
|
||||
(logDatabase.data.database?.partition?.percent_used || 0) > 80
|
||||
? "destructive"
|
||||
: "outline"
|
||||
}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
(logDatabase.data.database?.partition?.percent_used || 0) > 80
|
||||
? "bg-red-200 text-red-800 border-red-400"
|
||||
: "bg-green-100 text-green-800 border-green-400"
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do banco de dados desatualizado
|
||||
</span>
|
||||
)}
|
||||
{Number(logDatabase.data.database?.partition?.percent_used || 0).toFixed(2)}%
|
||||
</Badge>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Linha de cards principais */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Card: Partição do Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Partição do Banco</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Unidade:</strong> {logDatabase?.data?.database?.partition.unit}</p>
|
||||
<p><strong>Espaço Total:</strong> {logDatabase?.data.database?.partition.total_disk_space}</p>
|
||||
<p><strong>Espaço Usado:</strong> {logDatabase?.data.database?.partition.total_used_disk_space}</p>
|
||||
<p><strong>Espaço Livre:</strong> {logDatabase?.data.database?.partition.total_free_disk_space}</p>
|
||||
<p>
|
||||
<strong>Uso (%):</strong>{" "}
|
||||
<Badge
|
||||
variant={logDatabase.data.database?.partition.percent_used > 80 ? "destructive" : "outline"}
|
||||
className={`ml-2 ${
|
||||
logDatabase.data.database?.partition.percent_used > 80
|
||||
? "bg-red-200 text-red-800 border-red-400"
|
||||
: "bg-green-100 text-green-800 border-green-400"
|
||||
}`}
|
||||
>
|
||||
{logDatabase.data.database?.partition.percent_used.toFixed(2)}%
|
||||
{/* Card: Informações do Arquivo de Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Informações do Arquivo</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Tamanho:</strong> {convertMBtoGB(logDatabase.data.database?.file_size_mb)}</p>
|
||||
<p><strong>Acessível:</strong>{" "}
|
||||
{logDatabase.data.database?.db_accessible ? (
|
||||
<Badge variant="outline" className="bg-green-100 text-green-800 border-green-400 ml-2">
|
||||
Sim
|
||||
</Badge>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Badge variant="outline" className="bg-red-100 text-red-800 border-red-400 ml-2">
|
||||
Não
|
||||
</Badge>
|
||||
)}
|
||||
</p>
|
||||
|
||||
{/* Card: Informações do Arquivo de Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Informações do Arquivo</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Tamanho:</strong> {convertMBtoGB(logDatabase.data.database?.file_size_mb)}</p>
|
||||
<p><strong>Acessível:</strong>{" "}
|
||||
{logDatabase.data.database?.db_accessible ? (
|
||||
<Badge variant="outline" className="bg-green-100 text-green-800 border-green-400 ml-2">
|
||||
Sim
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant="outline" className="bg-red-100 text-red-800 border-red-400 ml-2">
|
||||
Não
|
||||
</Badge>
|
||||
)}
|
||||
</p>
|
||||
{/* Aqui aplicamos a função */}
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime } = formatDateTime(
|
||||
logDatabase?.data?.database?.last_modified
|
||||
);
|
||||
return (
|
||||
<p>
|
||||
<strong>Última Modificação:</strong> {formattedDate} às {formattedTime}
|
||||
</p>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Aqui aplicamos a função */}
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime } = formatDateTime(
|
||||
logDatabase?.data?.database?.last_modified
|
||||
);
|
||||
return (
|
||||
<p>
|
||||
<strong>Última Modificação:</strong> {formattedDate} às {formattedTime}
|
||||
</p>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Card: Gráfico/Pie de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{renderPartitionPieChart(logDatabase.data.database?.partition)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card: Gráfico/Pie de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{renderPartitionPieChart(logDatabase.data.database?.partition)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Card: Gráfico/Bar de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const partition = logDatabase.data.database?.partition;
|
||||
if (!partition) return <p className="text-gray-500 italic">Sem dados disponíveis.</p>;
|
||||
return <PartitionBarChart used={partition.percent_used} free={partition.percent_free} />;
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card: Gráfico/Bar de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const partition = logDatabase.data.database?.partition;
|
||||
if (!partition) return <p className="text-gray-500 italic">Sem dados disponíveis.</p>;
|
||||
return <PartitionBarChart used={partition.percent_used} free={partition.percent_free} />;
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="text-gray-500 text-sm italic">
|
||||
Nenhum log de banco de dados disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
) : (
|
||||
<p className="p-4 text-gray-500 italic">
|
||||
Nenhum log de banco de dados disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
|
||||
|
|
@ -360,135 +391,304 @@ export default function ClientePage() {
|
|||
{/* Aba: GED */}
|
||||
{/* ===================================================== */}
|
||||
<TabsContent value="ged">
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Verifica se há dados disponíveis do banco */}
|
||||
{logGed?.data ? (
|
||||
<>
|
||||
{/* Badge com data e hora do log do banco */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logGed.data.data, logGed.data.hora);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Verifica se há dados disponíveis do banco */}
|
||||
{logGed?.data ? (
|
||||
<div className="mt-4 space-y-6">
|
||||
|
||||
{/* Badge com data e hora do log do banco */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logGed.data.data, logGed.data.hora);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do GED desatualizado
|
||||
</span>
|
||||
)}
|
||||
</Badge>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Linha de cards principais */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Card: Partição do Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Partição do GED</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Unidade:</strong> {logGed?.data?.ged?.partition.unit}</p>
|
||||
<p><strong>Espaço Total:</strong> {logGed?.data.ged?.partition.total_disk_space}</p>
|
||||
<p><strong>Espaço Usado:</strong> {logGed?.data.ged?.partition.total_used_disk_space}</p>
|
||||
<p><strong>Espaço Livre:</strong> {logGed?.data.ged?.partition.total_free_disk_space}</p>
|
||||
<p>
|
||||
<strong>Uso (%):</strong>{" "}
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
variant={logGed.data.ged?.partition.percent_used > 80 ? "destructive" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
logGed.data.ged?.partition.percent_used > 80
|
||||
? "bg-red-200 text-red-800 border-red-400"
|
||||
: "bg-green-100 text-green-800 border-green-400"
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
|
||||
{isOutdated && (
|
||||
<span>
|
||||
- Atenção: Log do GED desatualizado
|
||||
</span>
|
||||
)}
|
||||
{logGed.data.ged?.partition.percent_used.toFixed(2)}%
|
||||
</Badge>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Linha de cards principais */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Card: Partição do Banco */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Partição do GED</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<p><strong>Unidade:</strong> {logGed?.data?.ged?.partition.unit}</p>
|
||||
<p><strong>Espaço Total:</strong> {logGed?.data.ged?.partition.total_disk_space}</p>
|
||||
<p><strong>Espaço Usado:</strong> {logGed?.data.ged?.partition.total_used_disk_space}</p>
|
||||
<p><strong>Espaço Livre:</strong> {logGed?.data.ged?.partition.total_free_disk_space}</p>
|
||||
<p>
|
||||
<strong>Uso (%):</strong>{" "}
|
||||
<Badge
|
||||
variant={logGed.data.ged?.partition.percent_used > 80 ? "destructive" : "outline"}
|
||||
className={`ml-2 ${
|
||||
logGed.data.ged?.partition.percent_used > 80
|
||||
? "bg-red-200 text-red-800 border-red-400"
|
||||
: "bg-green-100 text-green-800 border-green-400"
|
||||
}`}
|
||||
>
|
||||
{logGed.data.ged?.partition.percent_used.toFixed(2)}%
|
||||
</Badge>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Pasta de arquivos */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Pasta de Arquivos</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[250px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Caminho</TableHead>
|
||||
<TableHead className="text-center">Data</TableHead>
|
||||
<TableHead className="text-center">Hora</TableHead>
|
||||
<TableHead className="text-center">Quantidade</TableHead>
|
||||
{/* Pasta de arquivos */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Pasta de Arquivos</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[250px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Caminho</TableHead>
|
||||
<TableHead className="text-center">Data</TableHead>
|
||||
<TableHead className="text-center">Hora</TableHead>
|
||||
<TableHead className="text-center">Quantidade</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{logGed?.data.ged?.arquivos.map((gedItem: any, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{gedItem.path}</TableCell>
|
||||
<TableCell className="text-center text-sm">{gedItem.data}</TableCell>
|
||||
<TableCell className="text-center">{gedItem.hora}</TableCell>
|
||||
<TableCell className="text-center">{gedItem.quantidade}</TableCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{logGed?.data.ged?.arquivos.map((gedItem: any, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{gedItem.path}</TableCell>
|
||||
<TableCell className="text-center text-sm">{gedItem.data}</TableCell>
|
||||
<TableCell className="text-center">{gedItem.hora}</TableCell>
|
||||
<TableCell className="text-center">{gedItem.quantidade}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
{/* Card: Gráfico/Pie de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{renderPartitionPieChart(logGed.data.ged?.partition)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Card: Gráfico/Pie de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{renderPartitionPieChart(logGed.data.ged?.partition)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card: Gráfico/Bar de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const partition = logGed.data.ged?.partition;
|
||||
if (!partition) return <p className="text-gray-500 italic">Sem dados disponíveis.</p>;
|
||||
return <PartitionBarChart used={partition.percent_used} free={partition.percent_free} />;
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="text-gray-500 text-sm italic">
|
||||
Nenhum log de banco de dados disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
{/* Card: Gráfico/Bar de Uso da Partição */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{(() => {
|
||||
const partition = logGed.data.ged?.partition;
|
||||
if (!partition) return <p className="text-gray-500 italic">Sem dados disponíveis.</p>;
|
||||
return <PartitionBarChart used={partition.percent_used} free={partition.percent_free} />;
|
||||
})()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
) : (
|
||||
<p className="p-4 text-gray-500 italic">
|
||||
Nenhum log de GED disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
|
||||
{/* ===================================================== */}
|
||||
{/* Aba: Informação do disco do servidor */}
|
||||
{/* ===================================================== */}
|
||||
<TabsContent value="disk">
|
||||
{logDisk?.data ? (
|
||||
<div className="mt-4 space-y-6">
|
||||
|
||||
{/* Badge com data e hora do log do disco */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logDisk.data.data, logDisk.data.hora);
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${
|
||||
isOutdated
|
||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
{isOutdated && (
|
||||
<span> - Atenção: Log do disco desatualizado</span>
|
||||
)}
|
||||
</Badge>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Tabela com as partições/discos encontrados */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Unidades de Disco</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[150px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Unidade</TableHead>
|
||||
<TableHead className="text-center">Capacidade</TableHead>
|
||||
<TableHead className="text-center">Disponível</TableHead>
|
||||
<TableHead className="text-center">Utilizado</TableHead>
|
||||
<TableHead className="text-center">Disponível (%)</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="text-center font-medium">{unit}</TableCell>
|
||||
<TableCell className="text-center">{convertMBtoGB(info.capacidade)}</TableCell>
|
||||
<TableCell className="text-center">{convertMBtoGB(info.disponivel)}</TableCell>
|
||||
<TableCell className="text-center">{convertMBtoGB(info.utilizados)}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Badge
|
||||
variant={
|
||||
info.disponivel_percentual < 20
|
||||
? "destructive"
|
||||
: "outline"
|
||||
}
|
||||
className={`${
|
||||
info.disponivel_percentual < 20
|
||||
? "bg-red-200 text-red-800 border-red-400"
|
||||
: "bg-green-100 text-green-800 border-green-400"
|
||||
}`}
|
||||
>
|
||||
{info.disponivel_percentual}%
|
||||
</Badge>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
|
||||
<div key={index} className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Card: Gráfico de Pizza mostrando espaço usado vs livre */}
|
||||
{renderDiskPieChartsFromJson(logDisk.data.disk)}
|
||||
|
||||
{/* Card: Gráfico de Barras mostrando uso da partição */}
|
||||
{renderDiskBarChartsFromJson(logDisk.data.disk)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
</div>
|
||||
) : (
|
||||
<p className="p-4 text-gray-500 italic">
|
||||
Nenhum log do disco(s) disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
|
||||
{/* ===================================================== */}
|
||||
{/* Aba: Informação do servidor */}
|
||||
{/* ===================================================== */}
|
||||
<TabsContent value="backup">
|
||||
{logBackup?.data ? (
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Badge com data do backup */}
|
||||
<div>
|
||||
{(() => {
|
||||
const { formattedDate, formattedTime, isOutdated } =
|
||||
formatLogDateTime(logBackup?.data, logBackup?.hora);
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant={isOutdated ? "warning" : "outline"}
|
||||
className={`ml-2 ${isOutdated ? "bg-yellow-200 text-yellow-800 border-yellow-400" : ""}`}
|
||||
>
|
||||
Data do log: {formattedDate} às {formattedTime}
|
||||
{isOutdated && <span> - Atenção: Log do Backup desatualizado</span>}
|
||||
</Badge>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Card com tabela de backups */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Arquivos de Backup</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[350px] w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">Arquivo</TableHead>
|
||||
<TableHead className="text-center">Dia</TableHead>
|
||||
<TableHead className="text-center">Data</TableHead>
|
||||
<TableHead className="text-center">Hora</TableHead>
|
||||
<TableHead className="text-center">Caminho</TableHead>
|
||||
<TableHead className="text-center">Período (dias)</TableHead>
|
||||
<TableHead className="text-center">Tamanho</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{Object.entries(logBackup?.backup || {}).map(([fileName, details]) => (
|
||||
<TableRow key={fileName}>
|
||||
<TableCell className="text-center">{fileName}</TableCell>
|
||||
<TableCell className="text-center text-sm">{details?.dia}</TableCell>
|
||||
<TableCell className="text-center">{details?.data}</TableCell>
|
||||
<TableCell className="text-center">{details?.hora}</TableCell>
|
||||
<TableCell className="text-center">{details?.caminho}</TableCell>
|
||||
<TableCell className="text-center">{details?.periodo}</TableCell>
|
||||
<TableCell className="text-center">{details?.tamanho}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
Total de arquivos: {Object.keys(logBackup?.backup || {}).length}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<p className="p-4 text-gray-500 italic">
|
||||
Nenhum log de backup disponível para este cliente.
|
||||
</p>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ const data = {
|
|||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const { userAuthenticated } = useGUsuarioGetJWTHook();
|
||||
console.log('LOGADO', userAuthenticated)
|
||||
return (
|
||||
<Sidebar collapsible="icon" {...props}>
|
||||
<SidebarHeader>
|
||||
|
|
|
|||
31
src/packages/administrativo/data/Log/LogBackupData.ts
Normal file
31
src/packages/administrativo/data/Log/LogBackupData.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
'use server'
|
||||
// Indica que este módulo será executado no lado do servidor (Backup Action do Next.js)
|
||||
|
||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||
// Importa o enumerador que contém os métodos HTTP padronizados (GET, POST, PUT, DELETE)
|
||||
|
||||
import API from '@/shared/services/api/Api';
|
||||
// Importa a classe responsável por realizar requisições HTTP à API backend
|
||||
|
||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
||||
// Importa o wrapper que padroniza o tratamento de erros e respostas para o cliente
|
||||
|
||||
// Função principal responsável por buscar um usuário específico pelo seu ID
|
||||
async function executeLogBackupData(client_id: number) {
|
||||
|
||||
// Cria uma nova instância da classe de comunicação com a API
|
||||
const api = new API();
|
||||
|
||||
// Envia uma requisição GET ao endpoint que retorna os dados de um usuário específico
|
||||
const response = await api.send({
|
||||
'method': Methods.GET, // Define o método HTTP da requisição
|
||||
'endpoint': `administrativo/log/backup/${client_id}` // Monta dinamicamente o endpoint com o ID do usuário
|
||||
});
|
||||
|
||||
// Retorna a resposta recebida da API (dados do usuário ou erro)
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta a função encapsulada com o handler de erro
|
||||
// Isso garante que exceções sejam tratadas de forma padronizada na camada superior
|
||||
export const LogBackupData = withClientErrorHandler(executeLogBackupData);
|
||||
31
src/packages/administrativo/data/Log/LogDiskData.ts
Normal file
31
src/packages/administrativo/data/Log/LogDiskData.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
'use server'
|
||||
// Indica que este módulo será executado no lado do servidor (Disk Action do Next.js)
|
||||
|
||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||
// Importa o enumerador que contém os métodos HTTP padronizados (GET, POST, PUT, DELETE)
|
||||
|
||||
import API from '@/shared/services/api/Api';
|
||||
// Importa a classe responsável por realizar requisições HTTP à API backend
|
||||
|
||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
||||
// Importa o wrapper que padroniza o tratamento de erros e respostas para o cliente
|
||||
|
||||
// Função principal responsável por buscar um usuário específico pelo seu ID
|
||||
async function executeLogDiskData(client_id: number) {
|
||||
|
||||
// Cria uma nova instância da classe de comunicação com a API
|
||||
const api = new API();
|
||||
|
||||
// Envia uma requisição GET ao endpoint que retorna os dados de um usuário específico
|
||||
const response = await api.send({
|
||||
'method': Methods.GET, // Define o método HTTP da requisição
|
||||
'endpoint': `administrativo/log/disk/${client_id}` // Monta dinamicamente o endpoint com o ID do usuário
|
||||
});
|
||||
|
||||
// Retorna a resposta recebida da API (dados do usuário ou erro)
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta a função encapsulada com o handler de erro
|
||||
// Isso garante que exceções sejam tratadas de forma padronizada na camada superior
|
||||
export const LogDiskData = withClientErrorHandler(executeLogDiskData);
|
||||
35
src/packages/administrativo/hooks/Log/useLogBackupHook.ts
Normal file
35
src/packages/administrativo/hooks/Log/useLogBackupHook.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { LogBackupInterface } from '../../interfaces/Log/LogBackupInterface';
|
||||
import { LogBackupService } from '../../services/Log/LogBackupService';
|
||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||
|
||||
export const useLogBackupHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
// Estado tipado para armazenar apenas os dados reais de log
|
||||
const [logBackup, setLog] = useState<LogBackupInterface | null>(null);
|
||||
|
||||
const fetchLogBackup = async (client_id: number) => {
|
||||
try {
|
||||
const response = await LogBackupService(client_id);
|
||||
|
||||
// Verifica se a API retorna no formato esperado
|
||||
//console.log(' Resposta bruta do LogBackupService:', response);
|
||||
|
||||
// Se a estrutura for { success, message, data }, use response.data
|
||||
const logData =
|
||||
response?.data && response.data.backup ? response.data : response;
|
||||
|
||||
setLog(logData); // Armazena só a parte relevante
|
||||
setResponse(response); // Mantém o contexto global
|
||||
|
||||
//console.log(' LogBackup armazenado no estado:', logData);
|
||||
} catch (error) {
|
||||
console.error(' Erro ao buscar informação do servidor por ID:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return { logBackup, fetchLogBackup };
|
||||
};
|
||||
23
src/packages/administrativo/hooks/Log/useLogDiskHook.ts
Normal file
23
src/packages/administrativo/hooks/Log/useLogDiskHook.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { LogDiskInterface } from '../../interfaces/Log/LogDiskInterface';
|
||||
import { LogDiskService } from '../../services/Log/LogDiskService';
|
||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||
|
||||
export const useLogDiskHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
const [logDisk, setLog] = useState<LogDiskInterface | null>(null);
|
||||
|
||||
const fetchLogDisk = async (client_id: number) => {
|
||||
try {
|
||||
const response = await LogDiskService(client_id);
|
||||
setLog(response as LogDiskInterface);
|
||||
setResponse(response);
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar informação do banco de dados:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return { logDisk, fetchLogDisk };
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Interface que representa o log de backup retornado pelo endpoint /log/backup.
|
||||
*/
|
||||
export interface LogBackupInterface {
|
||||
message?: string; // Mensagem geral de status
|
||||
data: {
|
||||
cns: string; // Código CNS do cartório
|
||||
cartorio: string; // Nome do cartório
|
||||
data: string; // Data do log
|
||||
hora: string; // Hora do log
|
||||
backup: {
|
||||
// Cada chave do objeto backup representa o nome de um arquivo ZIP
|
||||
[arquivo: string]: {
|
||||
dia: string; // Dia da semana em que o backup foi feito
|
||||
data: string; // Data completa do backup (formato dd/mm/yyyy)
|
||||
hora: string; // Hora da execução do backup
|
||||
caminho: string; // Caminho onde o arquivo está armazenado
|
||||
periodo: number; // Quantidade de dias desde o backup
|
||||
tamanho: string; // Tamanho do arquivo com unidade (ex: "240.388 MB")
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Interface que representa o log de banco de dados retornado pelo endpoint /log/database.
|
||||
*/
|
||||
export interface LogDiskInterface {
|
||||
message?: string;
|
||||
data: {
|
||||
cns: string;
|
||||
cartorio: string;
|
||||
data: string;
|
||||
hora: string;
|
||||
disk: {
|
||||
[diskName: string]: { // cada chave é o identificador do disco, ex: "//", "C:", "D:"
|
||||
capacidade: string;
|
||||
disponivel: string;
|
||||
utilizados: string;
|
||||
disponivel_percentual: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
22
src/packages/administrativo/services/Log/LogBackupService.ts
Normal file
22
src/packages/administrativo/services/Log/LogBackupService.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
'use server'
|
||||
// Indica que este arquivo é um "Backup Action", executado no lado do servidor pelo Next.js
|
||||
|
||||
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
|
||||
// Importa o wrapper responsável por padronizar o tratamento de erros nas requisições do Loge
|
||||
|
||||
import { LogBackupData } from "../../data/Log/LogBackupData";
|
||||
// Importa a função que acessa a camada de dados e retorna as informações do usuário a partir do ID
|
||||
|
||||
// Função assíncrona principal responsável por buscar um usuário pelo seu ID
|
||||
async function executeLogBackupService(client_id: number) {
|
||||
|
||||
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
|
||||
const response = await LogBackupData(client_id);
|
||||
|
||||
// Retorna a resposta vinda da camada de dados (usuário encontrado ou erro)
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta o serviço com o tratamento de erros encapsulado
|
||||
// O wrapper "withClientErrorHandler" assegura respostas consistentes em caso de falhas
|
||||
export const LogBackupService = withClientErrorHandler(executeLogBackupService);
|
||||
22
src/packages/administrativo/services/Log/LogDiskService.ts
Normal file
22
src/packages/administrativo/services/Log/LogDiskService.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
'use server'
|
||||
// Indica que este arquivo é um "disk Action", executado no lado do servidor pelo Next.js
|
||||
|
||||
import { withClientErrorHandler } from "@/withClientErrorHandler/withClientErrorHandler";
|
||||
// Importa o wrapper responsável por padronizar o tratamento de erros nas requisições do Loge
|
||||
|
||||
import { LogDiskData } from "../../data/Log/LogDiskData";
|
||||
// Importa a função que acessa a camada de dados e retorna as informações do usuário a partir do ID
|
||||
|
||||
// Função assíncrona principal responsável por buscar um usuário pelo seu ID
|
||||
async function executeLogDiskService(client_id: number) {
|
||||
|
||||
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
|
||||
const response = await LogDiskData(client_id);
|
||||
|
||||
// Retorna a resposta vinda da camada de dados (usuário encontrado ou erro)
|
||||
return response;
|
||||
}
|
||||
|
||||
// Exporta o serviço com o tratamento de erros encapsulado
|
||||
// O wrapper "withClientErrorHandler" assegura respostas consistentes em caso de falhas
|
||||
export const LogDiskService = withClientErrorHandler(executeLogDiskService);
|
||||
|
|
@ -35,6 +35,23 @@ export function PartitionBarChart({ used, free }: PartitionBarChartProps) {
|
|||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
{/* Legenda personalizada com os valores reais */}
|
||||
<div className="flex justify-center gap-6 mt-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-red-500"></span>
|
||||
<span>
|
||||
Usado {`${used?.toFixed(1)}%`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-green-500"></span>
|
||||
<span>
|
||||
Livre ({`${free?.toFixed(1)}%`})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
82
src/shared/components/charts/PartitionBarChartDisk.tsx
Normal file
82
src/shared/components/charts/PartitionBarChartDisk.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
'use client';
|
||||
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, Legend } from "recharts";
|
||||
|
||||
/**
|
||||
* Função responsável por renderizar um gráfico de barras para cada unidade de disco
|
||||
* retornada pelo JSON do log.
|
||||
*
|
||||
* @param diskData - Objeto contendo as partições (ex: logDisk.data.disk)
|
||||
*/
|
||||
export function renderDiskBarChartsFromJson(diskData: any) {
|
||||
// Caso não exista dado algum
|
||||
if (!diskData || Object.keys(diskData).length === 0) {
|
||||
return (
|
||||
<p className="text-gray-500 italic text-sm">
|
||||
Nenhum dado de disco disponível.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
// Loop sobre cada unidade do JSON (ex: "C:/", "D:/", "//")
|
||||
return (
|
||||
<>
|
||||
{Object.entries(diskData).map(([unit, info]: [string, any], index) => {
|
||||
// Calcula percentual usado e livre
|
||||
const usedPercent = 100 - Number(info.disponivel_percentual);
|
||||
const freePercent = Number(info.disponivel_percentual);
|
||||
|
||||
// Prepara dados para o gráfico
|
||||
const barData = [
|
||||
{
|
||||
name: "Uso (%)",
|
||||
Usado: usedPercent,
|
||||
Livre: freePercent,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card key={index}>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição ({unit})</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="w-full h-64">
|
||||
<ResponsiveContainer>
|
||||
<BarChart
|
||||
data={barData}
|
||||
margin={{ top: 10, right: 20, left: 0, bottom: 0 }}
|
||||
>
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis domain={[0, 100]} tickFormatter={(v) => `${v}%`} />
|
||||
<Tooltip formatter={(value: number) => `${value.toFixed(1)}%`} />
|
||||
<Legend />
|
||||
<Bar dataKey="Usado" radius={[8, 8, 0, 0]} fill="#ef4444" />
|
||||
<Bar dataKey="Livre" radius={[8, 8, 0, 0]} fill="#22c55e" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Legenda personalizada com os valores reais */}
|
||||
<div className="flex justify-center gap-6 mt-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-red-500"></span>
|
||||
<span>
|
||||
Usado ({info.utilizados || `${usedPercent.toFixed(1)}%`})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-green-500"></span>
|
||||
<span>
|
||||
Livre ({info.disponivel || `${freePercent.toFixed(1)}%`})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
92
src/shared/components/charts/PartitionPieChartDisk.tsx
Normal file
92
src/shared/components/charts/PartitionPieChartDisk.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
'use client';
|
||||
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { ResponsiveContainer, PieChart, Pie, Tooltip, Cell } from "recharts";
|
||||
|
||||
/**
|
||||
* Função responsável por renderizar um gráfico de pizza para cada unidade de disco
|
||||
* retornada pelo JSON do log.
|
||||
*
|
||||
* @param diskData - Objeto contendo as partições (ex: logDisk.data.disk)
|
||||
*/
|
||||
export function renderDiskPieChartsFromJson(diskData: any) {
|
||||
// Caso não exista dado algum
|
||||
if (!diskData || Object.keys(diskData).length === 0) {
|
||||
return (
|
||||
<p className="text-gray-500 italic text-sm">
|
||||
Nenhum dado de disco disponível.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
// Paleta de cores padrão
|
||||
const COLORS = ['#ef4444', '#22c55e'];
|
||||
|
||||
// Loop sobre cada unidade do JSON (ex: "C:/", "D:/", "//")
|
||||
return (
|
||||
<>
|
||||
{Object.entries(diskData).map(([unit, info]: [string, any], index) => {
|
||||
// Calcula percentual usado (baseado no percentual livre)
|
||||
const usedPercent = 100 - Number(info.disponivel_percentual);
|
||||
const freePercent = Number(info.disponivel_percentual);
|
||||
|
||||
// Prepara dados para o gráfico
|
||||
const pieData = [
|
||||
{ name: 'Usado', value: usedPercent },
|
||||
{ name: 'Livre', value: freePercent },
|
||||
];
|
||||
|
||||
return (
|
||||
<Card key={index}>
|
||||
<CardHeader>
|
||||
<CardTitle>Uso da Partição ({unit})</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col items-center">
|
||||
{/* Container do gráfico */}
|
||||
<div className="w-full h-64">
|
||||
<ResponsiveContainer>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={pieData}
|
||||
dataKey="value"
|
||||
nameKey="name"
|
||||
innerRadius={60}
|
||||
outerRadius={100}
|
||||
paddingAngle={3}
|
||||
label={({ name, value }) => `${name}: ${value.toFixed(1)}%`}
|
||||
>
|
||||
{pieData.map((entry, idx) => (
|
||||
<Cell key={`cell-${idx}`} fill={COLORS[idx]} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
formatter={(value: number, name: string) => [`${value.toFixed(1)}%`, name]}
|
||||
/>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Legenda personalizada com valores reais */}
|
||||
<div className="flex justify-center gap-6 mt-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-red-500"></span>
|
||||
<span>
|
||||
Usado ({info.utilizados || `${usedPercent.toFixed(1)}%`})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-3 h-3 rounded-full bg-green-500"></span>
|
||||
<span>
|
||||
Livre ({info.disponivel || `${freePercent.toFixed(1)}%`})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import CookiesGet from '../../actions/cookies/CookiesGet';
|
||||
import GetSigla from '@/shared/actions/text/GetSigla';
|
||||
import GUsuarioAuthenticatedInterface from '@/shared/interfaces/UserAuthenticatedInterface';
|
||||
|
||||
|
|
@ -19,39 +18,37 @@ export default function useGUsuarioGetJWTHook() {
|
|||
useEffect(() => {
|
||||
async function fetchToken() {
|
||||
try {
|
||||
// Executa a serve action para obtem o token, salvo em cookies
|
||||
const token = await CookiesGet('access_token');
|
||||
// Lê o token diretamente do cookie (lado do cliente)
|
||||
const token = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('access_token='))
|
||||
?.split('=')[1];
|
||||
|
||||
// Verifica se o token esta preenchido
|
||||
// Se não houver token, apenas sai (sem erro nem redirecionamento)
|
||||
if (!token) {
|
||||
console.error('Não foi localizado dados dentro do token');
|
||||
|
||||
// Encerra a aplicação
|
||||
console.warn('Token ausente ou inválido no cookie.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Decodifica os dados do JWT
|
||||
// Decodifica o JWT
|
||||
const decoded = jwtDecode<JwtPayload>(token);
|
||||
|
||||
// Se existir campo data e for string, corrige aspas simples
|
||||
// Se o campo data for string JSON, converte e adiciona sigla
|
||||
if (decoded.data && typeof decoded.data === 'string') {
|
||||
// Decodifica os dados enviado via json
|
||||
decoded.data = JSON.parse(decoded.data);
|
||||
|
||||
if (decoded.data) {
|
||||
// Gera Sigla para o nome
|
||||
decoded.data.sigla = GetSigla(decoded.data.nome || '');
|
||||
}
|
||||
}
|
||||
|
||||
// Armazena os dados decodificados
|
||||
if (decoded.data) {
|
||||
decoded.data.sigla = GetSigla(decoded.data.nome || '');
|
||||
}
|
||||
|
||||
// Salva no estado global/local
|
||||
setUserAuthenticated(decoded);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar token', error);
|
||||
console.error('Erro ao buscar token:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Busca o TOken
|
||||
fetchToken();
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,52 @@
|
|||
/**
|
||||
* Formata uma string no formato "YYYY-MM-DD HH:mm:ss"
|
||||
* para "DD/MM/YYYY" e "HH:mm".
|
||||
*
|
||||
* Inclui validação para entradas inválidas, nulas ou em formatos diferentes.
|
||||
*/
|
||||
export function formatDateTime(datetimeString: string) {
|
||||
if (!datetimeString) return { formattedDate: '-', formattedTime: '-' };
|
||||
export function formatDateTime(datetimeString: unknown) {
|
||||
// 1 Verifica se o valor foi fornecido
|
||||
if (!datetimeString) {
|
||||
return { formattedDate: '-', formattedTime: '-' };
|
||||
}
|
||||
|
||||
const [datePart, timePart] = datetimeString.split(' ');
|
||||
// 2 Converte para string de forma segura (caso venha como Date, número, etc.)
|
||||
const value = String(datetimeString).trim();
|
||||
|
||||
// 3 Se for uma string vazia, retorna padrão
|
||||
if (value.length === 0) {
|
||||
return { formattedDate: '-', formattedTime: '-' };
|
||||
}
|
||||
|
||||
// 4 Caso o valor seja uma data ISO (ex: "2025-11-09T12:30:00Z")
|
||||
// ou algo que o construtor Date entenda, tenta converter
|
||||
if (!value.includes(' ') && value.includes('T')) {
|
||||
const dateObj = new Date(value);
|
||||
if (!isNaN(dateObj.getTime())) {
|
||||
return {
|
||||
formattedDate: dateObj.toLocaleDateString('pt-BR'),
|
||||
formattedTime: dateObj.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 5 Tenta separar a data e a hora no formato esperado "YYYY-MM-DD HH:mm:ss"
|
||||
const parts = value.split(' ');
|
||||
if (parts.length < 1 || parts[0].split('-').length !== 3) {
|
||||
return { formattedDate: '-', formattedTime: '-' }; // formato inválido
|
||||
}
|
||||
|
||||
const [datePart, timePart] = parts;
|
||||
const [year, month, day] = datePart.split('-');
|
||||
|
||||
// Exemplo: 2025-11-04 09:00:01 → 04/11/2025 às 09:00
|
||||
// 6 Verifica se os componentes da data são válidos
|
||||
if (!year || !month || !day || year.length !== 4) {
|
||||
return { formattedDate: '-', formattedTime: '-' };
|
||||
}
|
||||
|
||||
// 7 Retorna a data e hora formatadas
|
||||
return {
|
||||
formattedDate: `${day}/${month}/${year}`,
|
||||
formattedTime: timePart ? timePart.substring(0, 5) : ''
|
||||
formattedTime: timePart ? timePart.substring(0, 5) : '-'
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,38 +2,40 @@ import { format } from "date-fns";
|
|||
import { ptBR } from "date-fns/locale";
|
||||
|
||||
/**
|
||||
* Formata os campos "data" e "hora" de um log no padrão brasileiro
|
||||
* e indica se o log está desatualizado em relação à data/hora atual.
|
||||
* Formata o campo "data" de um log no padrão brasileiro
|
||||
* e indica se o log está desatualizado em relação à data atual.
|
||||
*
|
||||
* @param data - Data no formato "YYYY-MM-DD"
|
||||
* @param hora - Hora no formato "HH:mm:ss"
|
||||
* @param hora - Hora no formato "HH:mm:ss" (opcional, usado apenas para exibir)
|
||||
* @returns Objeto com data/hora formatadas e flag de desatualização
|
||||
*/
|
||||
export function formatLogDateTime(data?: string, hora?: string) {
|
||||
// Combina data e hora em um formato ISO válido (ex: "2025-11-04T09:00:01")
|
||||
const combinedDateTime = data && hora ? `${data}T${hora}` : null;
|
||||
|
||||
// Cria o objeto Date com base no datetime combinado
|
||||
const logDate = combinedDateTime ? new Date(combinedDateTime) : null;
|
||||
// Cria um objeto Date apenas com a data (ignora a hora)
|
||||
const logDate = data ? new Date(`${data}T00:00:00`) : null;
|
||||
|
||||
// Evita erros se a data for inválida
|
||||
const isValid = logDate && !isNaN(logDate.getTime());
|
||||
|
||||
// Formata data e hora no padrão brasileiro (dd/MM/yyyy e HH:mm)
|
||||
// Formata data no padrão brasileiro (dd/MM/yyyy)
|
||||
const formattedDate = isValid
|
||||
? format(logDate!, "dd/MM/yyyy", { locale: ptBR })
|
||||
: "Data inválida";
|
||||
|
||||
const formattedTime = isValid
|
||||
? format(logDate!, "HH:mm", { locale: ptBR })
|
||||
// Formata hora apenas para exibição (sem influência lógica)
|
||||
const formattedTime = hora
|
||||
? hora.slice(0, 5) // ex: "13:45"
|
||||
: "--:--";
|
||||
|
||||
// Verifica se o log está desatualizado (anterior à data/hora atual)
|
||||
const isOutdated = isValid ? logDate! < new Date() : false;
|
||||
// Pega a data atual (zerando a hora para comparar só o dia)
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
// Verifica se a data do log é anterior à data atual
|
||||
const isOutdated = isValid ? logDate! < today : false;
|
||||
|
||||
return {
|
||||
formattedDate, // Ex: "04/11/2025"
|
||||
formattedTime, // Ex: "09:00"
|
||||
isOutdated, // true se a data/hora for anterior à atual
|
||||
formattedDate, // Ex: "09/11/2025"
|
||||
formattedTime, // Ex: "13:00"
|
||||
isOutdated, // true se a data for anterior à data atual
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue