diff --git a/package-lock.json b/package-lock.json
index 8dc307a..13e751c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index ffc9aaf..5042a7c 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/app/(protected)/administrativo/(client)/clientes/[id]/page.tsx b/src/app/(protected)/administrativo/(client)/clientes/[id]/page.tsx
index e23d318..818ea63 100644
--- a/src/app/(protected)/administrativo/(client)/clientes/[id]/page.tsx
+++ b/src/app/(protected)/administrativo/(client)/clientes/[id]/page.tsx
@@ -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 (
-
- Nenhum dado encontrado para o cliente {id}.
-
- );
- }
-
// Formata data e hora do log do servidor
const { formattedDate, formattedTime, isOutdated } = formatLogDateTime(
logServer?.data,
@@ -102,257 +124,266 @@ export default function ClientePage() {
return (
- {logServer.cns} - {logServer.cartorio}
+ {logServer?.cns ?? 'CNS não disponível'} - {logServer?.cartorio ?? 'Cartório não disponível'}
-
+
Informações do Servidor
Banco de Dados
GED
- Configurações
+ Unidades de Disco
+ Backup
{/* ===================================================== */}
{/* Aba: Informação do servidor */}
{/* ===================================================== */}
-
- {/* Badge com data e alerta */}
-
-
- Data do log: {formattedDate} às {formattedTime}
+ {logServer ? (
+
+ {/* Badge com data e alerta */}
+
+
+ Data do log: {formattedDate} às {formattedTime}
- {isOutdated && (
-
- - Atenção: Log do servidor desatualizado
-
- )}
+ {isOutdated && (
+
+ - Atenção: Log do servidor desatualizado
+
+ )}
-
+
-
+
- {/* Linha com dois cards lado a lado */}
-
- {/* Sistema Operacional */}
+ {/* Linha com dois cards lado a lado */}
+
+ {/* Sistema Operacional */}
+
+
+ Sistema Operacional
+
+
+
+
SO: {logServer?.server?.operacional_system?.so}
+
Versão: {logServer?.server?.operacional_system?.versao}
+
Release: {logServer?.server?.operacional_system?.release}
+
+ Arquitetura:
+
+ {logServer?.server?.operacional_system?.arquitetura}
+
+
+
+
+
+
+ {/* Memória */}
+
+
+ Memória
+
+
+
+
Total: {logServer?.server?.memory.total_MB} MB
+
Usada: {logServer?.server?.memory.usada_MB} MB
+
Livre: {logServer?.server?.memory.livre_MB} MB
+
Buffer: {logServer?.server?.memory.buffer_MB} MB
+
Swap Total: {logServer?.server?.memory.swap_total_MB} MB
+
Swap Usada: {logServer?.server?.memory.swap_usada_MB} MB
+
+
+
+
+
+ {/* Processadores */}
- Sistema Operacional
+ Processadores
-
-
SO: {logServer.server.operacional_system.so}
-
Versão: {logServer.server.operacional_system.versao}
-
Release: {logServer.server.operacional_system.release}
-
- Arquitetura:
-
- {logServer.server.operacional_system.arquitetura}
-
-
-
-
-
-
- {/* Memória */}
-
-
- Memória
-
-
-
-
Total: {logServer.server.memory.total_MB} MB
-
Usada: {logServer.server.memory.usada_MB} MB
-
Livre: {logServer.server.memory.livre_MB} MB
-
Buffer: {logServer.server.memory.buffer_MB} MB
-
Swap Total: {logServer.server.memory.swap_total_MB} MB
-
Swap Usada: {logServer.server.memory.swap_usada_MB} MB
-
-
-
-
-
- {/* Processadores */}
-
-
- Processadores
-
-
-
-
-
-
- Núcleo
- Modelo
- Clock (MHz)
- Cache
- Threads
- Núcleos
-
-
-
- {logServer.server.cpu.map((cpuItem: any, index: number) => (
-
- {cpuItem.núcleo}
- {cpuItem.modelo}
- {cpuItem.clock_mhz}
- {cpuItem.cache}
- {cpuItem.threads}
- {cpuItem.núcleos}
+
+
+
+
+ Núcleo
+ Modelo
+ Clock (MHz)
+ Cache
+ Threads
+ Núcleos
- ))}
-
-
-
-
-
-
+
+
+ {logServer?.server?.cpu.map((cpuItem: any, index: number) => (
+
+ {cpuItem.núcleo}
+ {cpuItem.modelo}
+ {cpuItem.clock_mhz}
+ {cpuItem.cache}
+ {cpuItem.threads}
+ {cpuItem.núcleos}
+
+ ))}
+
+
+
+
+
+
+ ) : (
+
+ Nenhum log do servidor disponível para este cliente.
+
+ )}
{/* ===================================================== */}
{/* Aba: Banco de Dados */}
{/* ===================================================== */}
-
- {/* Verifica se há dados disponíveis do banco */}
- {logDatabase?.data ? (
- <>
- {/* Badge com data e hora do log do banco */}
-
- {(() => {
- const { formattedDate, formattedTime, isOutdated } =
- formatLogDateTime(logDatabase.data.data, logDatabase.data.hora);
+ {/* Verifica se há dados disponíveis do banco */}
+ {logDatabase?.data ? (
+
+
+ {/* Badge com data e hora do log do banco */}
+
+ {(() => {
+ const { formattedDate, formattedTime, isOutdated } =
+ formatLogDateTime(logDatabase.data.data, logDatabase.data.hora);
- return (
- <>
+ return (
+ <>
+
+ Data do log: {formattedDate} às {formattedTime}
+
+ {isOutdated && (
+
+ - Atenção: Log do banco de dados desatualizado
+
+ )}
+
+ >
+ );
+ })()}
+
+
+ {/* Linha de cards principais */}
+
+ {/* Card: Partição do Banco */}
+
+
+ Partição do Banco
+
+
+
+
Unidade: {logDatabase?.data?.database?.partition.unit}
+
Espaço Total: {logDatabase?.data.database?.partition.total_disk_space}
+
Espaço Usado: {logDatabase?.data.database?.partition.total_used_disk_space}
+
Espaço Livre: {logDatabase?.data.database?.partition.total_free_disk_space}
+
+ Uso (%):{" "}
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 && (
-
- - Atenção: Log do banco de dados desatualizado
-
- )}
+ {Number(logDatabase.data.database?.partition?.percent_used || 0).toFixed(2)}%
- >
- );
- })()}
-
+
+
+
+
- {/* Linha de cards principais */}
-
- {/* Card: Partição do Banco */}
-
-
- Partição do Banco
-
-
-
-
Unidade: {logDatabase?.data?.database?.partition.unit}
-
Espaço Total: {logDatabase?.data.database?.partition.total_disk_space}
-
Espaço Usado: {logDatabase?.data.database?.partition.total_used_disk_space}
-
Espaço Livre: {logDatabase?.data.database?.partition.total_free_disk_space}
-
- Uso (%):{" "}
- 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 */}
+
+
+ Informações do Arquivo
+
+
+
+
Tamanho: {convertMBtoGB(logDatabase.data.database?.file_size_mb)}
+
Acessível:{" "}
+ {logDatabase.data.database?.db_accessible ? (
+
+ Sim
-
-
-
-
+ ) : (
+
+ Não
+
+ )}
+
- {/* Card: Informações do Arquivo de Banco */}
-
-
- Informações do Arquivo
-
-
-
-
Tamanho: {convertMBtoGB(logDatabase.data.database?.file_size_mb)}
-
Acessível:{" "}
- {logDatabase.data.database?.db_accessible ? (
-
- Sim
-
- ) : (
-
- Não
-
- )}
-
+ {/* Aqui aplicamos a função */}
+ {(() => {
+ const { formattedDate, formattedTime } = formatDateTime(
+ logDatabase?.data?.database?.last_modified
+ );
+ return (
+
+ Última Modificação: {formattedDate} às {formattedTime}
+
+ );
+ })()}
- {/* Aqui aplicamos a função */}
- {(() => {
- const { formattedDate, formattedTime } = formatDateTime(
- logDatabase?.data?.database?.last_modified
- );
- return (
-
- Última Modificação: {formattedDate} às {formattedTime}
-
- );
- })()}
+
+
+
-
-
-
+ {/* Card: Gráfico/Pie de Uso da Partição */}
+
+
+ Uso da Partição
+
+
+ {renderPartitionPieChart(logDatabase.data.database?.partition)}
+
+
- {/* Card: Gráfico/Pie de Uso da Partição */}
-
-
- Uso da Partição
-
-
- {renderPartitionPieChart(logDatabase.data.database?.partition)}
-
-
+ {/* Card: Gráfico/Bar de Uso da Partição */}
+
+
+ Uso da Partição
+
+
+ {(() => {
+ const partition = logDatabase.data.database?.partition;
+ if (!partition) return Sem dados disponíveis.
;
+ return ;
+ })()}
+
+
- {/* Card: Gráfico/Bar de Uso da Partição */}
-
-
- Uso da Partição
-
-
- {(() => {
- const partition = logDatabase.data.database?.partition;
- if (!partition) return Sem dados disponíveis.
;
- return ;
- })()}
-
-
-
-
- >
- ) : (
-
- Nenhum log de banco de dados disponível para este cliente.
-
- )}
+
-
+ ) : (
+
+ Nenhum log de banco de dados disponível para este cliente.
+
+ )}
@@ -360,135 +391,304 @@ export default function ClientePage() {
{/* Aba: GED */}
{/* ===================================================== */}
-
- {/* Verifica se há dados disponíveis do banco */}
- {logGed?.data ? (
- <>
- {/* Badge com data e hora do log do banco */}
-
- {(() => {
- const { formattedDate, formattedTime, isOutdated } =
- formatLogDateTime(logGed.data.data, logGed.data.hora);
- return (
- <>
+ {/* Verifica se há dados disponíveis do banco */}
+ {logGed?.data ? (
+
+
+ {/* Badge com data e hora do log do banco */}
+
+ {(() => {
+ const { formattedDate, formattedTime, isOutdated } =
+ formatLogDateTime(logGed.data.data, logGed.data.hora);
+
+ return (
+ <>
+
+ Data do log: {formattedDate} às {formattedTime}
+
+ {isOutdated && (
+
+ - Atenção: Log do GED desatualizado
+
+ )}
+
+ >
+ );
+ })()}
+
+
+ {/* Linha de cards principais */}
+
+ {/* Card: Partição do Banco */}
+
+
+ Partição do GED
+
+
+
+
Unidade: {logGed?.data?.ged?.partition.unit}
+
Espaço Total: {logGed?.data.ged?.partition.total_disk_space}
+
Espaço Usado: {logGed?.data.ged?.partition.total_used_disk_space}
+
Espaço Livre: {logGed?.data.ged?.partition.total_free_disk_space}
+
+ Uso (%):{" "}
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 && (
-
- - Atenção: Log do GED desatualizado
-
- )}
+ {logGed.data.ged?.partition.percent_used.toFixed(2)}%
- >
- );
- })()}
-
+
+
+
+
- {/* Linha de cards principais */}
-
- {/* Card: Partição do Banco */}
-
-
- Partição do GED
-
-
-
-
Unidade: {logGed?.data?.ged?.partition.unit}
-
Espaço Total: {logGed?.data.ged?.partition.total_disk_space}
-
Espaço Usado: {logGed?.data.ged?.partition.total_used_disk_space}
-
Espaço Livre: {logGed?.data.ged?.partition.total_free_disk_space}
-
- Uso (%):{" "}
- 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)}%
-
-
-
-
-
-
- {/* Pasta de arquivos */}
-
-
- Pasta de Arquivos
-
-
-
-
-
-
- Caminho
- Data
- Hora
- Quantidade
+ {/* Pasta de arquivos */}
+
+
+ Pasta de Arquivos
+
+
+
+
+
+
+ Caminho
+ Data
+ Hora
+ Quantidade
+
+
+
+ {logGed?.data.ged?.arquivos.map((gedItem: any, index: number) => (
+
+ {gedItem.path}
+ {gedItem.data}
+ {gedItem.hora}
+ {gedItem.quantidade}
-
-
- {logGed?.data.ged?.arquivos.map((gedItem: any, index: number) => (
-
- {gedItem.path}
- {gedItem.data}
- {gedItem.hora}
- {gedItem.quantidade}
-
- ))}
-
-
-
-
-
+ ))}
+
+
+
+
+
- {/* Card: Gráfico/Pie de Uso da Partição */}
-
-
- Uso da Partição
-
-
- {renderPartitionPieChart(logGed.data.ged?.partition)}
-
-
+ {/* Card: Gráfico/Pie de Uso da Partição */}
+
+
+ Uso da Partição
+
+
+ {renderPartitionPieChart(logGed.data.ged?.partition)}
+
+
- {/* Card: Gráfico/Bar de Uso da Partição */}
-
-
- Uso da Partição
-
-
- {(() => {
- const partition = logGed.data.ged?.partition;
- if (!partition) return Sem dados disponíveis.
;
- return ;
- })()}
-
-
-
- >
- ) : (
-
- Nenhum log de banco de dados disponível para este cliente.
-
- )}
+ {/* Card: Gráfico/Bar de Uso da Partição */}
+
+
+ Uso da Partição
+
+
+ {(() => {
+ const partition = logGed.data.ged?.partition;
+ if (!partition) return Sem dados disponíveis.
;
+ return ;
+ })()}
+
+
+
-
+ ) : (
+
+ Nenhum log de GED disponível para este cliente.
+
+ )}
+
+ {/* ===================================================== */}
+ {/* Aba: Informação do disco do servidor */}
+ {/* ===================================================== */}
+
+ {logDisk?.data ? (
+
+
+ {/* Badge com data e hora do log do disco */}
+
+ {(() => {
+ const { formattedDate, formattedTime, isOutdated } =
+ formatLogDateTime(logDisk.data.data, logDisk.data.hora);
+
+ return (
+
+ Data do log: {formattedDate} às {formattedTime}
+ {isOutdated && (
+ - Atenção: Log do disco desatualizado
+ )}
+
+ );
+ })()}
+
+
+ {/* Tabela com as partições/discos encontrados */}
+
+
+ Unidades de Disco
+
+
+
+
+
+
+ Unidade
+ Capacidade
+ Disponível
+ Utilizado
+ Disponível (%)
+
+
+
+ {Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
+
+ {unit}
+ {convertMBtoGB(info.capacidade)}
+ {convertMBtoGB(info.disponivel)}
+ {convertMBtoGB(info.utilizados)}
+
+
+ {info.disponivel_percentual}%
+
+
+
+ ))}
+
+
+
+
+
+
+ {Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
+
+ {/* 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)}
+
+ ))}
+
+
+
+ ) : (
+
+ Nenhum log do disco(s) disponível para este cliente.
+
+ )}
+
+
+
+ {/* ===================================================== */}
+ {/* Aba: Informação do servidor */}
+ {/* ===================================================== */}
+
+ {logBackup?.data ? (
+
+ {/* Badge com data do backup */}
+
+ {(() => {
+ const { formattedDate, formattedTime, isOutdated } =
+ formatLogDateTime(logBackup?.data, logBackup?.hora);
+
+ return (
+
+ Data do log: {formattedDate} às {formattedTime}
+ {isOutdated && - Atenção: Log do Backup desatualizado}
+
+ );
+ })()}
+
+
+ {/* Card com tabela de backups */}
+
+
+ Arquivos de Backup
+
+
+
+
+
+
+ Arquivo
+ Dia
+ Data
+ Hora
+ Caminho
+ Período (dias)
+ Tamanho
+
+
+
+ {Object.entries(logBackup?.backup || {}).map(([fileName, details]) => (
+
+ {fileName}
+ {details?.dia}
+ {details?.data}
+ {details?.hora}
+ {details?.caminho}
+ {details?.periodo}
+ {details?.tamanho}
+
+ ))}
+
+
+
+ Total de arquivos: {Object.keys(logBackup?.backup || {}).length}
+
+
+
+ ) : (
+
+ Nenhum log de backup disponível para este cliente.
+
+ )}
+
+
+
);
diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx
index 34f3495..d2fa314 100644
--- a/src/components/app-sidebar.tsx
+++ b/src/components/app-sidebar.tsx
@@ -58,6 +58,7 @@ const data = {
export function AppSidebar({ ...props }: React.ComponentProps) {
const { userAuthenticated } = useGUsuarioGetJWTHook();
+ console.log('LOGADO', userAuthenticated)
return (
diff --git a/src/packages/administrativo/data/Log/LogBackupData.ts b/src/packages/administrativo/data/Log/LogBackupData.ts
new file mode 100644
index 0000000..a2e3d69
--- /dev/null
+++ b/src/packages/administrativo/data/Log/LogBackupData.ts
@@ -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);
diff --git a/src/packages/administrativo/data/Log/LogDiskData.ts b/src/packages/administrativo/data/Log/LogDiskData.ts
new file mode 100644
index 0000000..bb4a2cb
--- /dev/null
+++ b/src/packages/administrativo/data/Log/LogDiskData.ts
@@ -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);
diff --git a/src/packages/administrativo/hooks/Log/useLogBackupHook.ts b/src/packages/administrativo/hooks/Log/useLogBackupHook.ts
new file mode 100644
index 0000000..3b4af10
--- /dev/null
+++ b/src/packages/administrativo/hooks/Log/useLogBackupHook.ts
@@ -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(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 };
+};
diff --git a/src/packages/administrativo/hooks/Log/useLogDiskHook.ts b/src/packages/administrativo/hooks/Log/useLogDiskHook.ts
new file mode 100644
index 0000000..b1eed4a
--- /dev/null
+++ b/src/packages/administrativo/hooks/Log/useLogDiskHook.ts
@@ -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(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 };
+};
diff --git a/src/packages/administrativo/interfaces/Log/LogBackupInterface.ts b/src/packages/administrativo/interfaces/Log/LogBackupInterface.ts
new file mode 100644
index 0000000..92a11f6
--- /dev/null
+++ b/src/packages/administrativo/interfaces/Log/LogBackupInterface.ts
@@ -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")
+ };
+ };
+ };
+}
diff --git a/src/packages/administrativo/interfaces/Log/LogDiskInterface.ts b/src/packages/administrativo/interfaces/Log/LogDiskInterface.ts
new file mode 100644
index 0000000..c8d3636
--- /dev/null
+++ b/src/packages/administrativo/interfaces/Log/LogDiskInterface.ts
@@ -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;
+ };
+ };
+ };
+}
diff --git a/src/packages/administrativo/services/Log/LogBackupService.ts b/src/packages/administrativo/services/Log/LogBackupService.ts
new file mode 100644
index 0000000..d43e951
--- /dev/null
+++ b/src/packages/administrativo/services/Log/LogBackupService.ts
@@ -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);
diff --git a/src/packages/administrativo/services/Log/LogDiskService.ts b/src/packages/administrativo/services/Log/LogDiskService.ts
new file mode 100644
index 0000000..975124b
--- /dev/null
+++ b/src/packages/administrativo/services/Log/LogDiskService.ts
@@ -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);
diff --git a/src/shared/components/charts/PartitionBarChart.tsx b/src/shared/components/charts/PartitionBarChart.tsx
index 1c0b4f7..830f725 100644
--- a/src/shared/components/charts/PartitionBarChart.tsx
+++ b/src/shared/components/charts/PartitionBarChart.tsx
@@ -35,6 +35,23 @@ export function PartitionBarChart({ used, free }: PartitionBarChartProps) {
+
+ {/* Legenda personalizada com os valores reais */}
+
+
+
+
+ Usado {`${used?.toFixed(1)}%`}
+
+
+
+
+
+ Livre ({`${free?.toFixed(1)}%`})
+
+
+
+
);
}
diff --git a/src/shared/components/charts/PartitionBarChartDisk.tsx b/src/shared/components/charts/PartitionBarChartDisk.tsx
new file mode 100644
index 0000000..13208ce
--- /dev/null
+++ b/src/shared/components/charts/PartitionBarChartDisk.tsx
@@ -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 (
+
+ Nenhum dado de disco disponível.
+
+ );
+ }
+
+ // 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 (
+
+
+ Uso da Partição ({unit})
+
+
+
+
+
+
+ `${v}%`} />
+ `${value.toFixed(1)}%`} />
+
+
+
+
+
+
+
+ {/* Legenda personalizada com os valores reais */}
+
+
+
+
+ Usado ({info.utilizados || `${usedPercent.toFixed(1)}%`})
+
+
+
+
+
+ Livre ({info.disponivel || `${freePercent.toFixed(1)}%`})
+
+
+
+
+
+ );
+ })}
+ >
+ );
+}
diff --git a/src/shared/components/charts/PartitionPieChartDisk.tsx b/src/shared/components/charts/PartitionPieChartDisk.tsx
new file mode 100644
index 0000000..08afa56
--- /dev/null
+++ b/src/shared/components/charts/PartitionPieChartDisk.tsx
@@ -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 (
+
+ Nenhum dado de disco disponível.
+
+ );
+ }
+
+ // 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 (
+
+
+ Uso da Partição ({unit})
+
+
+
+ {/* Container do gráfico */}
+
+
+
+ `${name}: ${value.toFixed(1)}%`}
+ >
+ {pieData.map((entry, idx) => (
+ |
+ ))}
+
+ [`${value.toFixed(1)}%`, name]}
+ />
+
+
+
+
+ {/* Legenda personalizada com valores reais */}
+
+
+
+
+ Usado ({info.utilizados || `${usedPercent.toFixed(1)}%`})
+
+
+
+
+
+ Livre ({info.disponivel || `${freePercent.toFixed(1)}%`})
+
+
+
+
+
+
+ );
+ })}
+ >
+ );
+}
diff --git a/src/shared/hooks/auth/useGUsuarioGetJWTHook.ts b/src/shared/hooks/auth/useGUsuarioGetJWTHook.ts
index dbcf567..ee8eed9 100644
--- a/src/shared/hooks/auth/useGUsuarioGetJWTHook.ts
+++ b/src/shared/hooks/auth/useGUsuarioGetJWTHook.ts
@@ -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(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();
}, []);
diff --git a/src/shared/utils/formatDateTime.ts b/src/shared/utils/formatDateTime.ts
index ec38518..a40e861 100644
--- a/src/shared/utils/formatDateTime.ts
+++ b/src/shared/utils/formatDateTime.ts
@@ -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) : '-'
};
}
diff --git a/src/shared/utils/formatLogDateTime.ts b/src/shared/utils/formatLogDateTime.ts
index d663c57..8a63659 100644
--- a/src/shared/utils/formatLogDateTime.ts
+++ b/src/shared/utils/formatLogDateTime.ts
@@ -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
};
}