feat(): Crianção da consumação dos seguintes endpoints, Log Server, Log Ged e Log Database

This commit is contained in:
Kenio 2025-11-09 09:44:13 -03:00
parent bee7779e39
commit b6a5dffdec
21 changed files with 1589 additions and 30 deletions

578
package-lock.json generated
View file

@ -18,9 +18,10 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-table": "^8.21.3",
@ -29,6 +30,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"cookies-next": "^6.1.0",
"date-fns": "^4.1.0",
"faker-js": "^1.0.0",
"input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
@ -41,6 +43,7 @@
"react-dom": "19.1.0",
"react-hook-form": "^7.62.0",
"react-masked-text": "^1.0.5",
"recharts": "^3.3.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tinymce": "^8.1.2",
@ -1119,6 +1122,24 @@
}
}
},
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
@ -1255,6 +1276,24 @@
}
}
},
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@ -1321,6 +1360,24 @@
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@ -1513,6 +1570,24 @@
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
@ -1550,6 +1625,24 @@
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@ -1653,6 +1746,24 @@
}
}
},
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
@ -1684,6 +1795,37 @@
}
}
},
"node_modules/@radix-ui/react-scroll-area": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz",
"integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
@ -1727,6 +1869,24 @@
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
@ -1751,9 +1911,9 @@
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
@ -1832,6 +1992,24 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@ -2015,6 +2193,32 @@
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@reduxjs/toolkit": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.10.1.tgz",
"integrity": "sha512-/U17EXQ9Do9Yx4DlNGU6eVNfZvFJfYpUtRRdLf19PbPjdWBxNlxGZXywQZ1p1Nz8nMkWplTI7iD/23m07nolDA==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^10.2.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@ -2029,6 +2233,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
@ -2383,6 +2593,69 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
},
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
@ -2431,6 +2704,7 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@ -2441,10 +2715,17 @@
"integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
@ -2962,6 +3243,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -3521,6 +3803,127 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -3582,6 +3985,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -3600,6 +4013,12 @@
}
}
},
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT"
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -3894,6 +4313,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-toolkit": {
"version": "1.41.0",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.41.0.tgz",
"integrity": "sha512-bDd3oRmbVgqZCJS6WmeQieOrzpl3URcWBUVDXxOELlUW2FuW+0glPOz1n0KnRie+PdyvUZcXz2sOn00c6pPRIA==",
"license": "MIT",
"workspaces": [
"docs",
"benchmarks"
]
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -3914,6 +4343,7 @@
"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",
@ -3998,6 +4428,7 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@ -4099,6 +4530,7 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@ -4524,6 +4956,12 @@
"node": ">=0.10.0"
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/faker-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/faker-js/-/faker-js-1.0.0.tgz",
@ -5039,6 +5477,16 @@
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
"integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@ -5110,6 +5558,15 @@
"node": ">= 0.4"
}
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@ -6208,6 +6665,7 @@
"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",
@ -6617,6 +7075,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@ -6774,6 +7233,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -6783,6 +7243,7 @@
"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"
},
@ -6795,6 +7256,7 @@
"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"
},
@ -6810,7 +7272,8 @@
"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"
"license": "MIT",
"peer": true
},
"node_modules/react-masked-text": {
"version": "1.0.5",
@ -6818,6 +7281,30 @@
"integrity": "sha512-WichrlCXehL0apIfIgOdi2mjBE03tdMi8wXF+DhHe2ySWYxXCkP88aqDBaJZWUMa3Jp8p2h71u7TpC7EzEjXYw==",
"license": "ISC"
},
"node_modules/react-redux": {
"version": "9.2.0",
"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"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-remove-scroll": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
@ -6887,6 +7374,49 @@
}
}
},
"node_modules/recharts": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.3.0.tgz",
"integrity": "sha512-Vi0qmTB0iz1+/Cz9o5B7irVyUjX2ynvEgImbgMt/3sKRREcUM07QiYjS1QpAVrkmVlXqy5gykq4nGWMz9AS4Rg==",
"license": "MIT",
"dependencies": {
"@reduxjs/toolkit": "1.x.x || 2.x.x",
"clsx": "^2.1.1",
"decimal.js-light": "^2.5.1",
"es-toolkit": "^1.39.3",
"eventemitter3": "^5.0.1",
"immer": "^10.1.1",
"react-redux": "8.x.x || 9.x.x",
"reselect": "5.1.1",
"tiny-invariant": "^1.3.3",
"use-sync-external-store": "^1.2.2",
"victory-vendor": "^37.0.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/redux": {
"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
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@ -6931,6 +7461,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@ -7617,6 +8153,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@ -7658,6 +8200,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -7669,7 +8212,8 @@
"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"
"license": "GPL-2.0-or-later",
"peer": true
},
"node_modules/to-regex-range": {
"version": "5.0.1",
@ -7967,6 +8511,28 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/victory-vendor": {
"version": "37.3.6",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
"license": "MIT AND ISC",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View file

@ -22,9 +22,10 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-table": "^8.21.3",
@ -33,6 +34,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"cookies-next": "^6.1.0",
"date-fns": "^4.1.0",
"faker-js": "^1.0.0",
"input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
@ -45,6 +47,7 @@
"react-dom": "19.1.0",
"react-hook-form": "^7.62.0",
"react-masked-text": "^1.0.5",
"recharts": "^3.3.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tinymce": "^8.1.2",

View file

@ -1,39 +1,495 @@
'use client';
import { useParams } from 'next/navigation';
import { useLogServerHook } from '@/packages/administrativo/hooks/Log/useLogServerHook'; // importa seu hook customizado
import { useEffect } from 'react';
// Helpers e hooks customizados
import { formatLogDateTime } from "@/shared/utils/formatLogDateTime";
import { formatDateTime } from '@/shared/utils/formatDateTime';
import { convertMBtoGB } from '@/shared/utils/convertMBtoGB';
// Hooks responsaveis em consumir os endpoint's
import { useLogServerHook } from '@/packages/administrativo/hooks/Log/useLogServerHook';
import { useLogDatabaseHook } from '@/packages/administrativo/hooks/Log/useLogDatabaseHook';
import { useLogGedHook } from '@/packages/administrativo/hooks/Log/useLogGedHook';
// Componentes do Shadcn/UI
import {
Card, CardHeader, CardTitle, CardContent
} from "@/components/ui/card";
import {
Table, TableHeader, TableRow, TableHead, TableCell, TableBody
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Tabs, TabsContent, TabsList, TabsTrigger
} from "@/components/ui/tabs";
import { PartitionBarChart } from '@/shared/components/charts/PartitionBarChart';
import { renderPartitionPieChart } from '@/shared/components/charts/PartitionPieChart';
export default function ClientePage() {
// Captura o ID da rota (ex: /administrativo/clientes/6)
const { id } = useParams();
// Hook customizado para carregar dados do log/server
const { log, fetchLogServer } = useLogServerHook();
// Hooks de logs (server e database)
const { logServer, fetchLogServer } = useLogServerHook();
const { logDatabase, fetchLogDatabase } = useLogDatabaseHook();
const { logGed, fetchLogGed } = useLogGedHook();
// Quando o componente montar, busca os dados do servidor/log
// Efeito responsável por buscar logs de forma sequencial
useEffect(() => {
if (id) fetchLogServer(Number(id));
// Só executa se houver um ID válido
if (!id) return;
const fetchSequentially = async () => {
try {
// Primeiro: tenta buscar o log do servidor
await fetchLogServer(Number(id));
} catch (error) {
console.error("Erro ao buscar log do servidor:", error);
} finally {
// Após terminar a busca do servidor (com erro ou não),
// tenta buscar o log do banco
try {
await fetchLogDatabase(Number(id));
} catch (error) {
console.error("Erro ao buscar log do banco:", error);
} finally {
// E SOMENTE após a conclusão da busca do banco
// (mesmo que dê erro ou traga 0 registros),
// executa a busca no GED
try {
await fetchLogGed(Number(id));
} catch (error) {
console.error("Erro ao buscar log do GED:", error);
}
}
}
};
fetchSequentially();
}, [id]);
// Caso não exista log
if (!log) {
return <p>Nenhum dado encontrado para o cliente {id}.</p>;
// 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,
logServer?.hora
);
// ============================================================
// Renderização principal da página
// ============================================================
return (
<div className="p-4">
<h1 className="text-2xl font-semibold mb-4">
Detalhes do Log do Cliente #{id}
<h1 className="text-2xl font-semibold mb-6">
{logServer.cns} - {logServer.cartorio}
</h1>
<div className="space-y-2">
<p><strong>Nome:</strong> {log.name}</p>
<p><strong>CNS:</strong> {log.cns}</p>
<p><strong>Status:</strong> {log.status}</p>
<p><strong>Email:</strong> {log.email}</p>
{/* outros campos conforme LogInterface */}
</div>
<Tabs defaultValue="server" className="w-full">
<TabsList className="grid w-full grid-cols-4">
<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>
</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}
{isOutdated && (
<span>
- Atenção: Log do servidor desatualizado
</span>
)}
</Badge>
</div>
{/* 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>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>
</TableRow>
))}
</TableBody>
</Table>
</ScrollArea>
</CardContent>
</Card>
</div>
</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);
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={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)}%
</Badge>
</p>
</div>
</CardContent>
</Card>
{/* 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>
);
})()}
</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/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>
</TabsContent>
{/* ===================================================== */}
{/* 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 (
<>
<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={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>
</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>
{/* 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>
)}
</div>
</TabsContent>
</Tabs>
</div>
);
}

View file

@ -0,0 +1,46 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary:
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
function Badge({
className,
variant,
asChild = false,
...props
}: React.ComponentProps<"span"> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot : "span"
return (
<Comp
data-slot="badge"
className={cn(badgeVariants({ variant }), className)}
{...props}
/>
)
}
export { Badge, badgeVariants }

View file

@ -0,0 +1,58 @@
"use client"
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
function ScrollArea({
className,
children,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn("relative", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
)
}
function ScrollBar({
className,
orientation = "vertical",
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return (
<ScrollAreaPrimitive.ScrollAreaScrollbar
data-slot="scroll-area-scrollbar"
orientation={orientation}
className={cn(
"flex touch-none p-px transition-colors select-none",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb
data-slot="scroll-area-thumb"
className="bg-border relative flex-1 rounded-full"
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
)
}
export { ScrollArea, ScrollBar }

View file

@ -0,0 +1,31 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Database 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 executeLogDatabaseData(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/database/${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 LogDatabaseData = withClientErrorHandler(executeLogDatabaseData);

View file

@ -0,0 +1,31 @@
'use server'
// Indica que este módulo será executado no lado do servidor (Ged 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 executeLogGedData(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/ged/${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 LogGedData = withClientErrorHandler(executeLogGedData);

View file

@ -0,0 +1,23 @@
'use client';
import { useState } from 'react';
import { LogDatabaseInterface } from '../../interfaces/Log/LogDatabaseInterface';
import { LogDatabaseService } from '../../services/Log/LogDatabaseService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useLogDatabaseHook = () => {
const { setResponse } = useResponse();
const [logDatabase, setLog] = useState<LogDatabaseInterface | null>(null);
const fetchLogDatabase = async (client_id: number) => {
try {
const response = await LogDatabaseService(client_id);
setLog(response as LogDatabaseInterface);
setResponse(response);
} catch (error) {
console.error("Erro ao buscar informação do banco de dados:", error);
}
};
return { logDatabase, fetchLogDatabase };
};

View file

@ -0,0 +1,23 @@
'use client';
import { useState } from 'react';
import { LogGedInterface } from '../../interfaces/Log/LogGedInterface';
import { LogGedService } from '../../services/Log/LogGedService';
import { useResponse } from '@/shared/components/response/ResponseContext';
export const useLogGedHook = () => {
const { setResponse } = useResponse();
const [logGed, setLog] = useState<LogGedInterface | null>(null);
const fetchLogGed = async (client_id: number) => {
try {
const response = await LogGedService(client_id);
setLog(response as LogGedInterface);
setResponse(response);
} catch (error) {
console.error("Erro ao buscar informação do banco de dados:", error);
}
};
return { logGed, fetchLogGed };
};

View file

@ -8,15 +8,12 @@ import { useResponse } from '@/shared/components/response/ResponseContext';
export const useLogServerHook = () => {
const { setResponse } = useResponse();
const [log, setLog] = useState<LogInterface | null>(null);
const [logServer, setLog] = useState<LogInterface | null>(null);
const fetchLogServer = async (client_id: number) => {
try {
const response = await LogServerService(client_id);
console.log(response)
setLog(response.data);
setResponse(response);
} catch (error) {
@ -26,5 +23,5 @@ export const useLogServerHook = () => {
}
};
return { log, fetchLogServer };
return { logServer, fetchLogServer };
};

View file

@ -0,0 +1,25 @@
/**
* Interface que representa o log de banco de dados retornado pelo endpoint /log/database.
*/
export interface LogDatabaseInterface {
message?: string;
data: {
cns: string;
cartorio: string;
data: string;
hora: string;
database: {
partition: {
unit: string;
percent_free: number;
percent_used: number;
total_disk_space: string;
total_free_disk_space: string;
total_used_disk_space: string;
};
file_size_mb: string;
db_accessible: boolean;
last_modified: string;
};
};
}

View file

@ -0,0 +1,28 @@
/**
* Interface que representa o log de banco de dados retornado pelo endpoint /log/database.
*/
export interface LogDatabaseInterface {
message?: string;
data: {
cns: string;
cartorio: string;
data: string;
hora: string;
ged: {
partition: {
unit: string;
percent_free: number;
percent_used: number;
total_disk_space: string;
total_free_disk_space: string;
total_used_disk_space: string;
};
arquivos: [{
path: string;
data: string;
hora: string;
quantidade: number;
}]
};
};
}

View file

@ -7,4 +7,9 @@ export interface LogInterface {
client_id: number; // ID do cliente relacionado (chave estrangeira obrigatória)
date_post?: string; // Data e hora do registro (gerada automaticamente pelo banco)
file: object; // Dados em formato JSON (ex: informações do arquivo ou operação)
cartorio?: string;
server?: string;
cns?: string;
data?: string;
hora?: string;
}

View file

@ -0,0 +1,36 @@
/**
* Interface que representa o log de servidor retornado pelo endpoint /log/server.
*/
export interface LogServerInterface {
message?: string;
data: {
cns: string;
cartorio: string;
data: string;
hora: string;
server: {
operacional_system: {
so: string;
versao: string;
release: string;
arquitetura: string;
};
memory: {
total_MB: number;
usada_MB: number;
livre_MB: number;
buffer_MB: number;
swap_total_MB: number;
swap_usada_MB: number;
};
cpu: Array<{
núcleo: string;
modelo: string;
clock_mhz: number;
cache: string;
threads: number;
núcleos: number;
}>;
};
};
}

View file

@ -0,0 +1,22 @@
'use server'
// Indica que este arquivo é um "Database 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 { LogDatabaseData } from "../../data/Log/LogDatabaseData";
// 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 executeLogDatabaseService(client_id: number) {
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
const response = await LogDatabaseData(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 LogDatabaseService = withClientErrorHandler(executeLogDatabaseService);

View file

@ -0,0 +1,22 @@
'use server'
// Indica que este arquivo é um "Ged 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 { LogGedData } from "../../data/Log/LogGedData";
// 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 executeLogGedService(client_id: number) {
// Executa a função de busca de usuário, passando o ID recebido como parâmetro
const response = await LogGedData(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 LogGedService = withClientErrorHandler(executeLogGedService);

View file

@ -0,0 +1,40 @@
'use client';
import { ResponsiveContainer, BarChart, Bar, XAxis, Tooltip, Cell } from 'recharts';
interface PartitionBarChartProps {
used: number;
free: number;
}
export function PartitionBarChart({ used, free }: PartitionBarChartProps) {
// Estrutura dos dados do gráfico
const barData = [
{ name: 'Usado', value: used },
{ name: 'Livre', value: free },
];
return (
<div className="w-full h-64">
<ResponsiveContainer>
<BarChart data={barData} margin={{ top: 20, right: 30, left: 0, bottom: 5 }}>
{/* Eixo X com rótulos (Usado / Livre) */}
<XAxis dataKey="name" />
{/* Tooltip com formatação percentual */}
<Tooltip formatter={(value: number) => `${value.toFixed(2)}%`} />
{/* Barras com cores diferentes para "Usado" e "Livre" */}
<Bar dataKey="value" radius={[8, 8, 0, 0]}>
{barData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={entry.name === 'Usado' ? '#ef4444' : '#22c55e'} // vermelho para Usado, verde para Livre
/>
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
);
}

View file

@ -0,0 +1,71 @@
'use client';
import { ResponsiveContainer, PieChart, Pie, Tooltip, Cell } from 'recharts';
/**
* Função auxiliar que retorna um gráfico de pizza pronto
* para exibição no CardContent.
*
* @param partition - Objeto contendo os dados da partição
* (percent_used, percent_free, total_used_disk_space, total_free_disk_space)
*/
export function renderPartitionPieChart(partition: any) {
// Verifica se há dados válidos
if (!partition) {
return (
<p className="text-gray-500 text-sm italic">
Nenhuma informação de partição disponível.
</p>
);
}
// Estrutura de dados para o gráfico de pizza
const pieData = [
{ name: 'Usado', value: partition.percent_used },
{ name: 'Livre', value: partition.percent_free },
];
// Paleta de cores das fatias (vermelho e verde)
const COLORS = ['#ef4444', '#22c55e'];
// Retorna o gráfico renderizado pronto para o CardContent
return (
<div className="flex flex-col items-center">
{/* Container responsivo 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, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index]} />
))}
</Pie>
<Tooltip
formatter={(value: number, name: string) => [`${value.toFixed(2)}%`, name]}
/>
</PieChart>
</ResponsiveContainer>
</div>
{/* Legenda abaixo do gráfico */}
<div className="flex justify-center gap-4 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 ({partition.total_used_disk_space})</span>
</div>
<div className="flex items-center gap-2">
<span className="w-3 h-3 rounded-full bg-green-500"></span>
<span>Livre ({partition.total_free_disk_space})</span>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,21 @@
/**
* Converte valor de megabytes (MB) para gigabytes (GB).
* Aceita valores numéricos ou strings com separadores e unidades.
* Exemplo: "22.380.281 GB" ou "22380281 MB"
*/
export function convertMBtoGB(value: string | number): string {
if (!value) return '-';
// Extrai apenas dígitos e pontos da string
const numericValue = parseFloat(
String(value).replace(/[^\d.]/g, '').replace(/\.(?=.*\.)/g, '')
);
if (isNaN(numericValue)) return '-';
// Converte MB → GB (1 GB = 1024 MB)
const gbValue = numericValue / 1024;
// Arredonda para 2 casas decimais
return `${gbValue.toFixed(2)} GB`;
}

View file

@ -0,0 +1,16 @@
/**
* Formata uma string no formato "YYYY-MM-DD HH:mm:ss"
* para "DD/MM/YYYY" e "HH:mm".
*/
export function formatDateTime(datetimeString: string) {
if (!datetimeString) return { formattedDate: '-', formattedTime: '-' };
const [datePart, timePart] = datetimeString.split(' ');
const [year, month, day] = datePart.split('-');
// Exemplo: 2025-11-04 09:00:01 → 04/11/2025 às 09:00
return {
formattedDate: `${day}/${month}/${year}`,
formattedTime: timePart ? timePart.substring(0, 5) : ''
};
}

View file

@ -0,0 +1,39 @@
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.
*
* @param data - Data no formato "YYYY-MM-DD"
* @param hora - Hora no formato "HH:mm:ss"
* @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;
// 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)
const formattedDate = isValid
? format(logDate!, "dd/MM/yyyy", { locale: ptBR })
: "Data inválida";
const formattedTime = isValid
? format(logDate!, "HH:mm", { locale: ptBR })
: "--:--";
// Verifica se o log está desatualizado (anterior à data/hora atual)
const isOutdated = isValid ? logDate! < new Date() : false;
return {
formattedDate, // Ex: "04/11/2025"
formattedTime, // Ex: "09:00"
isOutdated, // true se a data/hora for anterior à atual
};
}