Compare commits
No commits in common. "DMC-5" and "main" have entirely different histories.
146 changed files with 1239 additions and 6420 deletions
|
|
@ -1,10 +0,0 @@
|
||||||
node_modules
|
|
||||||
npm-debug.log
|
|
||||||
Dockerfile
|
|
||||||
.dockerignore
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.env
|
|
||||||
*.md
|
|
||||||
.vscode
|
|
||||||
dist
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -40,4 +40,4 @@ yarn-error.log*
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
# /src/config/app.json
|
/src/config/app.json
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,41 @@
|
||||||
# ========================================================
|
# Etapa 1: Construir a aplicação
|
||||||
# Etapa 1: Build da aplicação
|
|
||||||
# ========================================================
|
|
||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
# Define diretório de trabalho
|
# Define o diretório de trabalho
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copia arquivos de configuração do pacote
|
# Copia os arquivos de configuração do pacote
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
|
|
||||||
# Instala dependências de produção e desenvolvimento
|
# Instala as dependências do projeto
|
||||||
RUN npm ci
|
RUN npm install
|
||||||
|
|
||||||
# Copia todo o código fonte
|
# Copia todo o código da aplicação para o container
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Gera build standalone
|
# Constrói a aplicação com o output 'standalone'
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# ========================================================
|
# Etapa 2: Executar a aplicação
|
||||||
# Etapa 2: Runner (produção)
|
# Usa uma imagem Node.js leve
|
||||||
# ========================================================
|
|
||||||
FROM node:20-alpine AS runner
|
FROM node:20-alpine AS runner
|
||||||
|
|
||||||
# Define diretório de trabalho
|
# Define o diretório de trabalho
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copia diretório standalone gerado pelo build
|
# Copia o diretório 'standalone' da etapa de build, que já contém o servidor e as dependências
|
||||||
|
# O diretório 'standalone' é a pasta .next/standalone gerada pela configuração 'output: standalone'
|
||||||
COPY --from=builder /app/.next/standalone ./
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
|
||||||
# Copia pasta public
|
# Copia os arquivos públicos
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
# Copia arquivos estáticos do build
|
# Copia os arquivos estáticos gerados pelo build. É aqui que os arquivos CSS e JS ficam.
|
||||||
COPY --from=builder /app/.next/static ./.next/static
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
# Expondo porta padrão do Next.js
|
# Expõe a porta padrão do Next.js
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Comando de inicialização do servidor standalone
|
# Define o comando para iniciar a aplicação
|
||||||
|
# O 'start' do package.json não é necessário, o próprio servidor standalone já está no container
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
588
package-lock.json
generated
588
package-lock.json
generated
|
|
@ -18,10 +18,9 @@
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-popover": "^1.1.15",
|
"@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-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
|
@ -30,10 +29,8 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"cookies-next": "^6.1.0",
|
"cookies-next": "^6.1.0",
|
||||||
"date-fns": "^4.1.0",
|
|
||||||
"faker-js": "^1.0.0",
|
"faker-js": "^1.0.0",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"jose": "^6.1.1",
|
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
|
@ -44,7 +41,6 @@
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-masked-text": "^1.0.5",
|
"react-masked-text": "^1.0.5",
|
||||||
"recharts": "^3.3.0",
|
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tinymce": "^8.1.2",
|
"tinymce": "^8.1.2",
|
||||||
|
|
@ -1123,24 +1119,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||||
|
|
@ -1277,24 +1255,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
|
@ -1361,24 +1321,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-direction": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||||
|
|
@ -1571,24 +1513,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-popover": {
|
||||||
"version": "1.1.15",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
|
||||||
|
|
@ -1626,24 +1550,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
|
||||||
|
|
@ -1747,24 +1653,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-roving-focus": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
||||||
|
|
@ -1796,37 +1684,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-select": {
|
||||||
"version": "2.2.6",
|
"version": "2.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
|
||||||
|
|
@ -1870,24 +1727,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-separator": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
|
||||||
|
|
@ -1912,9 +1751,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.2"
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
|
@ -1993,24 +1832,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
|
@ -2194,32 +2015,6 @@
|
||||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
|
|
@ -2234,12 +2029,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@standard-schema/utils": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||||
|
|
@ -2594,69 +2383,6 @@
|
||||||
"tslib": "^2.4.0"
|
"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": {
|
"node_modules/@types/js-cookie": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
|
||||||
|
|
@ -2705,7 +2431,6 @@
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
|
|
@ -2716,17 +2441,10 @@
|
||||||
"integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==",
|
"integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@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": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.46.0",
|
"version": "8.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
|
||||||
|
|
@ -3244,7 +2962,6 @@
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
|
@ -3804,127 +3521,6 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/damerau-levenshtein": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||||
|
|
@ -3986,16 +3582,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
|
@ -4014,12 +3600,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||||
|
|
@ -4314,16 +3894,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
|
@ -4344,7 +3914,6 @@
|
||||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
|
|
@ -4429,7 +3998,6 @@
|
||||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
|
|
@ -4531,7 +4099,6 @@
|
||||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"array-includes": "^3.1.9",
|
||||||
|
|
@ -4957,12 +4524,6 @@
|
||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/faker-js": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/faker-js/-/faker-js-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/faker-js/-/faker-js-1.0.0.tgz",
|
||||||
|
|
@ -5478,16 +5039,6 @@
|
||||||
"node": ">= 4"
|
"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": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||||
|
|
@ -5559,15 +5110,6 @@
|
||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/is-array-buffer": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||||
|
|
@ -6022,15 +5564,6 @@
|
||||||
"jiti": "lib/jiti-cli.mjs"
|
"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": {
|
"node_modules/js-cookie": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||||
|
|
@ -6675,7 +6208,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz",
|
||||||
"integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==",
|
"integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "15.5.4",
|
"@next/env": "15.5.4",
|
||||||
"@swc/helpers": "0.5.15",
|
"@swc/helpers": "0.5.15",
|
||||||
|
|
@ -7085,7 +6617,6 @@
|
||||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
|
|
@ -7243,7 +6774,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -7253,7 +6783,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.26.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
|
|
@ -7266,7 +6795,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz",
|
||||||
"integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==",
|
"integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -7282,8 +6810,7 @@
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react-masked-text": {
|
"node_modules/react-masked-text": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
|
|
@ -7291,30 +6818,6 @@
|
||||||
"integrity": "sha512-WichrlCXehL0apIfIgOdi2mjBE03tdMi8wXF+DhHe2ySWYxXCkP88aqDBaJZWUMa3Jp8p2h71u7TpC7EzEjXYw==",
|
"integrity": "sha512-WichrlCXehL0apIfIgOdi2mjBE03tdMi8wXF+DhHe2ySWYxXCkP88aqDBaJZWUMa3Jp8p2h71u7TpC7EzEjXYw==",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/react-remove-scroll": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
||||||
|
|
@ -7384,49 +6887,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
|
|
@ -7471,12 +6931,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
|
|
@ -8163,12 +7617,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
|
@ -8210,7 +7658,6 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -8222,8 +7669,7 @@
|
||||||
"version": "8.1.2",
|
"version": "8.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-8.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-8.1.2.tgz",
|
||||||
"integrity": "sha512-KITxHEEHRlxC5xOnxA123eAJ67NgsWxNphtItWt9TRu07DiTZrWIqJeIKRX9euE51/l3kJO4WQiqoBXKTJJGsA==",
|
"integrity": "sha512-KITxHEEHRlxC5xOnxA123eAJ67NgsWxNphtItWt9TRu07DiTZrWIqJeIKRX9euE51/l3kJO4WQiqoBXKTJJGsA==",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
|
|
@ -8521,28 +7967,6 @@
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"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": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,9 @@
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-popover": "^1.1.15",
|
"@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-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
|
@ -34,10 +33,8 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"cookies-next": "^6.1.0",
|
"cookies-next": "^6.1.0",
|
||||||
"date-fns": "^4.1.0",
|
|
||||||
"faker-js": "^1.0.0",
|
"faker-js": "^1.0.0",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"jose": "^6.1.1",
|
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
|
@ -48,7 +45,6 @@
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-hook-form": "^7.62.0",
|
"react-hook-form": "^7.62.0",
|
||||||
"react-masked-text": "^1.0.5",
|
"react-masked-text": "^1.0.5",
|
||||||
"recharts": "^3.3.0",
|
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tinymce": "^8.1.2",
|
"tinymce": "^8.1.2",
|
||||||
|
|
|
||||||
|
|
@ -1,762 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { useParams } from 'next/navigation';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
// 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';
|
|
||||||
import { useLogDiskHook } from '@/packages/administrativo/hooks/Log/useLogDiskHook';
|
|
||||||
import { useLogBackupHook } from '@/packages/administrativo/hooks/Log/useLogBackupHook';
|
|
||||||
import { useLogWarningHook } from '@/packages/administrativo/hooks/Log/useLogWarningHook';
|
|
||||||
|
|
||||||
// Componentes do Shadcn/UI
|
|
||||||
import {
|
|
||||||
Card, CardHeader, CardTitle, CardContent
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import {
|
|
||||||
Table, TableHeader, TableRow, TableHead, TableCell, TableBody,
|
|
||||||
TableFooter
|
|
||||||
} 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';
|
|
||||||
import { renderDiskPieChartsFromJson } from '@/shared/components/charts/PartitionPieChartDisk';
|
|
||||||
import { renderDiskBarChartsFromJson } from '@/shared/components/charts/PartitionBarChartDisk';
|
|
||||||
|
|
||||||
export default function ClientePage() {
|
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
// Hooks de logs (server e database)
|
|
||||||
const { logServer, fetchLogServer } = useLogServerHook();
|
|
||||||
const { logDatabase, fetchLogDatabase } = useLogDatabaseHook();
|
|
||||||
const { logGed, fetchLogGed } = useLogGedHook();
|
|
||||||
const { logDisk, fetchLogDisk } = useLogDiskHook();
|
|
||||||
const { logBackup, fetchLogBackup } = useLogBackupHook();
|
|
||||||
const { logWarning, fetchLogWarning } = useLogWarningHook();
|
|
||||||
|
|
||||||
// Efeito responsável por buscar logs de forma sequencial
|
|
||||||
useEffect(() => {
|
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
// Helper: executa função e ignora erros (logando no console)
|
|
||||||
const safeRun = async (label: string, fn: () => Promise<any>) => {
|
|
||||||
try {
|
|
||||||
await fn();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Erro ao executar ${label}:`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchSequentially = async () => {
|
|
||||||
|
|
||||||
await safeRun("log do servidor", () => fetchLogServer(Number(id)));
|
|
||||||
|
|
||||||
await safeRun("log do banco", () => fetchLogDatabase(Number(id)));
|
|
||||||
|
|
||||||
await safeRun("log do GED", () => fetchLogGed(Number(id)));
|
|
||||||
|
|
||||||
await safeRun("log do Disk", () => fetchLogDisk(Number(id)));
|
|
||||||
|
|
||||||
await safeRun("log do Backup", () => fetchLogBackup(Number(id)));
|
|
||||||
|
|
||||||
await safeRun("log do Warning", () => fetchLogWarning(Number(id)));
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchSequentially();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
// 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-6">
|
|
||||||
{logServer?.cns ?? 'CNS não disponível'} - {logServer?.cartorio ?? 'Cartório não disponível'}
|
|
||||||
<span className="m-4 text-gray-500 "> :: </span>
|
|
||||||
<Link href={`/administrativo/clientes`} className="ml-2 text-gray-500 hover:underline">
|
|
||||||
Voltar
|
|
||||||
</Link>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
|
|
||||||
<Tabs defaultValue="server" className="w-full">
|
|
||||||
<TabsList className="grid w-full grid-cols-6">
|
|
||||||
<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="disk">Unidades de Disco</TabsTrigger>
|
|
||||||
<TabsTrigger className='cursor-pointer' value="backup">Backup</TabsTrigger>
|
|
||||||
<TabsTrigger className='cursor-pointer' value="warning">Avisos</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: Informação do servidor */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="server">
|
|
||||||
{logServer ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
{/* Badge com data e alerta */}
|
|
||||||
<div>
|
|
||||||
<Badge
|
|
||||||
variant={isOutdated ? "warning" : "outline"}
|
|
||||||
className={`ml-2 ${
|
|
||||||
isOutdated
|
|
||||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Data do log: {formattedDate} às {formattedTime}
|
|
||||||
|
|
||||||
{isOutdated && (
|
|
||||||
<span>
|
|
||||||
- Atenção: Log do servidor desatualizado
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</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>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum log do servidor disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: Banco de Dados */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="database">
|
|
||||||
{/* Verifica se há dados disponíveis do banco */}
|
|
||||||
{logDatabase?.data ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
|
|
||||||
{/* Badge com data e hora do log do banco */}
|
|
||||||
<div>
|
|
||||||
{(() => {
|
|
||||||
const { formattedDate, formattedTime, isOutdated } =
|
|
||||||
formatLogDateTime(logDatabase.data.data, logDatabase.data.hora);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<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 || 0) > 80
|
|
||||||
? "destructive"
|
|
||||||
: "outline"
|
|
||||||
}
|
|
||||||
className={`ml-2 ${
|
|
||||||
(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"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{Number(logDatabase.data.database?.partition?.percent_used || 0).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>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum log de banco de dados disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: GED */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="ged">
|
|
||||||
|
|
||||||
{/* Verifica se há dados disponíveis do banco */}
|
|
||||||
{logGed?.data ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
|
|
||||||
{/* Badge com data e hora do log do banco */}
|
|
||||||
<div>
|
|
||||||
{(() => {
|
|
||||||
const { formattedDate, formattedTime, isOutdated } =
|
|
||||||
formatLogDateTime(logGed.data.data, logGed.data.hora);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Badge
|
|
||||||
variant={isOutdated ? "warning" : "outline"}
|
|
||||||
className={`ml-2 ${
|
|
||||||
isOutdated
|
|
||||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Data do log: {formattedDate} às {formattedTime}
|
|
||||||
|
|
||||||
{isOutdated && (
|
|
||||||
<span>
|
|
||||||
- Atenção: Log do GED desatualizado
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Badge>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Linha de cards principais */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
{/* Card: Partição do Banco */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Partição do GED</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p><strong>Unidade:</strong> {logGed?.data?.ged?.partition.unit}</p>
|
|
||||||
<p><strong>Espaço Total:</strong> {logGed?.data.ged?.partition.total_disk_space}</p>
|
|
||||||
<p><strong>Espaço Usado:</strong> {logGed?.data.ged?.partition.total_used_disk_space}</p>
|
|
||||||
<p><strong>Espaço Livre:</strong> {logGed?.data.ged?.partition.total_free_disk_space}</p>
|
|
||||||
<p>
|
|
||||||
<strong>Uso (%):</strong>{" "}
|
|
||||||
<Badge
|
|
||||||
variant={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>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum log de GED disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: Informação do disco do servidor */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="disk">
|
|
||||||
{logDisk?.data ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
|
|
||||||
{/* Badge com data e hora do log do disco */}
|
|
||||||
<div>
|
|
||||||
{(() => {
|
|
||||||
const { formattedDate, formattedTime, isOutdated } =
|
|
||||||
formatLogDateTime(logDisk.data.data, logDisk.data.hora);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
variant={isOutdated ? "warning" : "outline"}
|
|
||||||
className={`ml-2 ${
|
|
||||||
isOutdated
|
|
||||||
? "bg-yellow-200 text-yellow-800 border-yellow-400"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Data do log: {formattedDate} às {formattedTime}
|
|
||||||
{isOutdated && (
|
|
||||||
<span> - Atenção: Log do disco desatualizado</span>
|
|
||||||
)}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tabela com as partições/discos encontrados */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Unidades de Disco</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<ScrollArea className="h-[150px] w-full rounded-md border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="text-center">Unidade</TableHead>
|
|
||||||
<TableHead className="text-center">Capacidade</TableHead>
|
|
||||||
<TableHead className="text-center">Disponível</TableHead>
|
|
||||||
<TableHead className="text-center">Utilizado</TableHead>
|
|
||||||
<TableHead className="text-center">Disponível (%)</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
|
|
||||||
<TableRow key={index}>
|
|
||||||
<TableCell className="text-center font-medium">{unit}</TableCell>
|
|
||||||
<TableCell className="text-center">{convertMBtoGB(info.capacidade)}</TableCell>
|
|
||||||
<TableCell className="text-center">{convertMBtoGB(info.disponivel)}</TableCell>
|
|
||||||
<TableCell className="text-center">{convertMBtoGB(info.utilizados)}</TableCell>
|
|
||||||
<TableCell className="text-center">
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
info.disponivel_percentual < 20
|
|
||||||
? "destructive"
|
|
||||||
: "outline"
|
|
||||||
}
|
|
||||||
className={`${
|
|
||||||
info.disponivel_percentual < 20
|
|
||||||
? "bg-red-200 text-red-800 border-red-400"
|
|
||||||
: "bg-green-100 text-green-800 border-green-400"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{info.disponivel_percentual}%
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</ScrollArea>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{Object.entries(logDisk.data.disk || {}).map(([unit, info]: any, index) => (
|
|
||||||
<div key={index} className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
{/* Card: Gráfico de Pizza mostrando espaço usado vs livre */}
|
|
||||||
{renderDiskPieChartsFromJson(logDisk.data.disk)}
|
|
||||||
|
|
||||||
{/* Card: Gráfico de Barras mostrando uso da partição */}
|
|
||||||
{renderDiskBarChartsFromJson(logDisk.data.disk)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum log do disco(s) disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: Informação do Backup */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="backup">
|
|
||||||
{logBackup?.data ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
{/* Badge com data do backup */}
|
|
||||||
<div>
|
|
||||||
{(() => {
|
|
||||||
const { formattedDate, formattedTime, isOutdated } =
|
|
||||||
formatLogDateTime(logBackup?.data, logBackup?.hora);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
variant={isOutdated ? "warning" : "outline"}
|
|
||||||
className={`ml-2 ${isOutdated ? "bg-yellow-200 text-yellow-800 border-yellow-400" : ""}`}
|
|
||||||
>
|
|
||||||
Data do log: {formattedDate} às {formattedTime}
|
|
||||||
{isOutdated && <span> - Atenção: Log do Backup desatualizado</span>}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Card com tabela de backups */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Arquivos de Backup</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
|
|
||||||
{/** Lista os backups mais recentes */}
|
|
||||||
{logBackup?.latest_backups && (
|
|
||||||
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg mb-4">
|
|
||||||
<h2 className="text-lg font-semibold text-blue-800">
|
|
||||||
Backup mais recente ({logBackup.latest_backups.data})
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<table className="w-full text-sm mt-2 border">
|
|
||||||
<thead className="bg-blue-100">
|
|
||||||
<tr>
|
|
||||||
<th className="px-3 py-2 text-left">Arquivo</th>
|
|
||||||
<th className="px-3 py-2 text-left">Hora</th>
|
|
||||||
<th className="px-3 py-2 text-left">Tamanho</th>
|
|
||||||
<th className="px-3 py-2 text-left">Caminho</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{logBackup.latest_backups.arquivos.map((item) => (
|
|
||||||
<tr key={item.nome_arquivo} className="border-t">
|
|
||||||
<td className="px-3 py-1">{item.nome_arquivo}</td>
|
|
||||||
<td className="px-3 py-1">{item.dados.hora}</td>
|
|
||||||
<td className="px-3 py-1">{item.dados.tamanho}</td>
|
|
||||||
<td className="px-3 py-1">{item.dados.caminho}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ScrollArea className="h-[350px] w-full rounded-md border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="text-center">Arquivo</TableHead>
|
|
||||||
<TableHead className="text-center">Dia</TableHead>
|
|
||||||
<TableHead className="text-center">Data</TableHead>
|
|
||||||
<TableHead className="text-center">Hora</TableHead>
|
|
||||||
<TableHead className="text-center">Caminho</TableHead>
|
|
||||||
<TableHead className="text-center">Período (dias)</TableHead>
|
|
||||||
<TableHead className="text-center">Tamanho</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{Object.entries(logBackup?.backup || {}).map(([fileName, details]) => (
|
|
||||||
<TableRow key={fileName}>
|
|
||||||
<TableCell className="text-center">{fileName}</TableCell>
|
|
||||||
<TableCell className="text-center text-sm">{details?.dia}</TableCell>
|
|
||||||
<TableCell className="text-center">{details?.data}</TableCell>
|
|
||||||
<TableCell className="text-center">{details?.hora}</TableCell>
|
|
||||||
<TableCell className="text-center">{details?.caminho}</TableCell>
|
|
||||||
<TableCell className="text-center">{details?.periodo}</TableCell>
|
|
||||||
<TableCell className="text-center">{details?.tamanho}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</ScrollArea>
|
|
||||||
Total de arquivos: {Object.keys(logBackup?.backup || {}).length}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum log de backup disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
|
|
||||||
{/* ===================================================== */}
|
|
||||||
{/* Aba: Avisos (Warnings) */}
|
|
||||||
{/* ===================================================== */}
|
|
||||||
<TabsContent value="warning">
|
|
||||||
{logWarning?.data?.warning && logWarning.data.warning.length > 0 ? (
|
|
||||||
<div className="mt-4 space-y-6">
|
|
||||||
|
|
||||||
{/* Cabeçalho com data e hora */}
|
|
||||||
<div>
|
|
||||||
{(() => {
|
|
||||||
const { formattedDate, formattedTime, isOutdated } =
|
|
||||||
formatLogDateTime(logWarning?.data.data, logWarning?.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 de Avisos desatualizado</span>}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Card de lista de avisos */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Lista de Avisos</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<ScrollArea className="h-[400px] w-full rounded-md border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="text-center w-[80px]">#</TableHead>
|
|
||||||
<TableHead>Mensagem</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{logWarning.data.warning.map((msg: string, index: number) => (
|
|
||||||
<TableRow key={index}>
|
|
||||||
<TableCell className="text-center">{index + 1}</TableCell>
|
|
||||||
<TableCell className="text-sm text-gray-800">{msg}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</ScrollArea>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="p-4 text-gray-500 italic">
|
|
||||||
Nenhum aviso disponível para este cliente.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from 'react';
|
|
||||||
|
|
||||||
// Componentes de UI Genéricos
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
|
||||||
import Loading from '@/shared/components/loading/loading';
|
|
||||||
import Header from '@/shared/components/structure/Header';
|
|
||||||
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
|
||||||
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
|
|
||||||
|
|
||||||
// Componentes Específicos
|
|
||||||
import ClientTable from '@/packages/administrativo/components/Client/ClientTable';
|
|
||||||
import ClientForm, { ClientFormData } from '@/packages/administrativo/components/Client/ClientForm';
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import { useClientIndexHook } from '@/packages/administrativo/hooks/Client/useClientIndexHook';
|
|
||||||
import { useClientSaveHook } from '@/packages/administrativo/hooks/Client/useClientSaveHook';
|
|
||||||
import { useClientDeleteHook } from '@/packages/administrativo/hooks/Client/useClientDeleteHook'; // Hook solicitado
|
|
||||||
|
|
||||||
// Interface
|
|
||||||
import { ClientInterface } from '@/packages/administrativo/interfaces/Client/ClientInterface';
|
|
||||||
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
|
|
||||||
|
|
||||||
// Dados iniciais
|
|
||||||
const initialClient: ClientInterface = {
|
|
||||||
client_id: 0,
|
|
||||||
cns: '',
|
|
||||||
name: '',
|
|
||||||
date_register: new Date().toISOString(),
|
|
||||||
state: '',
|
|
||||||
city: '',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
responsible: '',
|
|
||||||
consultant: '',
|
|
||||||
type_contract: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function ClientsPage() {
|
|
||||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
|
||||||
|
|
||||||
const { clients, fetchClients } = useClientIndexHook();
|
|
||||||
|
|
||||||
const { saveClient } = useClientSaveHook();
|
|
||||||
|
|
||||||
// 2. Uso do hook de exclusão solicitado
|
|
||||||
const { removeClient } = useClientDeleteHook();
|
|
||||||
|
|
||||||
// Estados da página
|
|
||||||
const [selectedClient, setSelectedClient] = useState<ClientInterface | null>(null);
|
|
||||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
|
||||||
|
|
||||||
// Estado para armazenar quem será excluído
|
|
||||||
const [clientToDelete, setClientToDelete] = useState<ClientInterface | null>(null);
|
|
||||||
|
|
||||||
// Hook do modal de confirmação
|
|
||||||
const {
|
|
||||||
isOpen: isConfirmOpen,
|
|
||||||
openDialog: openConfirmDialog,
|
|
||||||
closeDialog: closeConfirmDialog,
|
|
||||||
} = useConfirmDialog();
|
|
||||||
|
|
||||||
// --- Lógica de Formulário ---
|
|
||||||
const handleOpenForm = useCallback((client: ClientInterface | null) => {
|
|
||||||
setSelectedClient(client || initialClient);
|
|
||||||
setIsFormOpen(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleCloseForm = useCallback(() => {
|
|
||||||
setSelectedClient(null);
|
|
||||||
setIsFormOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSave = useCallback(
|
|
||||||
async (formData: ClientFormData) => {
|
|
||||||
setButtonIsLoading(true);
|
|
||||||
// Casting necessário pois o form usa z.input e o hook espera a interface completa
|
|
||||||
await saveClient(formData as unknown as ClientInterface);
|
|
||||||
setButtonIsLoading(false);
|
|
||||||
setIsFormOpen(false);
|
|
||||||
fetchClients();
|
|
||||||
},
|
|
||||||
[saveClient, fetchClients]
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- Lógica de Exclusão (Nova) ---
|
|
||||||
|
|
||||||
// Passo 1: O usuário clica em "Excluir" na tabela
|
|
||||||
const handleRequestDelete = useCallback(
|
|
||||||
(client: ClientInterface) => {
|
|
||||||
setClientToDelete(client); // Guarda quem será excluído
|
|
||||||
openConfirmDialog(); // Abre o modal
|
|
||||||
},
|
|
||||||
[openConfirmDialog],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Passo 2: O usuário confirma no Modal
|
|
||||||
const handleConfirmDelete = useCallback(async () => {
|
|
||||||
if (!clientToDelete) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Chama o seu hook de exclusão
|
|
||||||
await removeClient(clientToDelete);
|
|
||||||
|
|
||||||
// Atualiza a lista
|
|
||||||
await fetchClients();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro ao excluir", error);
|
|
||||||
} finally {
|
|
||||||
setClientToDelete(null);
|
|
||||||
closeConfirmDialog();
|
|
||||||
}
|
|
||||||
}, [clientToDelete, removeClient, fetchClients, closeConfirmDialog]);
|
|
||||||
|
|
||||||
// Busca inicial
|
|
||||||
useEffect(() => {
|
|
||||||
fetchClients();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Loading inicial
|
|
||||||
if (!clients) {
|
|
||||||
return <Loading type={2} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Header
|
|
||||||
title={'Clientes'}
|
|
||||||
description={'Gerenciamento de Clientes do Sistema'}
|
|
||||||
buttonText={'Novo Cliente'}
|
|
||||||
buttonAction={() => handleOpenForm(null)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
<ClientTable
|
|
||||||
data={clients}
|
|
||||||
onEdit={handleOpenForm}
|
|
||||||
onDelete={handleRequestDelete} // Passamos a função que abre o modal
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Componente de Confirmação que você enviou */}
|
|
||||||
<ConfirmDialog
|
|
||||||
isOpen={isConfirmOpen}
|
|
||||||
title="Confirmar exclusão"
|
|
||||||
description="Esta ação removerá o cliente permanentemente do sistema."
|
|
||||||
message={`Tem certeza que deseja excluir o cliente "${clientToDelete?.name}"?`}
|
|
||||||
confirmText="Sim, excluir"
|
|
||||||
cancelText="Cancelar"
|
|
||||||
onConfirm={handleConfirmDelete} // Chama a função que executa o removeClient
|
|
||||||
onCancel={closeConfirmDialog}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ClientForm
|
|
||||||
isOpen={isFormOpen}
|
|
||||||
data={selectedClient}
|
|
||||||
onClose={handleCloseForm}
|
|
||||||
onSave={handleSave}
|
|
||||||
buttonIsLoading={buttonIsLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from 'react';
|
|
||||||
|
|
||||||
// Componentes de UI Genéricos
|
|
||||||
import z from 'zod';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
|
||||||
import Loading from '@/shared/components/loading/loading';
|
|
||||||
import Header from '@/shared/components/structure/Header';
|
|
||||||
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
|
||||||
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
|
|
||||||
|
|
||||||
// Componentes de UI Específicos de Usuário
|
|
||||||
import UserTable from '@/packages/administrativo/components/User/UserTable';
|
|
||||||
import UserForm from '@/packages/administrativo/components/User/UserForm';
|
|
||||||
|
|
||||||
// Hooks Específicos de Usuário
|
|
||||||
import { useUserIndexHook } from '@/packages/administrativo/hooks/User/useUserIndexHook';
|
|
||||||
import { useUserSaveHook } from '@/packages/administrativo/hooks/User/useUserSaveHook';
|
|
||||||
import { useUserDeleteHook } from '@/packages/administrativo/hooks/User/useUserDeleteHook';
|
|
||||||
|
|
||||||
// Interface
|
|
||||||
import { UserInterface } from '@/packages/administrativo/interfaces/User/UserInterface';
|
|
||||||
import { UserSchema } from '@/packages/administrativo/schemas/User/UserSchema';
|
|
||||||
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
|
|
||||||
|
|
||||||
const initialUser: UserInterface = {
|
|
||||||
user_id: 0,
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
team: '',
|
|
||||||
position: 'string',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
user_id_create: null,
|
|
||||||
user_id_update: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function UsersPage() {
|
|
||||||
|
|
||||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
|
||||||
// 1. Hooks de dados para Usuário
|
|
||||||
const { usuarios, fetchUsuarios } = useUserIndexHook();
|
|
||||||
const { saveUser } = useUserSaveHook();
|
|
||||||
const { removeUser } = useUserDeleteHook(); // Presumindo que o hook existe e expõe `removeUser`
|
|
||||||
|
|
||||||
// 2. Estados da página
|
|
||||||
const [selectedUser, setSelectedUser] = useState<UserInterface | null>(null);
|
|
||||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
|
||||||
const [userToDelete, setUserToDelete] = useState<UserInterface | null>(null);
|
|
||||||
|
|
||||||
// Hook do modal de confirmação
|
|
||||||
const {
|
|
||||||
isOpen: isConfirmOpen,
|
|
||||||
openDialog: openConfirmDialog,
|
|
||||||
handleCancel,
|
|
||||||
} = useConfirmDialog();
|
|
||||||
|
|
||||||
// 3. Funções de manipulação do formulário
|
|
||||||
const handleOpenForm = useCallback((user: UserInterface | null) => {
|
|
||||||
setSelectedUser(user);
|
|
||||||
setIsFormOpen(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleCloseForm = useCallback(() => {
|
|
||||||
setSelectedUser(null);
|
|
||||||
setIsFormOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 4. Função para salvar (criar ou editar)
|
|
||||||
const handleSave = useCallback(
|
|
||||||
async (formData: UserInterface) => {
|
|
||||||
|
|
||||||
setButtonIsLoading(true);
|
|
||||||
|
|
||||||
await saveUser(formData);
|
|
||||||
|
|
||||||
setButtonIsLoading(false);
|
|
||||||
|
|
||||||
setIsFormOpen(false);
|
|
||||||
|
|
||||||
fetchUsuarios(); // Atualiza a lista de usuários
|
|
||||||
},
|
|
||||||
[saveUser, fetchUsuarios, handleCloseForm],
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5. Funções para exclusão
|
|
||||||
const handleConfirmDelete = useCallback(
|
|
||||||
(user: UserInterface) => {
|
|
||||||
setUserToDelete(user);
|
|
||||||
openConfirmDialog();
|
|
||||||
},
|
|
||||||
[openConfirmDialog],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDelete = useCallback(async () => {
|
|
||||||
if (!userToDelete) return;
|
|
||||||
|
|
||||||
await removeUser(userToDelete); // Chama o hook de remoção
|
|
||||||
await fetchUsuarios(); // Atualiza a lista
|
|
||||||
setUserToDelete(null); // Limpa o estado
|
|
||||||
handleCancel(); // Fecha o modal de confirmação
|
|
||||||
}, [userToDelete, fetchUsuarios, handleCancel]);
|
|
||||||
|
|
||||||
// 6. Busca inicial dos dados
|
|
||||||
useEffect(() => {
|
|
||||||
fetchUsuarios();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 7. Renderização condicional de loading
|
|
||||||
if (usuarios?.length == 0) {
|
|
||||||
return <Loading type={2} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Renderização da página
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Header
|
|
||||||
title={'Usuários'}
|
|
||||||
description={'Gerenciamento de Usuários do Sistema'}
|
|
||||||
buttonText={'Novo Usuário'}
|
|
||||||
buttonAction={(data) => handleOpenForm(data = initialUser)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
<UserTable
|
|
||||||
data={usuarios}
|
|
||||||
onEdit={handleOpenForm}
|
|
||||||
onDelete={handleConfirmDelete}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<ConfirmDialog
|
|
||||||
isOpen={isConfirmOpen}
|
|
||||||
title="Confirmar exclusão"
|
|
||||||
description="Esta ação não pode ser desfeita."
|
|
||||||
message={`Deseja realmente excluir o usuário "${userToDelete?.name}"?`}
|
|
||||||
confirmText="Sim, excluir"
|
|
||||||
cancelText="Cancelar"
|
|
||||||
onConfirm={handleDelete}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UserForm
|
|
||||||
isOpen={isFormOpen}
|
|
||||||
data={selectedUser}
|
|
||||||
onClose={handleCloseForm}
|
|
||||||
onSave={handleSave}
|
|
||||||
buttonIsLoading={buttonIsLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -28,7 +28,7 @@ const geistMono = Geist_Mono({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Monitoring App - Orius Tecnologia',
|
title: 'SAAS - Orius Tecnologia',
|
||||||
description: 'Evolução tecnológica com toque humano',
|
description: 'Evolução tecnológica com toque humano',
|
||||||
icons: {
|
icons: {
|
||||||
icon: '/images/favicon.ico',
|
icon: '/images/favicon.ico',
|
||||||
|
|
@ -56,14 +56,14 @@ export default function RootLayout({
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<BreadcrumbItem className="hidden md:block">
|
<BreadcrumbItem className="hidden md:block">
|
||||||
<BreadcrumbLink href="/">
|
<BreadcrumbLink href="#">
|
||||||
Orius Tecnologia
|
Building Your Application
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator className="hidden md:block" />
|
<BreadcrumbSeparator className="hidden md:block" />
|
||||||
<BreadcrumbItem>
|
<BreadcrumbItem>
|
||||||
<BreadcrumbPage>
|
<BreadcrumbPage>
|
||||||
Monitoramento de Servidores
|
Data Fetching
|
||||||
</BreadcrumbPage>
|
</BreadcrumbPage>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
</BreadcrumbList>
|
</BreadcrumbList>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,3 @@
|
||||||
'use client'; // Necessário, pois usaremos hooks do React
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
return <div></div>;
|
||||||
|
}
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
//Redireciona para a tela de clientes
|
|
||||||
router.replace('/administrativo/clientes');
|
|
||||||
|
|
||||||
}, [router]);
|
|
||||||
|
|
||||||
return null; // Página em branco (não renderiza nada)
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { LoginForm } from '@/packages/administrativo/components/User/UserLoginForm';
|
import { LoginForm } from '@/packages/administrativo/components/GUsuario/GUsuarioLoginForm';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { jwtDecode } from 'jwt-decode';
|
|
||||||
import GetSigla from '@/shared/actions/text/GetSigla';
|
|
||||||
import UserAuthenticatedInterface from '@/shared/interfaces/UserAuthenticatedInterface';
|
|
||||||
|
|
||||||
interface JwtPayload {
|
|
||||||
id: string;
|
|
||||||
iat: number;
|
|
||||||
exp: number;
|
|
||||||
data?: UserAuthenticatedInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
|
||||||
try {
|
|
||||||
const token = req.cookies.get('access_token')?.value;
|
|
||||||
if (!token) return NextResponse.json(null);
|
|
||||||
|
|
||||||
const decoded: JwtPayload = jwtDecode(token);
|
|
||||||
if (decoded.data && typeof decoded.data === 'string') {
|
|
||||||
decoded.data = JSON.parse(decoded.data);
|
|
||||||
}
|
|
||||||
if (decoded.data) {
|
|
||||||
decoded.data.sigla = GetSigla(decoded.data.nome || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(decoded);
|
|
||||||
} catch (err) {
|
|
||||||
return NextResponse.json(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exibe um selo (badge) visual para status do usuário: Ativo / Inativo
|
|
||||||
*/
|
|
||||||
export function StatusBadge({ status }: { status: SituacoesEnum }) {
|
|
||||||
const isActive = status === 'A';
|
|
||||||
const baseClasses = 'text-xs font-medium px-2.5 py-0.5 rounded-sm me-2';
|
|
||||||
const activeClasses = 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300';
|
|
||||||
const inactiveClasses = 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span className={`${baseClasses} ${isActive ? activeClasses : inactiveClasses}`}>
|
|
||||||
{isActive ? 'Ativo' : 'Inativo'}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
import { NavMain } from '@/components/nav-main';
|
import { NavMain } from '@/components/nav-main';
|
||||||
|
import { NavProjects } from '@/components/nav-projects';
|
||||||
import { NavUser } from '@/components/nav-user';
|
import { NavUser } from '@/components/nav-user';
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
|
|
@ -20,7 +21,7 @@ import {
|
||||||
SidebarRail,
|
SidebarRail,
|
||||||
} from '@/components/ui/sidebar';
|
} from '@/components/ui/sidebar';
|
||||||
|
|
||||||
import useUserGetJWTHook from '@/shared/hooks/auth/useUserGetJWTHook';
|
import useGUsuarioGetJWTHook from '@/shared/hooks/auth/useGUsuarioGetJWTHook';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
// This is sample data.
|
// This is sample data.
|
||||||
|
|
@ -34,8 +35,8 @@ const data = {
|
||||||
isActive: false,
|
isActive: false,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: 'Clientes',
|
title: 'Dashboard',
|
||||||
url: '/administrativo/clientes/',
|
url: '/dashboard/',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -47,15 +48,16 @@ const data = {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: 'Usuários',
|
title: 'Usuários',
|
||||||
url: '/administrativo/usuarios/',
|
url: '/usuarios/',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
projects: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
const { userAuthenticated } = useUserGetJWTHook();
|
const { userAuthenticated } = useGUsuarioGetJWTHook();
|
||||||
return (
|
return (
|
||||||
<Sidebar collapsible="icon" {...props}>
|
<Sidebar collapsible="icon" {...props}>
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
|
|
@ -75,10 +77,10 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-0.5 leading-none">
|
<div className="flex flex-col gap-0.5 leading-none">
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
Monitoring App
|
Orius Tecnologia
|
||||||
</span>
|
</span>
|
||||||
<span className="">
|
<span className="">
|
||||||
v1.0
|
25.9.1
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -88,6 +90,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<NavMain items={data.navMain} />
|
<NavMain items={data.navMain} />
|
||||||
|
<NavProjects projects={data.projects} />
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>
|
<SidebarFooter>
|
||||||
{userAuthenticated?.data ? <NavUser user={userAuthenticated.data} /> : 'Carregando...'}
|
{userAuthenticated?.data ? <NavUser user={userAuthenticated.data} /> : 'Carregando...'}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export function NavMain({
|
||||||
>
|
>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<SidebarMenuButton tooltip={item.title} className='cursor-pointer'>
|
<SidebarMenuButton tooltip={item.title}>
|
||||||
{item.icon && <item.icon />}
|
{item.icon && <item.icon />}
|
||||||
|
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ import {
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from '@/components/ui/sidebar';
|
} from '@/components/ui/sidebar';
|
||||||
|
|
||||||
import UserAuthenticatedInterface from '@/shared/interfaces/UserAuthenticatedInterface';
|
import GUsuarioAuthenticatedInterface from '@/shared/interfaces/GUsuarioAuthenticatedInterface';
|
||||||
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
||||||
import { useUserLogoutHook } from '@/packages/administrativo/hooks/User/useUserLogoutHook';
|
import { useGUsuarioLogoutHook } from '@/packages/administrativo/hooks/GUsuario/useGUsuarioLogoutHook';
|
||||||
import { use, useCallback, useState } from 'react';
|
import { use, useCallback, useState } from 'react';
|
||||||
|
|
||||||
export function NavUser({ user }: { user: UserAuthenticatedInterface }) {
|
export function NavUser({ user }: { user: GUsuarioAuthenticatedInterface }) {
|
||||||
// Hook para encerrar sessão
|
// Hook para encerrar sessão
|
||||||
const { logoutUsuario } = useUserLogoutHook();
|
const { logoutUsuario } = useGUsuarioLogoutHook();
|
||||||
|
|
||||||
// Controle de exibição do formulário de confirmação
|
// Controle de exibição do formulário de confirmação
|
||||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
|
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
|
||||||
|
|
@ -37,12 +37,10 @@ export function NavUser({ user }: { user: UserAuthenticatedInterface }) {
|
||||||
setIsConfirmOpen(true);
|
setIsConfirmOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Consifrma ação de log-out
|
|
||||||
const handleLogoutConfirm = useCallback(async () => {
|
const handleLogoutConfirm = useCallback(async () => {
|
||||||
logoutUsuario();
|
logoutUsuario();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Cancela ação de log-out
|
|
||||||
const handleLogoutCancel = useCallback(async () => {
|
const handleLogoutCancel = useCallback(async () => {
|
||||||
setIsConfirmOpen(false);
|
setIsConfirmOpen(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -101,18 +99,18 @@ export function NavUser({ user }: { user: UserAuthenticatedInterface }) {
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
{/* <DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem className="cursor-pointer">
|
<DropdownMenuItem className="cursor-pointer">
|
||||||
<Sparkles />
|
<Sparkles />
|
||||||
Configurações
|
Configurações
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
|
|
||||||
<DropdownMenuSeparator /> */}
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
<DropdownMenuItem className="cursor-pointer" onClick={() => handleConfirmOpen()}>
|
<DropdownMenuItem className="cursor-pointer" onClick={() => handleConfirmOpen()}>
|
||||||
<LogOut />
|
<LogOut />
|
||||||
Sair do sistema
|
Log out
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
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 }
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
"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 }
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { Loader2Icon } from "lucide-react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
|
||||||
return (
|
|
||||||
<Loader2Icon
|
|
||||||
role="status"
|
|
||||||
aria-label="Loading"
|
|
||||||
className={cn("size-4 animate-spin", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Spinner }
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"state": "go",
|
|
||||||
"api": {
|
|
||||||
"url": "https://monitoring-api.oriustecnologia.com/",
|
|
||||||
"prefix": "api/v1/",
|
|
||||||
"content_type": "application/json"
|
|
||||||
},
|
|
||||||
"api_debit": "https://admin.oriustecnologia.com/router.php"
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { NextResponse, type MiddlewareConfig, type NextRequest } from 'next/serv
|
||||||
const publicRoutes = [
|
const publicRoutes = [
|
||||||
{ path: '/login', whenAuthenticated: 'redirect' },
|
{ path: '/login', whenAuthenticated: 'redirect' },
|
||||||
{ path: '/register', whenAuthenticated: 'redirect' },
|
{ path: '/register', whenAuthenticated: 'redirect' },
|
||||||
|
{ path: '/', whenAuthenticated: 'next' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const ROOT_PATH = '/login'; // url raiz
|
const ROOT_PATH = '/login'; // url raiz
|
||||||
|
|
@ -13,44 +14,37 @@ export function middleware(request: NextRequest) {
|
||||||
const publicRoute = publicRoutes.find((route) => route.path === path);
|
const publicRoute = publicRoutes.find((route) => route.path === path);
|
||||||
const authToken = request.cookies.get('access_token');
|
const authToken = request.cookies.get('access_token');
|
||||||
|
|
||||||
// Evita loop — se já estiver na página de login, não redireciona novamente
|
// 1. Não autenticado e rota pública → segue normal
|
||||||
if (path === ROOT_PATH) {
|
|
||||||
return NextResponse.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 Não autenticado e rota pública → segue normal
|
|
||||||
if (!authToken && publicRoute) {
|
if (!authToken && publicRoute) {
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 Não autenticado e rota privada → redireciona para login
|
// 2. Não autenticado e rota privada → redireciona para raiz
|
||||||
if (!authToken && !publicRoute) {
|
if (!authToken && !publicRoute) {
|
||||||
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 Autenticado em rota pública com flag "redirect" → vai para raiz
|
// 3. Autenticado em rota pública com flag "redirect" → vai para raiz
|
||||||
if (authToken && publicRoute && publicRoute.whenAuthenticated === 'redirect') {
|
if (authToken && publicRoute && publicRoute.whenAuthenticated === 'redirect') {
|
||||||
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4 Autenticado em rota privada → valida token
|
// 4. Autenticado em rota privada → valida token
|
||||||
if (authToken && !publicRoute) {
|
if (authToken && !publicRoute) {
|
||||||
const decoded: any = jwt.decode(authToken.value);
|
const decoded: any = jwt.decode(authToken.value);
|
||||||
|
|
||||||
// Token inválido → redireciona (uma vez)
|
|
||||||
if (!decoded || !decoded.exp) {
|
if (!decoded || !decoded.exp) {
|
||||||
console.log('Token inválido');
|
console.log('Token inválido');
|
||||||
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifica expiração
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
if (decoded.exp <= currentTime) {
|
if (decoded.exp <= currentTime) {
|
||||||
console.log('Token expirado');
|
console.log('Token expirado');
|
||||||
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token válido → segue
|
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,294 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import { Label } from '@/components/ui/label';
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/ui/dialog';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
|
||||||
|
|
||||||
// Enum
|
|
||||||
export enum SituacoesEnum {
|
|
||||||
ATIVO = 'A',
|
|
||||||
INATIVO = 'I',
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Schema
|
|
||||||
const clientSchema = z.object({
|
|
||||||
client_id: z.number().optional(),
|
|
||||||
name: z.string().min(3, 'O nome é obrigatório'),
|
|
||||||
// Permitimos string vazia transformando em null ou undefined se necessário,
|
|
||||||
// mas para formulários de texto, manter string vazia costuma ser mais fácil.
|
|
||||||
cns: z.string().min(3, 'O CNS é obrigatório'),
|
|
||||||
state: z.string().max(2).optional().nullable(),
|
|
||||||
city: z.string().optional().nullable(),
|
|
||||||
responsible: z.string().optional().nullable(),
|
|
||||||
consultant: z.string().optional().nullable(),
|
|
||||||
type_contract: z.string().optional().nullable(),
|
|
||||||
|
|
||||||
// Status com valor padrão
|
|
||||||
status: z.nativeEnum(SituacoesEnum).default(SituacoesEnum.ATIVO),
|
|
||||||
});
|
|
||||||
|
|
||||||
// AQUI: Usamos z.input para evitar conflito de tipos com campos que têm default()
|
|
||||||
export type ClientFormData = z.input<typeof clientSchema>;
|
|
||||||
|
|
||||||
interface ClientFormProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
data: any | null;
|
|
||||||
onClose: () => void;
|
|
||||||
onSave: (data: ClientFormData) => void;
|
|
||||||
buttonIsLoading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ClientForm({
|
|
||||||
isOpen,
|
|
||||||
data,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
buttonIsLoading,
|
|
||||||
}: ClientFormProps) {
|
|
||||||
|
|
||||||
const form = useForm<ClientFormData>({
|
|
||||||
resolver: zodResolver(clientSchema),
|
|
||||||
defaultValues: {
|
|
||||||
client_id: 0,
|
|
||||||
name: '',
|
|
||||||
cns: '',
|
|
||||||
state: '',
|
|
||||||
city: '',
|
|
||||||
responsible: '',
|
|
||||||
consultant: '',
|
|
||||||
type_contract: '',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset do formulário ao abrir/fechar ou mudar dados
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
if (data) {
|
|
||||||
form.reset({
|
|
||||||
...data,
|
|
||||||
// Garante que nulos venham como strings vazias ou mantenham o valor
|
|
||||||
cns: data.cns ?? '',
|
|
||||||
state: data.state ?? '',
|
|
||||||
city: data.city ?? '',
|
|
||||||
responsible: data.responsible ?? '',
|
|
||||||
consultant: data.consultant ?? '',
|
|
||||||
type_contract: data.type_contract ?? '',
|
|
||||||
status: data.status || SituacoesEnum.ATIVO,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
form.reset({
|
|
||||||
client_id: 0,
|
|
||||||
name: '',
|
|
||||||
cns: '',
|
|
||||||
state: '',
|
|
||||||
city: '',
|
|
||||||
responsible: '',
|
|
||||||
consultant: '',
|
|
||||||
type_contract: '',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isOpen, data, form]);
|
|
||||||
|
|
||||||
const handleFormSubmit = (values: ClientFormData) => {
|
|
||||||
// Ao salvar, o Zod já garantiu que o 'status' existe (por causa do default),
|
|
||||||
// mesmo que o tipo ClientFormData diga que é opcional.
|
|
||||||
onSave(values);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenChange = (open: boolean) => {
|
|
||||||
if (!open) {
|
|
||||||
form.reset();
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
|
|
||||||
<DialogContent className="sm:max-w-[600px]">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
{data?.client_id ? 'Editar Cliente' : 'Novo Cliente'}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Preencha os campos abaixo.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<Form {...form}>
|
|
||||||
<form onSubmit={form.handleSubmit(handleFormSubmit)} className="space-y-4">
|
|
||||||
|
|
||||||
{/* Linha 1: Nome e Status */}
|
|
||||||
<div className="flex gap-4 items-start">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex-1">
|
|
||||||
<FormLabel>Nome / Razão Social</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Digite o nome" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Checkbox de Status */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="status"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0 mt-8 border p-2 rounded-md h-10">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value === SituacoesEnum.ATIVO}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
field.onChange(checked ? SituacoesEnum.ATIVO : SituacoesEnum.INATIVO);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<div className="leading-none">
|
|
||||||
<Label>Ativo?</Label>
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Linha 2: CNS e Tipo Contrato */}
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="cns"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>CNS</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
{/* O '?? ""' previne erro de uncontrolled component se o valor for null */}
|
|
||||||
<Input placeholder="CNS" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="type_contract"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Tipo de Contrato</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Ex: Mensal" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Linha 3: Cidade e UF */}
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="city"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="col-span-2">
|
|
||||||
<FormLabel>Cidade</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Cidade" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="state"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>UF</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="UF" maxLength={2} {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Linha 4: Responsável e Consultor */}
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="responsible"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Responsável</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Responsável" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="consultant"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Consultor</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Consultor" {...field} value={field.value ?? ''} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter className="mt-4">
|
|
||||||
<Button type="button" variant="outline" onClick={onClose}>
|
|
||||||
Cancelar
|
|
||||||
</Button>
|
|
||||||
<LoadingButton
|
|
||||||
type="submit"
|
|
||||||
text="Salvar"
|
|
||||||
textLoading="Salvando..."
|
|
||||||
loading={buttonIsLoading}
|
|
||||||
/>
|
|
||||||
</DialogFooter>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,200 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
// Importei o ícone Trash2 (lixeira)
|
|
||||||
import { ChartPie, EllipsisIcon, PencilIcon, Trash2 } from 'lucide-react';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableFooter,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table';
|
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
|
||||||
import { StatusBadge } from '@/components/StatusBadge';
|
|
||||||
import FinanceStatusDialog from '@/shared/components/finance/FinanceStatusDialog';
|
|
||||||
|
|
||||||
// Utils & Interfaces
|
|
||||||
import { ClientTableInterface } from '../../interfaces/Client/ClienteTableInterface';
|
|
||||||
import { CheckLiberationHelper } from '@/shared/utils/CheckLiberationHelper';
|
|
||||||
|
|
||||||
interface FinancialStatus {
|
|
||||||
status: string;
|
|
||||||
message: string;
|
|
||||||
expired_count: number;
|
|
||||||
next_due_count?: number;
|
|
||||||
details: {
|
|
||||||
type: string;
|
|
||||||
description: string;
|
|
||||||
reference: string;
|
|
||||||
days: number;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ClientTable({ data, pagination, onEdit, onDelete }: ClientTableInterface) {
|
|
||||||
const router = useRouter();
|
|
||||||
const [financialStatus, setFinancialStatus] = useState<Record<number, FinancialStatus>>({});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!data || !Array.isArray(data)) return;
|
|
||||||
|
|
||||||
const fetchStatus = async () => {
|
|
||||||
const newStatuses: Record<number, FinancialStatus> = {};
|
|
||||||
await Promise.all(
|
|
||||||
data.map(async (client) => {
|
|
||||||
if (!client.cns) {
|
|
||||||
newStatuses[client.client_id] = {
|
|
||||||
status: 'indefinido',
|
|
||||||
message: 'CNS não cadastrado',
|
|
||||||
expired_count: 0,
|
|
||||||
details: []
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await CheckLiberationHelper(client.cns);
|
|
||||||
newStatuses[client.client_id] = {
|
|
||||||
status: res.status || 'indefinido',
|
|
||||||
message: res.message || '',
|
|
||||||
expired_count: res.expired_count || 0,
|
|
||||||
next_due_count: res.next_due_count || 0,
|
|
||||||
details: res.details || [],
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setFinancialStatus(newStatuses);
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchStatus();
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
const getFinanceColor = (status: string) => {
|
|
||||||
return status === 'em atraso' ? 'text-red-600' : 'text-green-600';
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="w-[80px]">#</TableHead>
|
|
||||||
<TableHead className="w-[100px]">Status</TableHead>
|
|
||||||
<TableHead className="w-[200px]">Financeiro</TableHead>
|
|
||||||
<TableHead>CNS</TableHead>
|
|
||||||
<TableHead>Nome</TableHead>
|
|
||||||
<TableHead className="text-right">Ações</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
<TableBody>
|
|
||||||
{Array.isArray(data) && data.length > 0 ? (
|
|
||||||
data.map((client) => {
|
|
||||||
const finStatus = financialStatus[client.client_id];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow key={client.client_id}>
|
|
||||||
<TableCell className="font-medium">{client.client_id}</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
|
||||||
<StatusBadge status={client.status} />
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
|
||||||
{finStatus ? (
|
|
||||||
<FinanceStatusDialog
|
|
||||||
triggerElement={
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`font-semibold cursor-pointer underline underline-offset-2 bg-transparent border-0 p-0 ${getFinanceColor(finStatus.status)}`}
|
|
||||||
title={finStatus.message}
|
|
||||||
>
|
|
||||||
{finStatus.status.toUpperCase()}
|
|
||||||
{finStatus.expired_count > 0 && ` (${finStatus.expired_count})`}
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
status={finStatus.status}
|
|
||||||
message={finStatus.message}
|
|
||||||
details={finStatus.details}
|
|
||||||
expired_count={finStatus.expired_count}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className="text-gray-400 opacity-50"><Spinner/></span>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>{client.cns}</TableCell>
|
|
||||||
<TableCell className="truncate max-w-[250px]" title={client.name}>
|
|
||||||
{client.name}
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="text-right">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="ghost" size="icon" className="h-8 w-8 p-0">
|
|
||||||
<EllipsisIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent side="left" align="start">
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem className="cursor-pointer" onSelect={() => onEdit(client)}>
|
|
||||||
<PencilIcon className="mr-2 h-4 w-4" />
|
|
||||||
Editar
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="cursor-pointer"
|
|
||||||
onSelect={() => router.push(`/administrativo/clientes/${client.client_id}`)}
|
|
||||||
>
|
|
||||||
<ChartPie className="mr-2 h-4 w-4" />
|
|
||||||
Dashboard
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
{/* OPÇÃO DE EXCLUIR */}
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="cursor-pointer text-red-600 focus:text-red-600 focus:bg-red-50"
|
|
||||||
onSelect={() => onDelete(client)}
|
|
||||||
>
|
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
|
||||||
Excluir
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={6} className="h-24 text-center text-muted-foreground">
|
|
||||||
Nenhum cliente encontrado.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
|
|
||||||
<TableFooter>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={6}>
|
|
||||||
Total de clientes: {pagination?.total_records ?? data?.length ?? 0}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableFooter>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -4,17 +4,40 @@ import Image from 'next/image';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
|
import z from 'zod';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import GUsuarioLoginService from '@/packages/administrativo/services/GUsuario/GUsuarioLoginService';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../../../../components/ui/form';
|
||||||
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
||||||
import { useUserFormLoginHook } from '../../hooks/User/useUserFormLoginHook';
|
import { Button } from '../../../../components/ui/button';
|
||||||
import { UserFormLoginInterface } from '../../interfaces/User/UserFormLoginInterface';
|
import { GUsuarioLoginSchema } from '@/packages/administrativo/schemas/GUsuario/GUsuarioLoginSchema';
|
||||||
|
|
||||||
/**
|
type FormValues = z.infer<typeof GUsuarioLoginSchema>;
|
||||||
* Componente de login — utiliza hook para gerenciar estado e lógica.
|
|
||||||
*/
|
export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
export function LoginForm({ className, ...props }: UserFormLoginInterface) {
|
const [loading, setLoading] = useState(false);
|
||||||
const { form, onSubmit, loading } = useUserFormLoginHook(); // Hook customizado
|
|
||||||
|
const form = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(GUsuarioLoginSchema),
|
||||||
|
defaultValues: {
|
||||||
|
login: '',
|
||||||
|
senha_api: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// onSubmit agora recebe o evento do form através do handleSubmit
|
||||||
|
const onSubmit = async (values: FormValues) => {
|
||||||
|
// Ativa o estado de loading do botão
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
// Realiza o login
|
||||||
|
await GUsuarioLoginService(values);
|
||||||
|
|
||||||
|
// Removo o estado de loading do botão
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
||||||
|
|
@ -22,18 +45,15 @@ export function LoginForm({ className, ...props }: UserFormLoginInterface) {
|
||||||
<CardContent className="grid p-0 md:grid-cols-2">
|
<CardContent className="grid p-0 md:grid-cols-2">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 p-6 md:p-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 p-6 md:p-8">
|
||||||
{/* Cabeçalho */}
|
|
||||||
<div className="mb-6 flex flex-col items-center text-center">
|
<div className="mb-6 flex flex-col items-center text-center">
|
||||||
<h1 className="text-2xl font-bold">Bem-vindo de volta!</h1>
|
<h1 className="text-2xl font-bold">Bem vindo de volta!</h1>
|
||||||
<p className="text-muted-foreground text-balance">
|
<p className="text-muted-foreground text-balance">
|
||||||
Entre na sua conta Orius Tecnologia.
|
Entre na sua conta Orius Tecnologia.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Campo: Login */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="login"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Login</FormLabel>
|
<FormLabel>Login</FormLabel>
|
||||||
|
|
@ -44,11 +64,9 @@ export function LoginForm({ className, ...props }: UserFormLoginInterface) {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Campo: Senha */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="senha_api"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Senha</FormLabel>
|
<FormLabel>Senha</FormLabel>
|
||||||
|
|
@ -59,35 +77,28 @@ export function LoginForm({ className, ...props }: UserFormLoginInterface) {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{/* Botão de loading */}
|
||||||
{/* Checkbox: Lembrar acesso */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="rememberMe"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex items-center space-x-2 cursor-pointer">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel className="text-sm font-normal cursor-pointer">Lembrar acesso</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Botão de envio com loading */}
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
text="Entrar"
|
text="Entrar"
|
||||||
textLoading="Aguarde..."
|
textLoading="Aguarde..."
|
||||||
type="submit"
|
type="submit"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
|
<div className="after:border-border relative my-4 text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||||
|
<span className="bg-card text-muted-foreground relative z-10 px-2">
|
||||||
|
Ou entre em contato
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<Button variant="outline" type="button" className="w-full">
|
||||||
|
Chamar no Whatsapp
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" type="button" className="w-full">
|
||||||
|
Chamar no Local
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
{/* Lado direito (imagem) */}
|
|
||||||
<div className="bg-brand relative hidden items-center justify-center md:flex">
|
<div className="bg-brand relative hidden items-center justify-center md:flex">
|
||||||
<Image
|
<Image
|
||||||
src="/images/logo-login.svg"
|
src="/images/logo-login.svg"
|
||||||
|
|
@ -99,12 +110,11 @@ export function LoginForm({ className, ...props }: UserFormLoginInterface) {
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Rodapé */}
|
|
||||||
<div className="text-muted-foreground text-center text-xs">
|
<div className="text-muted-foreground text-center text-xs">
|
||||||
Ao clicar você concorda com{' '}
|
Ao clicar você concorda com <a href="#">Nossos termos de serviços</a> e{' '}
|
||||||
<a href="#">Nossos termos de serviços</a> e{' '}
|
<a href="#">
|
||||||
<a href="#">Políticas de Privacidade</a>.
|
Políticas de Privacidade
|
||||||
|
</a>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import TImovelInterface from "../../interfaces/TImovel/TImovelInterface";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
EllipsisIcon,
|
||||||
|
PencilIcon,
|
||||||
|
Trash2Icon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { FormatDateTime } from "@/shared/actions/dateTime/FormatDateTime";
|
||||||
|
import { FormatCEP } from "@/shared/actions/CEP/FormatCEP";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { ImovelTipoRegistro } from "@/shared/enums/ImovelTipoRegistro";
|
||||||
|
import { SortableHeader } from "@/shared/components/dataTable/SortableHeader";
|
||||||
|
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
|
||||||
|
|
||||||
|
export default function TImovelColumns(
|
||||||
|
onEdit: (item: TImovelInterface, isEditingFormStatus: boolean) => void,
|
||||||
|
onDelete: (item: TImovelInterface, isEditingFormStatus: boolean) => void
|
||||||
|
): ColumnDef<TImovelInterface>[] {
|
||||||
|
return [
|
||||||
|
// ID
|
||||||
|
{
|
||||||
|
accessorKey: "imovel_id",
|
||||||
|
header: ({ column }) => SortableHeader("#", column),
|
||||||
|
cell: ({ row }) => Number(row.getValue("imovel_id")),
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tipo Registro
|
||||||
|
{
|
||||||
|
accessorKey: "tipo_registro",
|
||||||
|
header: ({ column }) => SortableHeader("Tipo Registro", column),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const value = row.getValue("tipo_registro") as keyof typeof ImovelTipoRegistro;
|
||||||
|
return ImovelTipoRegistro[value] ?? value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Número
|
||||||
|
{
|
||||||
|
accessorKey: "numero",
|
||||||
|
header: ({ column }) => SortableHeader("Número", column),
|
||||||
|
cell: ({ row }) => row.getValue("numero"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// UF / Cidade / Bairro
|
||||||
|
{
|
||||||
|
id: "uf_cidade_bairro",
|
||||||
|
accessorFn: (row) => row,
|
||||||
|
header: ({ column }) => SortableHeader("Cidade / UF / Bairro", column),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const imovel = row.original;
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold text-gray-900 capitalize">
|
||||||
|
{GetCapitalize(imovel.cidade)}/{imovel.uf}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-gray-500">{GetCapitalize(imovel.gtbb_descricao)}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
sortingFn: (a, b) =>
|
||||||
|
(a.original.cartorio?.toLowerCase() || "").localeCompare(
|
||||||
|
b.original.cartorio?.toLowerCase() || ""
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
// CEP
|
||||||
|
{
|
||||||
|
accessorKey: "cep",
|
||||||
|
header: ({ column }) => SortableHeader("CEP", column),
|
||||||
|
cell: ({ row }) => FormatCEP(row.getValue("cep")),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Data de Registro
|
||||||
|
{
|
||||||
|
accessorKey: "data_registro",
|
||||||
|
header: ({ column }) => SortableHeader("Cadastro", column),
|
||||||
|
cell: ({ row }) => FormatDateTime(row.getValue("data_registro")),
|
||||||
|
sortingFn: "datetime",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Ações
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
header: "Ações",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const imovel = row.original;
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<EllipsisIcon />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent side="left" align="start">
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem onSelect={() => onEdit(imovel, true)}>
|
||||||
|
<PencilIcon className="mr-2 h-4 w-4" />
|
||||||
|
Editar
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-red-600"
|
||||||
|
onSelect={() => onDelete(imovel, true)}
|
||||||
|
>
|
||||||
|
<Trash2Icon className="mr-2 h-4 w-4" />
|
||||||
|
Remover
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
385
src/packages/administrativo/components/TImovel/TImovelForm.tsx
Normal file
385
src/packages/administrativo/components/TImovel/TImovelForm.tsx
Normal file
|
|
@ -0,0 +1,385 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
|
||||||
|
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
import { CheckIcon, ChevronsUpDownIcon, HouseIcon, IdCardIcon } from 'lucide-react';
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select';
|
||||||
|
import { ImovelTipoRegistro } from '@/shared/enums/ImovelTipoRegistro';
|
||||||
|
import { ImovelTipoClasseEnum } from '@/shared/enums/ImovelTipoClasseEnum';
|
||||||
|
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
|
||||||
|
import { useGTBBairroReadHook } from '../../../../app/(protected)/(cadastros)/cadastros/_hooks/g_tb_bairro/useGTBBairroReadHook';
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import GetCapitalize from '@/shared/actions/text/GetCapitalize';
|
||||||
|
import { TImovelFormInterface } from '../../interfaces/TImovel/TImovelFormInterface';
|
||||||
|
import { useTImovelFormHook } from '../../hooks/TImovel/useTImovelFormHook';
|
||||||
|
import TImovelUnidadeUrbanoPage from '../TImovelUnidade/TImovelUnidadeUrbano/TImovelUnidadeUrbanoPage';
|
||||||
|
import TImovelUnidadeRuralPage from '../TImovelUnidade/TImovelUnidadeRural/TImovelUnidadeRuralPage';
|
||||||
|
import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
|
||||||
|
|
||||||
|
export default function TImovelForm({ isOpen, data, onClose, onSave, buttonIsLoading, tipoClasse }: TImovelFormInterface) {
|
||||||
|
|
||||||
|
const { gTBBairro, fetchGTBBairro } = useGTBBairroReadHook();
|
||||||
|
|
||||||
|
// Inicializa o react-hook-form com schema zod
|
||||||
|
const form = useTImovelFormHook();
|
||||||
|
|
||||||
|
// Atualiza o formulário quando recebe dados para edição
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
// Se existir dados, reseta o formulário com os mesmos
|
||||||
|
ResetFormIfData(form, data);
|
||||||
|
|
||||||
|
// Função sincrona para carregamento de dados
|
||||||
|
async function loadData() {
|
||||||
|
|
||||||
|
// Busca os bairros
|
||||||
|
await fetchGTBBairro();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executa a função
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
}, [data, form]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) onClose(null, false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent className="w-full max-w-full p-6 sm:max-w-3xl md:max-w-4xl lg:max-w-5xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
Imóvel Urbano
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Cadastro de imóvel urbano
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
|
||||||
|
{/* Tabs */}
|
||||||
|
<Tabs defaultValue="dadosDoImovel" className="space-y-4">
|
||||||
|
<TabsList className="flex w-full">
|
||||||
|
<TabsTrigger className="flex-1 text-center cursor-pointer" value="dadosDoImovel">
|
||||||
|
<HouseIcon className="me-1 inline" />
|
||||||
|
Dados do Imóvel
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger className="flex-1 text-center cursor-pointer" value="unidades">
|
||||||
|
<IdCardIcon className="inline" />
|
||||||
|
Unidades
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
{/* Dados do Imóvel */}
|
||||||
|
<TabsContent value="dadosDoImovel" className="space-y-4">
|
||||||
|
<div className="grid w-full grid-cols-12 gap-4">
|
||||||
|
{/* UF */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="uf"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>UF</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type='text' placeholder="UF" maxLength={2} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* CEP */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="cep"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>CEP</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type='text' placeholder="Digite o CEP" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Cidade */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="cidade"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Cidade</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type="text" placeholder="Digite a cidade" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Bairro */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="tb_bairro_id"
|
||||||
|
render={({ field }) => {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
return (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bairro</FormLabel>
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl className="w-full">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
aria-expanded={open}
|
||||||
|
className="justify-between"
|
||||||
|
>
|
||||||
|
{field.value
|
||||||
|
? gTBBairro.find(
|
||||||
|
(item) =>
|
||||||
|
String(item.tb_bairro_id) === String(field.value),
|
||||||
|
)?.descricao
|
||||||
|
: 'Selecione...'}
|
||||||
|
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-full p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Buscar tipo logradouro..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{gTBBairro?.map((item) => (
|
||||||
|
<CommandItem
|
||||||
|
key={item.tb_bairro_id}
|
||||||
|
value={(item.descricao ?? '').toLowerCase()}
|
||||||
|
onSelect={() => {
|
||||||
|
field.onChange(Number(item.tb_bairro_id));
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
'mr-2 h-4 w-4',
|
||||||
|
String(field.value) === String(item.descricao)
|
||||||
|
? 'opacity-100'
|
||||||
|
: 'opacity-0',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{GetCapitalize(item.descricao)}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Cartório */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="cartorio"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Cartório</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type='number' placeholder="Digite o cartório" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* CNS */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-5">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="cns"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>CNS</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type="number" placeholder="Digite o CNS" onChange={e => field.onChange(parseNumberInput(e))} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Livro */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-5">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="livro"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Livro</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type='text' placeholder="Digite o livro" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Tipo Registro */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="tipo_registro"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<FormLabel>Tipo Registro</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select value={field.value} onValueChange={field.onChange}>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
{field.value
|
||||||
|
? ImovelTipoRegistro[field.value as keyof typeof ImovelTipoRegistro]
|
||||||
|
: "Selecione"}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{Object.entries(ImovelTipoRegistro).map(([key, label]) => (
|
||||||
|
<SelectItem key={key} value={key} className="cursor-pointer">
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Número */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="numero"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Número</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type="number" placeholder="Digite o número" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Número Letra */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="numero_letra"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Número Letra</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} type='text' placeholder="Digite a letra" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Tipo Registro */}
|
||||||
|
<div className="col-span-12 sm:col-span-6 md:col-span-3">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="tipo_classe"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="w-full">
|
||||||
|
<FormLabel>Tipo Classe</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select value={field.value} onValueChange={field.onChange}>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
{field.value
|
||||||
|
? ImovelTipoClasseEnum[Number(field.value) as keyof typeof ImovelTipoClasseEnum]
|
||||||
|
: "Selecione"}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{Object.entries(ImovelTipoClasseEnum).map(([key, label]) => (
|
||||||
|
<SelectItem key={key} value={key} className="cursor-pointer">
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
{/* Unidades */}
|
||||||
|
<TabsContent value="unidades" className="space-y-4">
|
||||||
|
{/* Conteúdo das unidades */}
|
||||||
|
{tipoClasse === 1 ? <TImovelUnidadeUrbanoPage imovel_id={data?.imovel_id} /> : null}
|
||||||
|
{/* Conteúdo das unidades */}
|
||||||
|
{tipoClasse === 3 ? <TImovelUnidadeRuralPage /> : null}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
{/* Rodapé do Dialog */}
|
||||||
|
<DialogFooter className="mt-4 flex flex-col sm:flex-row gap-2 justify-end">
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline" type="button" onClick={() => onClose(null, false)}>
|
||||||
|
Cancelar
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<LoadingButton
|
||||||
|
text="Salvar"
|
||||||
|
textLoading="Aguarde..."
|
||||||
|
type="submit"
|
||||||
|
loading={buttonIsLoading}
|
||||||
|
/>
|
||||||
|
</DialogFooter>
|
||||||
|
{/* Campo oculto */}
|
||||||
|
<input type="hidden" {...form.register("imovel_id")} />
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
170
src/packages/administrativo/components/TImovel/TImovelIndex.tsx
Normal file
170
src/packages/administrativo/components/TImovel/TImovelIndex.tsx
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
|
|
||||||
|
import Loading from '@/shared/components/loading/loading';
|
||||||
|
|
||||||
|
import { useTImovelIndexHook } from '@/packages/administrativo/hooks/TImovel/useTImovelIndexHook';
|
||||||
|
import { useTImovelSaveHook } from '@/packages/administrativo/hooks/TImovel/useTImovelSaveHook';
|
||||||
|
import { useTImovelDeleteHook } from '@/packages/administrativo/hooks/TImovel/useTImovelDeleteHook';
|
||||||
|
|
||||||
|
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
||||||
|
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
|
||||||
|
|
||||||
|
import TImovelInterface from '@/packages/administrativo/interfaces/TImovel/TImovelInterface';
|
||||||
|
import Header from '@/shared/components/structure/Header';
|
||||||
|
import { TImovelIndexInterface } from '../../interfaces/TImovel/TImovelIndexInterface';
|
||||||
|
import TImovelTable from './TImovelTable';
|
||||||
|
import TImovelForm from './TImovelForm';
|
||||||
|
|
||||||
|
export default function TImovelIndex({ pageTitle, pageDescription, tipoClasse }: TImovelIndexInterface) {
|
||||||
|
|
||||||
|
// Controle de estado do botão
|
||||||
|
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
||||||
|
|
||||||
|
// Hooks para leitura e salvamento
|
||||||
|
const { tImovel, indexTImovel } = useTImovelIndexHook();
|
||||||
|
const { saveTImovel } = useTImovelSaveHook();
|
||||||
|
const { deleteTImovel } = useTImovelDeleteHook();
|
||||||
|
|
||||||
|
// Estados
|
||||||
|
const [selectedAndamento, setSelectedAndamento] = useState<TImovelInterface | null>(null);
|
||||||
|
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||||
|
|
||||||
|
// Estado para saber qual item será deletado
|
||||||
|
const [itemToDelete, setItemToDelete] = useState<TImovelInterface | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook do modal de confirmação
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
isOpen: isConfirmOpen,
|
||||||
|
openDialog: openConfirmDialog,
|
||||||
|
handleConfirm,
|
||||||
|
handleCancel,
|
||||||
|
} = useConfirmDialog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abre o formulário no modo de edição ou criação
|
||||||
|
*/
|
||||||
|
const handleOpenForm = useCallback((data: TImovelInterface | null) => {
|
||||||
|
setSelectedAndamento(data);
|
||||||
|
setIsFormOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fecha o formulário e limpa o andamento selecionado
|
||||||
|
*/
|
||||||
|
const handleCloseForm = useCallback(() => {
|
||||||
|
setSelectedAndamento(null);
|
||||||
|
setIsFormOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Salva os dados do formulário
|
||||||
|
*/
|
||||||
|
const handleSave = useCallback(
|
||||||
|
async (formData: TImovelInterface) => {
|
||||||
|
// Coloca o botão em estado de loading
|
||||||
|
setButtonIsLoading(true);
|
||||||
|
|
||||||
|
// Aguarda salvar o registro
|
||||||
|
await saveTImovel(formData);
|
||||||
|
|
||||||
|
// Remove o botão em estado de loading
|
||||||
|
setButtonIsLoading(false);
|
||||||
|
|
||||||
|
// Atualiza a lista de dados
|
||||||
|
indexTImovel();
|
||||||
|
},
|
||||||
|
[saveTImovel, indexTImovel, handleCloseForm],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quando o usuário clica em "remover" na tabela
|
||||||
|
*/
|
||||||
|
const handleConfirmDelete = useCallback(
|
||||||
|
(item: TImovelInterface) => {
|
||||||
|
// Define o item atual para remoção
|
||||||
|
setItemToDelete(item);
|
||||||
|
|
||||||
|
// Abre o modal de confirmação
|
||||||
|
openConfirmDialog();
|
||||||
|
},
|
||||||
|
[openConfirmDialog],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executa a exclusão de fato quando o usuário confirma
|
||||||
|
*/
|
||||||
|
const handleDelete = useCallback(async () => {
|
||||||
|
// Protege contra null
|
||||||
|
if (!itemToDelete) return;
|
||||||
|
|
||||||
|
// Executa o Hook de remoção
|
||||||
|
await deleteTImovel(itemToDelete);
|
||||||
|
|
||||||
|
// Atualiza a lista
|
||||||
|
await indexTImovel();
|
||||||
|
|
||||||
|
// Limpa o item selecionado
|
||||||
|
setItemToDelete(null);
|
||||||
|
|
||||||
|
// Fecha o modal
|
||||||
|
handleCancel();
|
||||||
|
}, [itemToDelete, indexTImovel, handleCancel]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca inicial dos dados
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
indexTImovel();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tela de loading enquanto carrega os dados
|
||||||
|
*/
|
||||||
|
if (tImovel?.length == 0) {
|
||||||
|
return <Loading type={2} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Cabeçalho */}
|
||||||
|
<Header
|
||||||
|
title={pageTitle}
|
||||||
|
description={pageDescription}
|
||||||
|
buttonText={'Novo imóvel'}
|
||||||
|
buttonAction={() => {
|
||||||
|
handleOpenForm(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Tabela de andamentos */}
|
||||||
|
<TImovelTable
|
||||||
|
data={tImovel}
|
||||||
|
onEdit={handleOpenForm}
|
||||||
|
onDelete={handleConfirmDelete}
|
||||||
|
/>
|
||||||
|
{/* Modal de confirmação */}
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={isConfirmOpen}
|
||||||
|
title="Confirmar exclusão"
|
||||||
|
description="Atenção"
|
||||||
|
message={`Deseja realmente excluir o imóvel "${itemToDelete?.cidade}"?`}
|
||||||
|
confirmText="Sim, excluir"
|
||||||
|
cancelText="Cancelar"
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
{/* Formulário de criação/edição */}
|
||||||
|
<TImovelForm
|
||||||
|
isOpen={isFormOpen}
|
||||||
|
data={selectedAndamento}
|
||||||
|
onClose={handleCloseForm}
|
||||||
|
onSave={handleSave}
|
||||||
|
buttonIsLoading={buttonIsLoading}
|
||||||
|
tipoClasse={tipoClasse}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { DataTable } from '@/shared/components/dataTable/DataTable';
|
||||||
|
import TImovelColumns from './TImovelColumns';
|
||||||
|
import TImovelTableInterface from '../../interfaces/TImovel/TImovelTabelInterface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Componente principal da tabela
|
||||||
|
*/
|
||||||
|
export default function TImovelTable({ data, onEdit, onDelete }: TImovelTableInterface) {
|
||||||
|
const columns = TImovelColumns(onEdit, onDelete);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DataTable
|
||||||
|
data={data}
|
||||||
|
columns={columns}
|
||||||
|
filterColumn="numero"
|
||||||
|
filterPlaceholder="Buscar pelo numero de transcrição, matricula etc..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
'use client'; // Define que este componente será executado no lado do cliente (Client Component)
|
|
||||||
|
|
||||||
import z from 'zod';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
|
|
||||||
// Importação dos componentes da UI (botões, inputs, dialogs, etc.)
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/ui/dialog';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { Label } from '@/components/ui/label';
|
|
||||||
|
|
||||||
// Componente de botão com estado de carregamento
|
|
||||||
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
|
||||||
|
|
||||||
// Importa a interface tipada separadamente
|
|
||||||
import { UserFormInterface, FormValues } from '../../interfaces/User/UserFormInterface';
|
|
||||||
|
|
||||||
// Importa o hook que gerencia o formulário
|
|
||||||
import { useUserFormHook } from '../../hooks/User/useUserFormHook';
|
|
||||||
|
|
||||||
// Componente principal do formulário de usuário
|
|
||||||
export default function UserForm({ isOpen, data, onClose, onSave, buttonIsLoading }: UserFormInterface) {
|
|
||||||
|
|
||||||
// Usa o hook que centraliza toda a lógica do form
|
|
||||||
const form = useUserFormHook(data);
|
|
||||||
|
|
||||||
// Callback de erro da submissão do formulário
|
|
||||||
function onError(error: any) {
|
|
||||||
console.log("error", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
// Componente de diálogo (modal)
|
|
||||||
<Dialog
|
|
||||||
open={isOpen} // Define se o diálogo está aberto
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) onClose(null, false); // Fecha o diálogo quando necessário
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Conteúdo do modal */}
|
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>{data?.user_id ? 'Editar Usuário' : 'Novo Usuário'}</DialogTitle>
|
|
||||||
<DialogDescription>Gerencie os dados do usuário aqui.</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
{/* Wrapper do formulário com react-hook-form */}
|
|
||||||
<Form {...form}>
|
|
||||||
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-4">
|
|
||||||
|
|
||||||
{/* Campo: Nome */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Nome</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} placeholder="Digite o nome completo" />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Campo: Email */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input type="email" {...field} placeholder="exemplo@email.com" />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Campo: Senha */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Senha</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
{...field}
|
|
||||||
placeholder={data ? 'Deixe em branco para não alterar' : 'Digite a senha'}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Campo: Equipe */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="team"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Equipe</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} placeholder="Digite o nome da equipe" />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Campo: Cargo */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="position"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Cargo</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} value={field.value ?? ''} placeholder="Digite o cargo do usuário" />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Campo: Status (Ativo/Inativo) */}
|
|
||||||
<Controller
|
|
||||||
name="status"
|
|
||||||
control={form.control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value === 'A'}
|
|
||||||
onCheckedChange={(checked) => field.onChange(checked ? 'A' : 'I')}
|
|
||||||
/>
|
|
||||||
<Label>Ativo</Label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Rodapé do formulário (botões) */}
|
|
||||||
<DialogFooter className="mt-4">
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
type="button"
|
|
||||||
onClick={() => onClose(null, false)}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
Cancelar
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
|
|
||||||
{/* Botão de salvar com loading */}
|
|
||||||
<LoadingButton
|
|
||||||
text={data?.user_id ? 'Salvar' : 'Cadastrar'}
|
|
||||||
textLoading="Aguarde..."
|
|
||||||
type="submit"
|
|
||||||
loading={buttonIsLoading}
|
|
||||||
/>
|
|
||||||
</DialogFooter>
|
|
||||||
|
|
||||||
{/* Campos ocultos para dados técnicos */}
|
|
||||||
<input type="hidden" {...form.register('user_id')} />
|
|
||||||
<input type="hidden" {...form.register('date_register')} />
|
|
||||||
<input type="hidden" {...form.register('date_update')} />
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table';
|
|
||||||
|
|
||||||
import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react';
|
|
||||||
import { UserTableInterface } from '../../interfaces/User/UserTableInterface';
|
|
||||||
import { StatusBadge } from '@/components/StatusBadge';
|
|
||||||
|
|
||||||
export default function UserTable({ data, onEdit, onDelete }: UserTableInterface) {
|
|
||||||
return (
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>#</TableHead>
|
|
||||||
<TableHead>Status</TableHead>
|
|
||||||
<TableHead>Nome</TableHead>
|
|
||||||
<TableHead>Email</TableHead>
|
|
||||||
<TableHead>Equipe</TableHead>
|
|
||||||
<TableHead className="text-right">Ações</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
<TableBody>
|
|
||||||
{data.map((user) => (
|
|
||||||
<TableRow key={user.user_id}>
|
|
||||||
<TableCell className="font-medium">{user.user_id}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<StatusBadge status={user.status} />
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{user.name}</TableCell>
|
|
||||||
<TableCell>{user.email}</TableCell>
|
|
||||||
<TableCell>{user.team}</TableCell>
|
|
||||||
<TableCell className="text-right">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="outline" size="icon" className="cursor-pointer">
|
|
||||||
<EllipsisIcon />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
|
|
||||||
<DropdownMenuContent side="left" align="start">
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="cursor-pointer"
|
|
||||||
onSelect={() => onEdit(user)}
|
|
||||||
>
|
|
||||||
<PencilIcon className="mr-2 h-4 w-4" />
|
|
||||||
Editar
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="cursor-pointer"
|
|
||||||
onSelect={() => onDelete(user)}
|
|
||||||
>
|
|
||||||
<Trash2Icon className="mr-2 h-4 w-4" />
|
|
||||||
Remover
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
'use server'
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeClientDeleteData(client_id: number) {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.DELETE,
|
|
||||||
'endpoint': `administrativo/client/${client_id}`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ClientDeleteData = withClientErrorHandler(executeClientDeleteData)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
'use server'
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeClientIndexData() {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.GET,
|
|
||||||
'endpoint': `administrativo/client/`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ClientIndexData = withClientErrorHandler(executeClientIndexData)
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
'use server'
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
// Ajuste o caminho da importação conforme a estrutura real das suas pastas
|
|
||||||
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
|
|
||||||
|
|
||||||
async function executeClientSaveData(form: ClientInterface) {
|
|
||||||
// Verifica se existe ID e se é maior que 0 para considerar edição
|
|
||||||
const isUpdate = Boolean(form.client_id && form.client_id > 0);
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': isUpdate ? Methods.PUT : Methods.POST,
|
|
||||||
// Se for update, adiciona o ID na URL. Se for create, remove (fica vazio após a barra)
|
|
||||||
'endpoint': `administrativo/client/${form.client_id || ''}`,
|
|
||||||
'body': form
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ClientSaveData = withClientErrorHandler(executeClientSaveData)
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
'use server';
|
||||||
|
|
||||||
|
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
||||||
|
import API from '@/shared/services/api/Api';
|
||||||
|
|
||||||
|
export default async function GUsuarioLoginData(form: any) {
|
||||||
|
const api = new API();
|
||||||
|
// Realiza o envio dos dados
|
||||||
|
const response = await api.send({
|
||||||
|
method: Methods.POST,
|
||||||
|
endpoint: `administrativo/g_usuario/authenticate`,
|
||||||
|
body: form,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'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);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'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);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'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);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'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);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'use server'
|
|
||||||
// Indica que este módulo será executado no lado do servidor (Server 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 executeLogIndexByIDData(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/${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 LogIndexByClientIDData = withClientErrorHandler(executeLogIndexByIDData);
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
'use server';
|
|
||||||
// Indica que este módulo será executado no lado do servidor (Server Action do Next.js)
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
// Importa o enum com os tipos de métodos HTTP disponíveis (GET, POST, PUT, DELETE...)
|
|
||||||
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
// Importa a classe responsável por centralizar chamadas à API (wrapper de fetch ou axios)
|
|
||||||
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
// Importa um decorador/função HOC que trata erros de forma padronizada nas requisições
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Função principal responsável por buscar a lista de loges na API.
|
|
||||||
* Executa uma requisição HTTP GET para o endpoint administrativo/log.
|
|
||||||
*/
|
|
||||||
async function executeLogIndexData() {
|
|
||||||
|
|
||||||
// Instancia o serviço de API para uso nesta função
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
// Executa uma requisição GET para o endpoint administrativo/log/
|
|
||||||
// - Usa o método 'send' da classe API
|
|
||||||
// - Passa o método HTTP e o endpoint como parâmetros
|
|
||||||
const response = await api.send({
|
|
||||||
method: Methods.GET, // Método HTTP GET
|
|
||||||
endpoint: `administrativo/log/`, // Rota da API que retorna a lista de loges
|
|
||||||
});
|
|
||||||
|
|
||||||
// Retorna a resposta obtida da API
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exporta a função encapsulada com o handler de erro.
|
|
||||||
* Caso ocorra falha na requisição, o withlogErrorHandler
|
|
||||||
* intercepta o erro e o trata de forma uniforme (ex: logging, toast, etc.)
|
|
||||||
*/
|
|
||||||
export const LogIndexData = withClientErrorHandler(executeLogIndexData);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'use server'
|
|
||||||
// Indica que este módulo será executado no lado do servidor (Server 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 executeLogServerData(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/server/${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 LogServerData = withClientErrorHandler(executeLogServerData);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'use server'
|
|
||||||
// Indica que este módulo será executado no lado do servidor (Warning 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 executeLogWarningData(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/warning/${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 LogWarningData = withClientErrorHandler(executeLogWarningData);
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
|
||||||
|
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
|
||||||
|
import API from "@/shared/services/api/Api";
|
||||||
|
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
|
||||||
|
import TImovelInterface from "../../interfaces/TImovel/TImovelInterface";
|
||||||
|
|
||||||
|
async function executeTImovelDeleteData(data: TImovelInterface): Promise<ApiResponseInterface> {
|
||||||
|
|
||||||
|
const api = new API();
|
||||||
|
|
||||||
|
return await api.send({
|
||||||
|
method: Methods.DELETE,
|
||||||
|
endpoint: `administrativo/t_imovel/${data.imovel_id}`
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TImovelDeleteData = withClientErrorHandler(executeTImovelDeleteData);
|
||||||
17
src/packages/administrativo/data/TImovel/TImovelIndexData.ts
Normal file
17
src/packages/administrativo/data/TImovel/TImovelIndexData.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
|
||||||
|
import API from "@/shared/services/api/Api";
|
||||||
|
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
|
||||||
|
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
|
||||||
|
|
||||||
|
async function executeTImovelIndexData(): Promise<ApiResponseInterface> {
|
||||||
|
|
||||||
|
const api = new API();
|
||||||
|
|
||||||
|
return api.send({
|
||||||
|
method: Methods.GET,
|
||||||
|
endpoint: `administrativo/t_imovel/classe/1`
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TImovelIndexData = withClientErrorHandler(executeTImovelIndexData);
|
||||||
24
src/packages/administrativo/data/TImovel/TImovelSaveData.ts
Normal file
24
src/packages/administrativo/data/TImovel/TImovelSaveData.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
|
||||||
|
import TImovelInterface from "../../interfaces/TImovel/TImovelInterface";
|
||||||
|
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
|
||||||
|
import API from "@/shared/services/api/Api";
|
||||||
|
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
|
||||||
|
|
||||||
|
async function executeTImovelSaveData(data: TImovelInterface): Promise<ApiResponseInterface> {
|
||||||
|
|
||||||
|
// Verifica se existe ID da cidade para decidir se é atualização (PUT) ou criação (POST)
|
||||||
|
const isUpdate = Boolean(data.imovel_id);
|
||||||
|
|
||||||
|
// Instancia o cliente da API para enviar a requisição
|
||||||
|
const api = new API();
|
||||||
|
|
||||||
|
// Executa a requisição para a API com o método apropriado e envia os dados no corpo
|
||||||
|
return await api.send({
|
||||||
|
method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar
|
||||||
|
endpoint: `administrativo/t_imovel/${data.imovel_id || ''}`, // endpoint dinâmico
|
||||||
|
body: data, // payload enviado para a API
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TImovelSaveData = withClientErrorHandler(executeTImovelSaveData);
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
'use server'
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeUserDeleteData(usuarioId: number) {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.DELETE,
|
|
||||||
'endpoint': `administrativo/user/${usuarioId}`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserDeleteData = withClientErrorHandler(executeUserDeleteData)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
'use server'
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeUserIndexByEmailData(email: string) {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.GET,
|
|
||||||
'endpoint': `administrativo/user/email?email=${email}`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserIndexByEmailData = withClientErrorHandler(executeUserIndexByEmailData)
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
'use server'
|
|
||||||
// Indica que este módulo será executado no lado do servidor (Server 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 executeUserIndexByIDData(user_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/user/${user_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 UserIndexByIDData = withClientErrorHandler(executeUserIndexByIDData);
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
'use server'
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeUserIndexData() {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.GET,
|
|
||||||
'endpoint': `administrativo/user/`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserIndexData = withClientErrorHandler(executeUserIndexData)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
'use server'
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeUserLoggedIndexData() {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.GET,
|
|
||||||
'endpoint': `administrativo/user/me`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserLoggedIndexData = withClientErrorHandler(executeUserLoggedIndexData)
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
'use server';
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { AuthenticateUserInterface } from '@/shared/interfaces/AuthenticateUserInterface';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
export default async function UserLoginData(form: any) {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
method: Methods.POST,
|
|
||||||
endpoint: `administrativo/user/authenticate`,
|
|
||||||
body: form,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("resposta da api",response)
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
'use server'
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
|
|
||||||
async function executeUserReadData(usuarioId: number) {
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': Methods.GET,
|
|
||||||
'endpoint': `administrativo/user/${usuarioId}`
|
|
||||||
});
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserReadData = withClientErrorHandler(executeUserReadData)
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
'use server'
|
|
||||||
|
|
||||||
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
|
|
||||||
import API from '@/shared/services/api/Api';
|
|
||||||
import { withClientErrorHandler } from '@/withClientErrorHandler/withClientErrorHandler';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
|
|
||||||
async function executeUserSaveData(form: UserInterface) {
|
|
||||||
const isUpdate = Boolean(form.user_id)
|
|
||||||
|
|
||||||
const api = new API();
|
|
||||||
|
|
||||||
const response = await api.send({
|
|
||||||
'method': isUpdate ? Methods.PUT : Methods.POST,
|
|
||||||
'endpoint': `administrativo/user/${form.user_id || ''}`,
|
|
||||||
'body': form
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserSaveData = withClientErrorHandler(executeUserSaveData)
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
import { ClientDeleteService } from '../../services/Client/ClientDeleteService';
|
|
||||||
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
|
|
||||||
|
|
||||||
export const useClientDeleteHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const removeClient = async (client: ClientInterface) => {
|
|
||||||
|
|
||||||
// Proteção extra: não tenta deletar se não tiver ID
|
|
||||||
if (!client.client_id) {
|
|
||||||
console.error("Tentativa de excluir cliente sem ID");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await ClientDeleteService(client.client_id);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erro ao remover cliente:', error);
|
|
||||||
// O withClientErrorHandler no Service já deve capturar isso,
|
|
||||||
// mas o try/catch aqui garante que a UI não quebre.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { removeClient };
|
|
||||||
};
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
// useClientIndexHook.ts
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
|
||||||
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
|
|
||||||
import { ClientIndexService } from '../../services/Client/ClientIndexService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useClientIndexHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
const [clients, setClients] = useState<ClientInterface[]>([]);
|
|
||||||
|
|
||||||
const fetchClients = useCallback(async () => {
|
|
||||||
const response = await ClientIndexService();
|
|
||||||
|
|
||||||
if (response.data) {
|
|
||||||
setClients(response.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRÍTICO: Não chame setResponse se deu tudo certo (200).
|
|
||||||
// Isso evita que o contexto atualize e force um re-render da tela toda.
|
|
||||||
if (response.code !== 200) {
|
|
||||||
setResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [setResponse]);
|
|
||||||
|
|
||||||
return { clients, fetchClients };
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { ClientInterface } from '../../interfaces/Client/ClientInterface';
|
|
||||||
import { ClientSaveService } from '../../services/Client/ClientSaveService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useClientSaveHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
// Estado para armazenar o último cliente salvo (útil se precisar redirecionar ou mostrar dados)
|
|
||||||
const [savedClient, setSavedClient] = useState<ClientInterface | null>(null);
|
|
||||||
|
|
||||||
const saveClient = async (client: ClientInterface) => {
|
|
||||||
const response = await ClientSaveService(client);
|
|
||||||
|
|
||||||
if (response.data) {
|
|
||||||
setSavedClient(response.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
setResponse(response);
|
|
||||||
|
|
||||||
// Retornamos a resposta para que o componente (page/modal) saiba se deve fechar ou não
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
return { savedClient, saveClient };
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import GUsuarioLogoutService from '../../services/GUsuario/GUsuarioLogoutService';
|
||||||
|
|
||||||
|
export const useGUsuarioLogoutHook = () => {
|
||||||
|
const logoutUsuario = async () => {
|
||||||
|
await GUsuarioLogoutService('access_token');
|
||||||
|
};
|
||||||
|
|
||||||
|
return { logoutUsuario };
|
||||||
|
};
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LogBackupInterface } from '../../interfaces/Log/LogBackupInterface';
|
|
||||||
import { LogBackupService } from '../../services/Log/LogBackupService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useLogBackupHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
// Estado tipado para armazenar apenas os dados reais de log
|
|
||||||
const [logBackup, setLog] = useState<LogBackupInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchLogBackup = async (client_id: number) => {
|
|
||||||
try {
|
|
||||||
const response = await LogBackupService(client_id);
|
|
||||||
|
|
||||||
// Se a estrutura for { success, message, data }, use response.data
|
|
||||||
const logData =
|
|
||||||
response?.data && response.data.backup ? response.data : response;
|
|
||||||
|
|
||||||
// --- ETAPA 1: ordenar backups por data ---
|
|
||||||
if (logData.backup) {
|
|
||||||
// Converte o objeto de backups em array [nomeArquivo, dados]
|
|
||||||
const sortedBackups = Object.entries(logData.backup).sort(([, a], [, b]) => {
|
|
||||||
const dateA = a.data.split('/').reverse().join('-');
|
|
||||||
const dateB = b.data.split('/').reverse().join('-');
|
|
||||||
return new Date(dateB).getTime() - new Date(dateA).getTime(); // Mais recente primeiro
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reconstrói o objeto ordenado
|
|
||||||
logData.backup = Object.fromEntries(sortedBackups);
|
|
||||||
|
|
||||||
// --- ETAPA 2: obter a data mais recente ---
|
|
||||||
const latestDate = sortedBackups[0][1].data;
|
|
||||||
|
|
||||||
// --- ETAPA 3: filtrar todos os backups dessa data ---
|
|
||||||
const latestBackups = sortedBackups
|
|
||||||
.filter(([, dados]) => dados.data === latestDate)
|
|
||||||
.map(([nome_arquivo, dados]) => ({ nome_arquivo, dados }));
|
|
||||||
|
|
||||||
// --- ETAPA 4: adicionar campo customizado no JSON ---
|
|
||||||
logData.latest_backups = {
|
|
||||||
data: latestDate,
|
|
||||||
arquivos: latestBackups,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Armazena os dados atualizados no estado
|
|
||||||
setLog(logData);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erro ao buscar informação do servidor por ID:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { logBackup, fetchLogBackup };
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
'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 };
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LogDiskInterface } from '../../interfaces/Log/LogDiskInterface';
|
|
||||||
import { LogDiskService } from '../../services/Log/LogDiskService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useLogDiskHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
const [logDisk, setLog] = useState<LogDiskInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchLogDisk = async (client_id: number) => {
|
|
||||||
try {
|
|
||||||
const response = await LogDiskService(client_id);
|
|
||||||
setLog(response as LogDiskInterface);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro ao buscar informação do banco de dados:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { logDisk, fetchLogDisk };
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
'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 };
|
|
||||||
};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LogInterface } from '../../interfaces/Log/LogInterface';
|
|
||||||
import { LogServerService } from '../../services/Log/LogServerService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useLogServerHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [logServer, setLog] = useState<LogInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchLogServer = async (client_id: number) => {
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await LogServerService(client_id);
|
|
||||||
setLog(response.data);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
// O withClientErrorHandler já deve tratar o erro e formatar a 'response',
|
|
||||||
// mas um catch local pode ser útil para lógicas adicionais se necessário.
|
|
||||||
console.error("Erro ao buscar informação do servidor por ID:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { logServer, fetchLogServer };
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { LogWarningInterface } from '../../interfaces/Log/LogWarningInterface';
|
|
||||||
import { LogWarningService } from '../../services/Log/LogWarningService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useLogWarningHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
const [logWarning, setLog] = useState<LogWarningInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchLogWarning = async (client_id: number) => {
|
|
||||||
try {
|
|
||||||
const response = await LogWarningService(client_id);
|
|
||||||
setLog(response as LogWarningInterface);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro ao buscar informação do banco de dados:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { logWarning, fetchLogWarning };
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
|
||||||
|
import { TImovelDeleteService } from '../../services/TImovel/TImovelDeleteService';
|
||||||
|
|
||||||
|
export const useTImovelDeleteHook = () => {
|
||||||
|
const { setResponse } = useResponse();
|
||||||
|
|
||||||
|
const [tImovel, setTImovel] = useState<TImovelInterface>();
|
||||||
|
|
||||||
|
const deleteTImovel = async (data: TImovelInterface) => {
|
||||||
|
const response = await TImovelDeleteService(data);
|
||||||
|
|
||||||
|
setTImovel(data);
|
||||||
|
|
||||||
|
setResponse(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { tImovel, deleteTImovel };
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { TImovelFormValues, TImovelSchema } from "../../schemas/TImovel/TImovelSchema";
|
||||||
|
|
||||||
|
export function useTImovelFormHook(defaults?: Partial<TImovelFormValues>) {
|
||||||
|
return useForm<TImovelFormValues>({
|
||||||
|
resolver: zodResolver(TImovelSchema),
|
||||||
|
defaultValues: {
|
||||||
|
...defaults,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
|
||||||
|
import { TImovelIndexData } from '../../data/TImovel/TImovelIndexData';
|
||||||
|
|
||||||
|
export const useTImovelIndexHook = () => {
|
||||||
|
const { setResponse } = useResponse();
|
||||||
|
|
||||||
|
const [tImovel, setTImovel] = useState<TImovelInterface[]>([]);
|
||||||
|
|
||||||
|
const indexTImovel = async () => {
|
||||||
|
const response = await TImovelIndexData();
|
||||||
|
|
||||||
|
// Armazena os dados consultados
|
||||||
|
setTImovel(response.data);
|
||||||
|
|
||||||
|
// Define os dados do componente de resposta (toast, modal, etc)
|
||||||
|
setResponse(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
tImovel,
|
||||||
|
indexTImovel
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useResponse } from '@/shared/components/response/ResponseContext';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import TImovelInterface from '../../interfaces/TImovel/TImovelInterface';
|
||||||
|
import { TImovelSaveService } from '../../services/TImovel/TImovelSaveService';
|
||||||
|
|
||||||
|
export const useTImovelSaveHook = () => {
|
||||||
|
const { setResponse } = useResponse();
|
||||||
|
|
||||||
|
const [tImovel, setTImovel] = useState<TImovelInterface>();
|
||||||
|
|
||||||
|
// controla se o formulário está aberto ou fechado
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const saveTImovel = async (data: TImovelInterface) => {
|
||||||
|
const response = await TImovelSaveService(data);
|
||||||
|
|
||||||
|
// Armazena os dados da repsota
|
||||||
|
setTImovel(response.data);
|
||||||
|
|
||||||
|
// Define os dados da respota(toast, modal, etc)
|
||||||
|
setResponse(response);
|
||||||
|
|
||||||
|
// Fecha o formulário automaticamente após salvar
|
||||||
|
setIsOpen(false);
|
||||||
|
|
||||||
|
// Retorna os valores de forma imediata
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { tImovel, saveTImovel };
|
||||||
|
};
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
import { UserDeleteService } from '../../services/User/UserDeleteService'; // Ajuste o caminho conforme necessário
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface'; // Ajuste o caminho
|
|
||||||
|
|
||||||
export const useUserDeleteHook = () => {
|
|
||||||
// Hook de contexto para fornecer feedback (toast, modal, etc.)
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const removeUser = async (user: UserInterface) => {
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Chama o serviço de exclusão, passando apenas o ID do usuário
|
|
||||||
const response = await UserDeleteService(user.user_id);
|
|
||||||
|
|
||||||
// Define a resposta para que o ResponseContext possa exibir um feedback
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
// O withClientErrorHandler já trata o erro, mas um log pode ser útil
|
|
||||||
console.error('Erro ao remover usuário:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Retorna a função de remoção e o estado de carregamento
|
|
||||||
return { removeUser };
|
|
||||||
};
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
|
|
||||||
// Importa o schema de validação (Zod)
|
|
||||||
import { UserSchema } from '../../schemas/User/UserSchema';
|
|
||||||
|
|
||||||
// Importa o enum com o status
|
|
||||||
import { SituacoesEnum } from '@/shared/enums/SituacoesEnum';
|
|
||||||
|
|
||||||
// Tipagem do formulário (interface compartilhada)
|
|
||||||
import { FormValues } from '../../interfaces/User/UserFormInterface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook responsável por inicializar e gerenciar o estado do formulário de usuários.
|
|
||||||
* Centraliza a lógica de criação, reset e carregamento de dados.
|
|
||||||
*/
|
|
||||||
export function useUserFormHook(data: FormValues | null) {
|
|
||||||
// Inicializa o React Hook Form com validação baseada no Zod
|
|
||||||
const form = useForm<FormValues>({
|
|
||||||
resolver: zodResolver(UserSchema), // Aplica o schema para validação automática
|
|
||||||
defaultValues: {
|
|
||||||
user_id: 0,
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
team: '',
|
|
||||||
cargo: '',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
user_id_create: null,
|
|
||||||
user_id_update: null,
|
|
||||||
date_register: new Date().toISOString(),
|
|
||||||
date_update: null,
|
|
||||||
} as FormValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Efeito responsável por atualizar o formulário
|
|
||||||
* sempre que houver dados (modo edição) ou resetar (modo criação).
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
// Modo edição → carrega dados do usuário
|
|
||||||
form.reset({
|
|
||||||
...data,
|
|
||||||
password: '',
|
|
||||||
});
|
|
||||||
console.log('Form carregado com dados:', data);
|
|
||||||
} else {
|
|
||||||
// Modo criação → limpa o formulário
|
|
||||||
form.reset({
|
|
||||||
user_id: 0,
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
team: '',
|
|
||||||
position: '',
|
|
||||||
status: SituacoesEnum.ATIVO,
|
|
||||||
user_id_create: null,
|
|
||||||
user_id_update: null,
|
|
||||||
date_register: new Date().toISOString(),
|
|
||||||
date_update: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [data]); // Atualiza sempre que "data" mudar
|
|
||||||
|
|
||||||
// Retorna o objeto form para uso no componente
|
|
||||||
return form;
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
import { UserLoginSchema } from '../../schemas/User/UserLoginSchema';
|
|
||||||
import UserLoginService from '../../services/User/UserLoginService';
|
|
||||||
import z from 'zod';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tipagem dos valores do formulário.
|
|
||||||
* Inclui o campo "rememberMe" que não está no schema Zod original.
|
|
||||||
*/
|
|
||||||
export type LoginFormValues = z.infer<typeof UserLoginSchema> & {
|
|
||||||
rememberMe: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook responsável por gerenciar o formulário de login e o comportamento
|
|
||||||
* de lembrar acesso (localStorage + API).
|
|
||||||
*/
|
|
||||||
export function useUserFormLoginHook() {
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
// Inicializa o formulário com validação Zod
|
|
||||||
const form = useForm<LoginFormValues>({
|
|
||||||
resolver: zodResolver(UserLoginSchema),
|
|
||||||
defaultValues: {
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
rememberMe: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Carrega o e-mail salvo no localStorage, se existir
|
|
||||||
useEffect(() => {
|
|
||||||
const savedEmail = localStorage.getItem('remembered_email');
|
|
||||||
if (savedEmail) {
|
|
||||||
form.setValue('email', savedEmail);
|
|
||||||
form.setValue('rememberMe', true);
|
|
||||||
}
|
|
||||||
}, [form]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Função de envio do formulário — autentica o usuário e
|
|
||||||
* salva o e-mail no localStorage se o "Lembrar acesso" estiver marcado.
|
|
||||||
*/
|
|
||||||
const onSubmit = async (values: LoginFormValues) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
await UserLoginService(values);
|
|
||||||
|
|
||||||
if (values.rememberMe) {
|
|
||||||
localStorage.setItem('remembered_email', values.email);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem('remembered_email');
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Retorna o formulário e os estados necessários para o componente
|
|
||||||
return { form, onSubmit, loading };
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
import { UserIndexByEmailService } from '../../services/User/UserIndexByEmailService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useUserIndexByEmailHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [user, setUser] = useState<UserInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchUserByEmail = async (email: string) => {
|
|
||||||
try {
|
|
||||||
const response = await UserIndexByEmailService(email);
|
|
||||||
|
|
||||||
setUser(response.data);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro ao buscar usuário por Email:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { user, fetchUserByEmail };
|
|
||||||
};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
import { UserIndexByIDService } from '../../services/User/UserIndexByIDService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useUserIndexByIdHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [user, setUser] = useState<UserInterface | null>(null);
|
|
||||||
|
|
||||||
const fetchUserById = async (userId: number) => {
|
|
||||||
try {
|
|
||||||
const response = await UserIndexByIDService(userId);
|
|
||||||
|
|
||||||
setUser(response.data);
|
|
||||||
setResponse(response);
|
|
||||||
} catch (error) {
|
|
||||||
// O withClientErrorHandler já deve tratar o erro e formatar a 'response',
|
|
||||||
// mas um catch local pode ser útil para lógicas adicionais se necessário.
|
|
||||||
console.error("Erro ao buscar usuário por ID:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { user, fetchUserById };
|
|
||||||
};
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
import { UserIndexService } from '../../services/User/UserIndexService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useUserIndexHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [usuarios, setUsuarios] = useState<UserInterface[]>([]);
|
|
||||||
|
|
||||||
const fetchUsuarios = async () => {
|
|
||||||
const response = await UserIndexService();
|
|
||||||
|
|
||||||
if (response.data) {
|
|
||||||
setUsuarios(response.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define os dados do componente de resposta (toast, modal, etc)
|
|
||||||
setResponse(response);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { usuarios, fetchUsuarios };
|
|
||||||
};
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
'use client'; // Indica que este código roda no lado do cliente (Next.js)
|
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation'; // Hook do Next.js para redirecionamentos no cliente
|
|
||||||
import { UserLogoutService } from '../../services/User/UserLogoutService'; // Importa o serviço de logout
|
|
||||||
|
|
||||||
// Hook customizado responsável por encapsular a lógica de logout
|
|
||||||
export const useUserLogoutHook = () => {
|
|
||||||
const router = useRouter(); // Inicializa o roteador do Next.js
|
|
||||||
|
|
||||||
// Função assíncrona responsável por executar o logout
|
|
||||||
const logoutUsuario = async () => {
|
|
||||||
try {
|
|
||||||
// Chama o serviço no servidor para apagar o cookie do token
|
|
||||||
await UserLogoutService('access_token');
|
|
||||||
|
|
||||||
// Redireciona o usuário para a tela de login após o logout
|
|
||||||
router.push('/login');
|
|
||||||
} catch (error) {
|
|
||||||
// Captura e exibe eventuais erros no processo de logout
|
|
||||||
console.error('Erro ao fazer logout:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Retorna a função de logout para ser usada em qualquer componente
|
|
||||||
return { logoutUsuario };
|
|
||||||
};
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
import { UserReadService } from '../../services/User/UserReadService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useUserReadHooks = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [User, setUser] = useState<UserInterface>();
|
|
||||||
|
|
||||||
const fetchUser = async (User: UserInterface) => {
|
|
||||||
const response = await UserReadService(User.user_id);
|
|
||||||
|
|
||||||
setUser(response.data);
|
|
||||||
|
|
||||||
setResponse(response);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { User, fetchUser };
|
|
||||||
};
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { UserInterface } from '../../interfaces/User/UserInterface';
|
|
||||||
import { UserSaveService } from '../../services/User/UserSaveService';
|
|
||||||
import { useResponse } from '@/shared/components/response/ResponseContext';
|
|
||||||
|
|
||||||
export const useUserSaveHook = () => {
|
|
||||||
const { setResponse } = useResponse();
|
|
||||||
|
|
||||||
const [usuarios, setUser] = useState<UserInterface>();
|
|
||||||
|
|
||||||
const saveUser = async (User: any) => {
|
|
||||||
const response = await UserSaveService(User);
|
|
||||||
|
|
||||||
setUser(response.data);
|
|
||||||
|
|
||||||
setResponse(response);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { usuarios, saveUser };
|
|
||||||
};
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
import { SituacoesEnum } from "@/shared/enums/SituacoesEnum";
|
|
||||||
|
|
||||||
export interface ClientInterface {
|
|
||||||
client_id: number;
|
|
||||||
cns: string;
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
// Campos que vieram como null no JSON, mas sabemos que são strings pelo contexto do negócio
|
|
||||||
state: string | null;
|
|
||||||
city: string | null;
|
|
||||||
responsible: string | null;
|
|
||||||
consultant: string | null;
|
|
||||||
type_contract: string | null; // No Postman era string ("1"), então mantive string | null
|
|
||||||
|
|
||||||
status: SituacoesEnum; // 'A' mapeado para o Enum existente
|
|
||||||
|
|
||||||
date_register?: string; // Opcional pois geralmente não é enviado na criação (gerado pelo banco)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface auxiliar para a Paginação (caso você vá tipar a resposta completa do serviço)
|
|
||||||
export interface ClientPaginationInterface {
|
|
||||||
total_records: number;
|
|
||||||
total_pages: number;
|
|
||||||
current_page: number;
|
|
||||||
next_page: number | null;
|
|
||||||
first: number;
|
|
||||||
skip: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface para a resposta completa da API (Útil para o Service/Data layer)
|
|
||||||
export interface ClientResponseInterface {
|
|
||||||
message: string;
|
|
||||||
data: ClientInterface[];
|
|
||||||
pagination: ClientPaginationInterface;
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { ClientInterface } from './ClientInterface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface que define as propriedades esperadas pelo componente ClientTable.
|
|
||||||
*/
|
|
||||||
export interface ClientTableInterface {
|
|
||||||
data: ClientInterface[]; // Lista de clientes a exibir
|
|
||||||
|
|
||||||
// Objeto opcional de paginação retornado pela API
|
|
||||||
pagination?: {
|
|
||||||
total_records: number; // Total de registros encontrados
|
|
||||||
total_pages: number; // Total de páginas disponíveis
|
|
||||||
current_page: number; // Página atual
|
|
||||||
next_page?: number | null; // Próxima página (opcional)
|
|
||||||
first?: number; // Quantidade padrão por página
|
|
||||||
skip?: number; // Offset inicial
|
|
||||||
};
|
|
||||||
|
|
||||||
onEdit: (client: ClientInterface) => void; // Ação ao clicar em editar
|
|
||||||
onDelete: (client: ClientInterface) => void; // Ação ao clicar em remover
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
export default interface GUsuario {
|
||||||
|
usuario_id: number;
|
||||||
|
trocarsenha: string;
|
||||||
|
login: string;
|
||||||
|
senha: string;
|
||||||
|
situacao: string;
|
||||||
|
nome_completo: string;
|
||||||
|
funcao: string;
|
||||||
|
assina: string;
|
||||||
|
sigla: string;
|
||||||
|
usuario_tab: string;
|
||||||
|
ultimo_login: string;
|
||||||
|
ultimo_login_regs: string;
|
||||||
|
data_expiracao: string;
|
||||||
|
senha_anterior: string;
|
||||||
|
andamento_padrao: string;
|
||||||
|
lembrete_pergunta: string;
|
||||||
|
lembrete_resposta: string;
|
||||||
|
andamento_padrao2: string;
|
||||||
|
receber_mensagem_arrolamento: string;
|
||||||
|
email: string;
|
||||||
|
assina_certidao: string;
|
||||||
|
receber_email_penhora: string;
|
||||||
|
foto: string;
|
||||||
|
nao_receber_chat_todos: string;
|
||||||
|
pode_alterar_caixa: string;
|
||||||
|
receber_chat_certidao_online: string;
|
||||||
|
receber_chat_cancelamento: string;
|
||||||
|
cpf: string;
|
||||||
|
somente_leitura: string;
|
||||||
|
receber_chat_envio_onr: string;
|
||||||
|
tipo_usuario: string;
|
||||||
|
senha_api: string;
|
||||||
|
}
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/**
|
|
||||||
* 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")
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface que representa a tabela `log` do banco de dados `monitoring`.
|
|
||||||
* Cada campo reflete a tipagem e as restrições definidas na DDL original.
|
|
||||||
*/
|
|
||||||
export interface LogInterface {
|
|
||||||
log_id?: number; // ID único do log (chave primária, gerada automaticamente pelo banco)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
warning: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { TImovelFormValues } from "../../schemas/TImovel/TImovelSchema";
|
||||||
|
|
||||||
|
export interface TImovelFormInterface {
|
||||||
|
isOpen: boolean;
|
||||||
|
data: TImovelFormValues | null;
|
||||||
|
onClose: (item: null, isFormStatus: boolean) => void;
|
||||||
|
onSave: (data: TImovelFormValues) => void;
|
||||||
|
buttonIsLoading: boolean;
|
||||||
|
tipoClasse: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface TImovelIndexInterface {
|
||||||
|
pageTitle: string,
|
||||||
|
pageDescription: string,
|
||||||
|
tipoClasse: number
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
export default interface TImovelInterface {
|
||||||
|
imovel_id?: number,
|
||||||
|
tipo_classe?: string,
|
||||||
|
tipo_registro?: string,
|
||||||
|
data_registro?: string,
|
||||||
|
numero?: number,
|
||||||
|
numero_letra?: string,
|
||||||
|
cidade?: string,
|
||||||
|
cep?: string,
|
||||||
|
uf?: string,
|
||||||
|
tb_bairro_id?: number,
|
||||||
|
cartorio?: string,
|
||||||
|
livro?: string,
|
||||||
|
cns?: number,
|
||||||
|
gtbb_descricao?: string,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import TImovelInterface from "./TImovelInterface";
|
||||||
|
|
||||||
|
export default interface TImovelTableInterface {
|
||||||
|
data?: TImovelInterface[];
|
||||||
|
onEdit: (item: TImovelInterface, isEditingFormStatus: boolean) => void;
|
||||||
|
onDelete: (item: TImovelInterface, isEditingFormStatus: boolean) => void;
|
||||||
|
}
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// Define o tipo base dos valores do formulário (importa do schema do usuário)
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { UserSchema } from '../../schemas/User/UserSchema';
|
|
||||||
|
|
||||||
// Cria o tipo inferido a partir do schema do usuário
|
|
||||||
export type FormValues = z.infer<typeof UserSchema>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface com as propriedades aceitas pelo componente UserForm.
|
|
||||||
* Isso facilita a reutilização e deixa o código mais limpo.
|
|
||||||
*/
|
|
||||||
export interface UserFormInterface {
|
|
||||||
isOpen: boolean; // Controla se o diálogo está aberto
|
|
||||||
data: FormValues | null; // Dados do usuário para edição (ou null no modo de criação)
|
|
||||||
onClose: (item: null, isFormStatus: boolean) => void; // Função executada ao fechar o diálogo
|
|
||||||
onSave: (data: FormValues) => void; // Função executada ao salvar o formulário
|
|
||||||
buttonIsLoading: boolean; // Define se o botão de envio está em modo de carregamento
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface de tipagem para o componente LoginForm.
|
|
||||||
*/
|
|
||||||
export interface UserFormLoginInterface extends React.ComponentProps<'div'> {
|
|
||||||
className?: string; // Classe CSS opcional
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { SituacoesEnum } from "@/shared/enums/SituacoesEnum";
|
|
||||||
|
|
||||||
export interface UserInterface {
|
|
||||||
user_id?: number;
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
position: string;
|
|
||||||
team: string;
|
|
||||||
status: SituacoesEnum; // 'A' ou 'I'
|
|
||||||
date_register?: string;
|
|
||||||
date_update?: string | null;
|
|
||||||
user_id_create: number | null,
|
|
||||||
user_id_update: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { UserInterface } from './UserInterface';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface que define as propriedades esperadas pelo componente UserTable.
|
|
||||||
*/
|
|
||||||
export interface UserTableInterface {
|
|
||||||
data: UserInterface[]; // Lista de usuários a exibir
|
|
||||||
onEdit: (user: UserInterface) => void; // Ação ao clicar em editar
|
|
||||||
onDelete: (user: UserInterface) => void; // Ação ao clicar em remover
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { z } from "zod";
|
|
||||||
import { SituacoesEnum } from "@/shared/enums/SituacoesEnum";
|
|
||||||
|
|
||||||
export const ClientSchema = z.object({
|
|
||||||
client_id: z.number().optional(),
|
|
||||||
|
|
||||||
// Validações Obrigatórias
|
|
||||||
name: z.string().min(3, { message: "O nome deve ter no mínimo 3 caracteres." }),
|
|
||||||
|
|
||||||
cns: z.string().min(1, { message: "O CNS é obrigatório." }),
|
|
||||||
// Dica: Se quiser validar o tamanho exato do CNS (geralmente 15 dígitos), use .length(15, "O CNS deve ter 15 dígitos")
|
|
||||||
|
|
||||||
// Campos Opcionais / Nulos
|
|
||||||
// O .or(z.literal("")) ajuda caso o input envie string vazia em vez de null
|
|
||||||
state: z.string().nullable().optional().or(z.literal("")),
|
|
||||||
city: z.string().nullable().optional().or(z.literal("")),
|
|
||||||
responsible: z.string().nullable().optional().or(z.literal("")),
|
|
||||||
consultant: z.string().nullable().optional().or(z.literal("")),
|
|
||||||
type_contract: z.string().nullable().optional().or(z.literal("")),
|
|
||||||
|
|
||||||
// Enumeração (Correção aplicada: nativeEnum para Enums do TypeScript)
|
|
||||||
status: z.nativeEnum(SituacoesEnum,{ message: "Selecione um status válido." }),
|
|
||||||
|
|
||||||
// Campos de Auditoria (Geralmente apenas para leitura ou ocultos)
|
|
||||||
date_register: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extrai o tipo TypeScript automaticamente a partir do Schema (opcional, mas útil)
|
|
||||||
export type ClientSchemaType = z.infer<typeof ClientSchema>;
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const GUsuarioLoginSchema = z.object({
|
||||||
|
login: z.string().min(1, 'O campo deve ser preenchido'),
|
||||||
|
senha_api: z.string().min(1, 'O campo deve ser preenchido'),
|
||||||
|
});
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schema de validação para a tabela `log`
|
|
||||||
* Baseado na DDL do banco de dados `monitoring.log`
|
|
||||||
*/
|
|
||||||
export const LogSchema = z.object({
|
|
||||||
// ID do log, gerado automaticamente pelo banco (AUTO_INCREMENT)
|
|
||||||
log_id: z.number().optional(),
|
|
||||||
|
|
||||||
// ID do cliente relacionado — campo obrigatório
|
|
||||||
client_id: z
|
|
||||||
.number({
|
|
||||||
message: "O campo client_id deve ser um número.",
|
|
||||||
})
|
|
||||||
.int()
|
|
||||||
.positive({ message: "O client_id deve ser um número positivo." }),
|
|
||||||
|
|
||||||
// Data e hora da inserção — gerada automaticamente pelo banco (CURRENT_TIMESTAMP)
|
|
||||||
date_post: z
|
|
||||||
.string()
|
|
||||||
.datetime({ message: "O campo date_post deve ser uma data/hora válida (ISO 8601)." })
|
|
||||||
.optional()
|
|
||||||
.nullable(),
|
|
||||||
|
|
||||||
// Campo JSON que armazena informações sobre o arquivo
|
|
||||||
// Pode ser um objeto com qualquer estrutura válida em JSON
|
|
||||||
file: z
|
|
||||||
.any()
|
|
||||||
.refine((val) => val !== undefined, {
|
|
||||||
message: "O campo file é obrigatório e deve conter um JSON válido.",
|
|
||||||
})
|
|
||||||
.refine(
|
|
||||||
(val) => typeof val === "object" && val !== null,
|
|
||||||
{ message: "O campo file deve ser um objeto JSON válido." }
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tipo TypeScript inferido automaticamente a partir do schema.
|
|
||||||
* Permite utilizar a tipagem do Zod em qualquer parte do código.
|
|
||||||
*/
|
|
||||||
export type LogSchemaType = z.infer<typeof LogSchema>;
|
|
||||||
19
src/packages/administrativo/schemas/TImovel/TImovelSchema.ts
Normal file
19
src/packages/administrativo/schemas/TImovel/TImovelSchema.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
export const TImovelSchema = z.object({
|
||||||
|
imovel_id: z.number().optional(),
|
||||||
|
tipo_classe: z.string().optional(),
|
||||||
|
tipo_registro: z.string().optional(),
|
||||||
|
data_registro: z.string().optional(),
|
||||||
|
numero: z.number().optional(),
|
||||||
|
numero_letra: z.string().optional(),
|
||||||
|
cidade: z.string().optional(),
|
||||||
|
cep: z.string().optional(),
|
||||||
|
uf: z.string().optional(),
|
||||||
|
tb_bairro_id: z.number().optional(),
|
||||||
|
cartorio: z.string().optional(),
|
||||||
|
livro: z.string().optional(),
|
||||||
|
cns: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TImovelFormValues = z.infer<typeof TImovelSchema>;
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schema de validação do login do usuário
|
|
||||||
* - Garante que email e senha sejam obrigatórios
|
|
||||||
* - `rememberMe` é opcional e booleano
|
|
||||||
*/
|
|
||||||
export const UserLoginSchema = z.object({
|
|
||||||
email: z.string().min(1, 'O campo deve ser preenchido'),
|
|
||||||
password: z.string().min(1, 'O campo deve ser preenchido'),
|
|
||||||
rememberMe: z.boolean().optional(),
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue