Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| db359d12bc |
1027 changed files with 2725 additions and 50411 deletions
117
.code-workspace
117
.code-workspace
|
|
@ -1,117 +0,0 @@
|
|||
{
|
||||
"folders": [{ "path": "D:/IIS/Orius/app" }],
|
||||
"settings": {
|
||||
// === GERAL ===
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit",
|
||||
"source.organizeImports": "explicit",
|
||||
},
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnType": false,
|
||||
"editor.minimap.enabled": false,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.autoSave": "onFocusChange",
|
||||
"telemetry.telemetryLevel": "off",
|
||||
"update.mode": "manual",
|
||||
|
||||
// === PERFORMANCE ===
|
||||
"files.watcherExclude": {
|
||||
"**/node_modules/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/build/**": true,
|
||||
"**/.next/**": true,
|
||||
"**/.git/**": true,
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/dist": true,
|
||||
"**/.next": true,
|
||||
"**/.git": true,
|
||||
},
|
||||
|
||||
// === FRONTEND ===
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features",
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features",
|
||||
},
|
||||
|
||||
// === TAILWIND ===
|
||||
"files.associations": {
|
||||
"*.css": "tailwindcss",
|
||||
},
|
||||
"tailwindCSS.includeLanguages": {
|
||||
"plaintext": "html",
|
||||
"javascript": "javascript",
|
||||
"typescriptreact": "typescriptreact",
|
||||
},
|
||||
|
||||
// === TERMINAIS ===
|
||||
"terminal.integrated.profiles.windows": {
|
||||
"Next.js Dev": {
|
||||
"path": "cmd.exe",
|
||||
"args": ["/k", "cd D:\\IIS\\Orius\\app && npm run dev"],
|
||||
},
|
||||
"Build & Preview": {
|
||||
"path": "cmd.exe",
|
||||
"args": ["/k", "cd D:\\IIS\\Orius\\app && npm run build && npm run start"],
|
||||
},
|
||||
"Git Bash": {
|
||||
"path": "C:\\Program Files\\Git\\bin\\bash.exe",
|
||||
},
|
||||
},
|
||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||
|
||||
// === GIT ===
|
||||
"git.enabled": true,
|
||||
"git.autorefresh": false,
|
||||
"git.fetchOnPull": true,
|
||||
"git.confirmSync": false,
|
||||
|
||||
// === VISUAL ===
|
||||
"workbench.colorTheme": "Default Dark Modern",
|
||||
"window.zoomLevel": 0,
|
||||
"breadcrumbs.enabled": true,
|
||||
"explorer.compactFolders": false,
|
||||
|
||||
// === MISC ===
|
||||
"files.exclude": {
|
||||
"**/.DS_Store": true,
|
||||
"**/*.log": true,
|
||||
},
|
||||
},
|
||||
"launch": {
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Next.js: Debug Development Server",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"port": 9229,
|
||||
},
|
||||
],
|
||||
},
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
// === FRONTEND CORE ===
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"ms-vscode.vscode-typescript-next",
|
||||
|
||||
// === DEV EXPERIENCE ===
|
||||
"formulahendry.code-runner",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"eamodio.gitlens",
|
||||
"mhutchie.git-graph",
|
||||
"donjayamanne.githistory",
|
||||
],
|
||||
},
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -39,5 +39,3 @@ yarn-error.log*
|
|||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
/src/config/app.json
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"arrowParens": "always",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
59
Dockerfile
59
Dockerfile
|
|
@ -1,59 +0,0 @@
|
|||
# ============================
|
||||
# STAGE 1 – Build
|
||||
# ============================
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copia pacotes e instala dependências
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copia o restante do código
|
||||
COPY . .
|
||||
|
||||
# ---------- Variáveis de build ----------
|
||||
# Estas variáveis são usadas pelo Next.js durante o "build"
|
||||
# para embutir no bundle do frontend.
|
||||
ARG NEXT_PUBLIC_ORIUS_APP_STATE
|
||||
ARG NEXT_PUBLIC_ORIUS_APP_API_URL
|
||||
ARG NEXT_PUBLIC_ORIUS_APP_API_PREFIX
|
||||
ARG NEXT_PUBLIC_ORIUS_APP_API_CONTENT_TYPE
|
||||
|
||||
ENV NEXT_PUBLIC_ORIUS_APP_STATE=$NEXT_PUBLIC_ORIUS_APP_STATE
|
||||
ENV NEXT_PUBLIC_ORIUS_APP_API_URL=$NEXT_PUBLIC_ORIUS_APP_API_URL
|
||||
ENV NEXT_PUBLIC_ORIUS_APP_API_PREFIX=$NEXT_PUBLIC_ORIUS_APP_API_PREFIX
|
||||
ENV NEXT_PUBLIC_ORIUS_APP_API_CONTENT_TYPE=$NEXT_PUBLIC_ORIUS_APP_API_CONTENT_TYPE
|
||||
|
||||
# ---------- Build ----------
|
||||
ENV NODE_ENV=production
|
||||
RUN npm run build
|
||||
|
||||
# ============================
|
||||
# STAGE 2 – Runner (standalone)
|
||||
# ============================
|
||||
FROM node:20-alpine AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# ---------- Variáveis em runtime ----------
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Copia apenas o necessário do build
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/package*.json ./
|
||||
|
||||
# ---------- Corrige permissões ----------
|
||||
RUN addgroup -S nodejs && adduser -S nextjs -G nodejs \
|
||||
&& mkdir -p .next/cache/images \
|
||||
&& chown -R nextjs:nodejs /app
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# ---------- Executa o servidor ----------
|
||||
CMD ["node", "server.js"]
|
||||
56
README.md
56
README.md
|
|
@ -1,58 +1,2 @@
|
|||
# saas_app
|
||||
|
||||
Criar envlocal para usar variaveis de ambiente no em desenvolvimento
|
||||
NEXT_PUBLIC_ORIUS_APP_STATE=GO
|
||||
NEXT_PUBLIC_ORIUS_APP_API_URL=<http://localhost:8000/>
|
||||
NEXT_PUBLIC_ORIUS_APP_API_PREFIX=api/v1/
|
||||
NEXT_PUBLIC_ORIUS_APP_API_CONTENT_TYPE=application/json
|
||||
|
||||
## Modo Debug
|
||||
|
||||
Abra Run → Add Configuration… → Attach to Node.js
|
||||
|
||||
Configure:
|
||||
|
||||
{
|
||||
"name": "Attach Next.js (9230)",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 9230,
|
||||
"restart": true,
|
||||
"smartStep": true,
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
}
|
||||
|
||||
npm run dev:debug
|
||||
|
||||
## onlyoffice
|
||||
|
||||
docker run -i -t -d -p 8081:80 --restart=always -e JWT_ENABLED=false onlyoffice/documentserver
|
||||
|
||||
## next em rede
|
||||
|
||||
```
|
||||
npx next dev -H 0.0.0.0
|
||||
```
|
||||
|
||||
```
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"dev:lan": "next dev -H 0.0.0.0" <-- Adicione esta linha
|
||||
},
|
||||
```
|
||||
|
||||
Como acessar no outro dispositivo
|
||||
Descubra seu IP Local:
|
||||
|
||||
No Windows (seu caso), abra um terminal (CMD ou PowerShell) e digite:
|
||||
|
||||
Bash
|
||||
|
||||
ipconfig
|
||||
Procure por Endereço IPv4 (geralmente começa com 192.168.x.x ou 10.0.x.x).
|
||||
|
||||
Acesse no navegador: No celular ou outro computador, digite: http://SEU_IP_AQUI:3000
|
||||
|
||||
Exemplo: <http://192.168.0.15:3000>
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
import js from '@eslint/js';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
||||
import importPlugin from 'eslint-plugin-import';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
ignores: ['node_modules/**', '.next/**', 'out/**', 'dist/**'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
globals: {
|
||||
React: true,
|
||||
JSX: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
react: reactPlugin,
|
||||
'react-hooks': reactHooks,
|
||||
'jsx-a11y': jsxA11y,
|
||||
import: importPlugin,
|
||||
},
|
||||
settings: {
|
||||
react: { version: 'detect' },
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/jsx-uses-react': 'off',
|
||||
'react/jsx-uses-vars': 'warn',
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
groups: [['builtin', 'external'], ['internal'], ['parent', 'sibling', 'index']],
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: '@/**',
|
||||
group: 'internal',
|
||||
position: 'after',
|
||||
},
|
||||
],
|
||||
alphabetize: { order: 'asc', caseInsensitive: true },
|
||||
'newlines-between': 'always',
|
||||
},
|
||||
],
|
||||
'import/no-duplicates': 'error',
|
||||
'import/newline-after-import': ['error', { count: 1 }],
|
||||
'no-unused-vars': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
@ -1,17 +1,7 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Gera build autônomo para rodar com "node server.js"
|
||||
output: 'standalone',
|
||||
|
||||
// Configurações gerais
|
||||
reactStrictMode: true,
|
||||
poweredByHeader: false,
|
||||
compress: true,
|
||||
|
||||
// Desativa verificações no build de produção
|
||||
eslint: { ignoreDuringBuilds: true },
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
export default nextConfig;
|
||||
|
|
|
|||
7245
package-lock.json
generated
7245
package-lock.json
generated
File diff suppressed because it is too large
Load diff
58
package.json
58
package.json
|
|
@ -1,89 +1,45 @@
|
|||
{
|
||||
"type": "module",
|
||||
"name": "saas",
|
||||
"version": "25.9.1",
|
||||
"name": "app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev:debug": "cross-env NEXT_USE_TURBOPACK=0 NODE_OPTIONS=\"--inspect=9230\" next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"postinstall": "shx mkdir -p public/libs && shx cp -r node_modules/tinymce public/libs/tinymce"
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^10.0.0",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@onlyoffice/document-editor-react": "^2.1.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-progress": "^1.1.7",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@tinymce/tinymce-react": "^6.3.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"cookies-next": "^6.1.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"framer-motion": "^12.23.24",
|
||||
"input-otp": "^1.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lucide-react": "^0.540.0",
|
||||
"next": "^15.5.3",
|
||||
"next": "15.4.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"react-masked-text": "^1.0.5",
|
||||
"recharts": "^3.3.0",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tinymce": "^8.1.2",
|
||||
"zod": "^4.0.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/date-fns": "^2.5.3",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.1",
|
||||
"@typescript-eslint/parser": "^8.46.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.0",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"shx": "^0.4.0",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "^8.46.1"
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const config = {
|
||||
plugins: ['@tailwindcss/postcss'],
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Creator: CorelDRAW 2018 (64 Bit) -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="109.504mm" height="32.45mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
|
||||
viewBox="0 0 4674.85 1385.32"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.fil0 {fill:#FF6600}
|
||||
.fil1 {fill:white;fill-rule:nonzero}
|
||||
]]>
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Camada_x0020_1">
|
||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
||||
<g id="_2536796572048">
|
||||
<path class="fil0" d="M444.51 337.84c440.36,-70.7 476.63,243.79 383.3,374.93 -121.36,170.53 -504.61,132.14 -589.48,-37.21 -31.1,-62.05 -10.18,-188.76 23.33,-238.81 35.98,-53.73 97.41,-85.19 182.85,-98.91zm-444.51 192.86c0,156.21 12.26,277.5 171.92,375.2 214.59,131.32 589.63,134.32 795.72,-31.29 174.6,-140.3 173.88,-487.9 -30.87,-624.12 -52.36,-34.83 -93.57,-53.67 -162.11,-73.16 -236.65,-67.31 -582.01,-30.52 -720.03,178.21 -25.47,38.51 -54.62,111.65 -54.62,175.17z"/>
|
||||
<path class="fil0" d="M3818.84 393.91c0,148.34 145.5,201.77 263.73,234.14 93.01,25.46 365.44,57.71 365.44,126.96 0,66.65 -152.26,76.59 -224.32,76.59 -221.11,0 -348.08,-81.05 -399.39,-93.01l0 196.95 192.2 43.05c87.27,14.66 138.58,22.6 234.54,22.6 441.47,0 507.61,-275.63 337.69,-419.75 -140.93,-119.53 -545.59,-120.82 -545.59,-203.95 0,-95.45 359.81,-45.21 409.3,-31.8l154.22 53.68 0 -186.02 -176.42 -42.42c-214.47,-43.37 -611.42,-48.39 -611.42,222.96z"/>
|
||||
<path class="fil0" d="M2631.61 755.01c0,86.12 62.34,149.87 115.84,185.07 127.76,84.05 323.75,68.39 463.32,16.58 29.5,-10.95 61.1,-25 91.99,-39.32 39.53,-18.31 67.28,-41.35 94.8,-41.97l0 103.95 218.85 0 0 -809.72 -218.85 0 0 492.39c0,18.46 -54.62,47.33 -68.92,56.91 -26.89,18 -50.44,32.94 -78.73,47.11 -130.98,65.59 -399.45,116.59 -399.45,-114.96l0 -481.45 -218.85 0 0 585.41z"/>
|
||||
<path class="fil0" d="M1515.5 289.97c0,-46.34 -5.47,-72.07 -5.47,-120.37l-218.84 0 0 809.72 218.84 0 0 -475.99c0,-25.16 113.08,-106.37 136.96,-120.18 84.99,-49.19 220.78,-69.84 320.2,-46.99 23.36,5.37 43.45,15 62.61,19.46l0 -186.02c-126.58,-10.54 -178.34,-55.19 -359.3,29.15 -83.29,38.81 -108.59,66.67 -154.99,91.22z"/>
|
||||
<polygon class="fil0" points="2166.56,979.32 2385.41,979.32 2385.41,169.6 2166.56,169.6 "/>
|
||||
<path class="fil0" d="M2172.04 131.3l218.85 0 0 -131.3c-30.09,7.01 -146.3,65.43 -180.57,82.04 -33.64,16.3 -38.27,6.4 -38.27,49.26z"/>
|
||||
</g>
|
||||
<path class="fil1" d="M99.26 1380.26l0 -225.54 -81.33 0 0 -27.47 193.01 0 0 27.47 -81.33 0 0 225.54 -30.36 0zm433.68 0l0 -253.02 155.79 0 0 27.47 -125.43 0 0 84.58 115.3 0 0 27.47 -115.3 0 0 86.02 127.23 0 0 27.47 -157.59 0zm578.27 5.06c-30.12,0 -54.1,-8.73 -71.93,-26.2 -17.83,-17.47 -26.75,-42.95 -26.75,-76.45l0 -57.83c0,-33.5 8.91,-58.98 26.75,-76.45 17.83,-17.47 41.81,-26.2 71.93,-26.2 29.88,0 52.83,8.31 68.86,24.94 16.03,16.62 24.04,39.4 24.04,68.31l0 1.81 -30 0 0 -2.89c0,-19.04 -5.12,-34.64 -15.36,-46.81 -10.24,-12.17 -26.08,-18.25 -47.53,-18.25 -21.44,0 -38.19,6.57 -50.24,19.7 -12.05,13.13 -18.07,31.51 -18.07,55.12l0 59.28c0,23.62 6.02,41.99 18.07,55.12 12.05,13.13 28.8,19.7 50.24,19.7 21.45,0 37.29,-6.08 47.53,-18.25 10.24,-12.17 15.36,-27.77 15.36,-46.81l0 -5.78 30 0 0 4.7c0,28.92 -8.01,51.69 -24.04,68.31 -16.02,16.63 -38.98,24.94 -68.86,24.94zm423.92 -5.06l0 -253.02 58.92 0 88.2 233.86 4.7 0 0 -233.86 30 0 0 253.02 -58.92 0 -87.83 -234.22 -5.06 0 0 234.22 -30 0zm615.13 5.06c-30.12,0 -54.16,-8.73 -72.11,-26.2 -17.95,-17.47 -26.93,-42.95 -26.93,-76.45l0 -57.83c0,-33.5 8.98,-58.98 26.93,-76.45 17.95,-17.47 41.99,-26.2 72.11,-26.2 30.36,0 54.52,8.73 72.47,26.2 17.95,17.47 26.93,42.95 26.93,76.45l0 57.83c0,33.5 -8.97,58.98 -26.93,76.45 -17.96,17.47 -42.11,26.2 -72.47,26.2zm0 -27.11c21.93,0 38.92,-6.57 50.96,-19.7 12.05,-13.13 18.07,-31.39 18.07,-54.76l0 -60c0,-23.37 -6.02,-41.63 -18.07,-54.76 -12.05,-13.13 -29.04,-19.7 -50.96,-19.7 -21.69,0 -38.55,6.57 -50.61,19.7 -12.05,13.13 -18.07,31.39 -18.07,54.76l0 60c0,23.37 6.02,41.63 18.07,54.76 12.05,13.13 28.92,19.7 50.61,19.7zm433.32 22.05l0 -253.02 30.36 0 0 225.54 127.96 0 0 27.47 -158.32 0zm565.25 5.06c-30.12,0 -54.16,-8.73 -72.11,-26.2 -17.95,-17.47 -26.93,-42.95 -26.93,-76.45l0 -57.83c0,-33.5 8.98,-58.98 26.93,-76.45 17.95,-17.47 41.99,-26.2 72.11,-26.2 30.36,0 54.52,8.73 72.47,26.2 17.95,17.47 26.93,42.95 26.93,76.45l0 57.83c0,33.5 -8.97,58.98 -26.93,76.45 -17.96,17.47 -42.11,26.2 -72.47,26.2zm0 -27.11c21.93,0 38.92,-6.57 50.96,-19.7 12.05,-13.13 18.07,-31.39 18.07,-54.76l0 -60c0,-23.37 -6.02,-41.63 -18.07,-54.76 -12.05,-13.13 -29.04,-19.7 -50.96,-19.7 -21.69,0 -38.55,6.57 -50.61,19.7 -12.05,13.13 -18.07,31.39 -18.07,54.76l0 60c0,23.37 6.02,41.63 18.07,54.76 12.05,13.13 28.92,19.7 50.61,19.7zm521.51 27.11c-18.8,0 -35.36,-3.91 -49.7,-11.74 -14.34,-7.83 -25.54,-19.34 -33.61,-34.52 -8.07,-15.18 -12.11,-33.98 -12.11,-56.39l0 -57.83c0,-33.5 8.91,-58.98 26.75,-76.45 17.83,-17.47 41.81,-26.2 71.93,-26.2 29.88,0 52.65,8.19 68.31,24.58 15.66,16.38 23.5,38.32 23.5,65.79l0 1.81 -30 0 0 -2.53c0,-12.05 -2.17,-22.77 -6.51,-32.17 -4.34,-9.4 -11.08,-16.81 -20.24,-22.23 -9.16,-5.42 -20.85,-8.13 -35.06,-8.13 -21.44,0 -38.19,6.57 -50.24,19.7 -12.05,13.13 -18.07,31.51 -18.07,55.12l0 59.28c0,23.62 6.02,41.99 18.07,55.12 12.05,13.13 28.92,19.7 50.61,19.7 21.2,0 36.81,-6.02 46.81,-18.07 10,-12.05 15,-28.07 15,-48.07l0 -6.14 -76.63 0 0 -26.02 106.27 0 0 120.36 -27.47 0 0 -27.47 -5.06 0c-2.65,5.3 -6.38,10.48 -11.2,15.54 -4.82,5.06 -11.33,9.16 -19.52,12.29 -8.19,3.13 -18.8,4.7 -31.81,4.7zm433.68 -5.06l0 -253.02 30.36 0 0 253.02 -30.36 0zm351.64 0l78.07 -253.02 53.49 0 78.08 253.02 -31.45 0 -19.52 -63.98 -107.71 0 -19.52 63.98 -31.45 0zm58.55 -91.45l92.17 0 -43.37 -143.13 -5.06 0 -43.73 143.13z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
Binary file not shown.
14
src/abstracts/Schema.ts
Normal file
14
src/abstracts/Schema.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
export default class Schema {
|
||||
|
||||
private _errors: any[] = []
|
||||
|
||||
get errors(): any {
|
||||
return this._errors;
|
||||
}
|
||||
|
||||
set errors(value: any) {
|
||||
this._errors.push(value);
|
||||
}
|
||||
|
||||
}
|
||||
11
src/actions/cookies/CookiesGet.ts
Normal file
11
src/actions/cookies/CookiesGet.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
'use client'
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export default async function CookiesGet() {
|
||||
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get('access_token');
|
||||
return token?.value;
|
||||
|
||||
}
|
||||
6
src/actions/json/Json.ts
Normal file
6
src/actions/json/Json.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import appConfig from '../../config/app.json';
|
||||
export default class Json {
|
||||
static execute() {
|
||||
return appConfig;
|
||||
}
|
||||
}
|
||||
11
src/actions/token/TokenGet.ts
Normal file
11
src/actions/token/TokenGet.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
'use server'
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export default async function TokenGet() {
|
||||
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get('access_token');
|
||||
return token?.value;
|
||||
|
||||
}
|
||||
9
src/actions/validations/empty.ts
Normal file
9
src/actions/validations/empty.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
export default function empty(data: any) {
|
||||
if (!data || !data === null || !data === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
'use client'
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent
|
||||
} from "@/components/ui/card";
|
||||
import { useGUsuarioReadHooks } from "@/app/(protected)/(administrativo)/_hooks/g_usuario/useGUsuarioReadHooks";
|
||||
import Usuario from "@/app/(protected)/(administrativo)/_interfaces/IGUsuario";
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
|
||||
export default function UsuarioDetalhes() {
|
||||
|
||||
const params = useParams();
|
||||
|
||||
const { usuario, fetchUsuario } = useGUsuarioReadHooks();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (params.id) {
|
||||
fetchUsuario({ usuario_id: Number(params.id) } as Usuario);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!usuario) return <Loading type={1} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="mb-4 grid gap-4 grid-cols-4">
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">
|
||||
Nome
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.nome_completo}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">
|
||||
CPF
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.cpf}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">
|
||||
Função
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.funcao}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">
|
||||
Email
|
||||
</div>
|
||||
<div className="text-xl">
|
||||
{usuario?.email}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
'use client'
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { z } from "zod"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { UsuarioFormSchema } from "../../../_schemas/GUsuarioSchema"
|
||||
|
||||
import {
|
||||
Button
|
||||
} from "@/components/ui/button"
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent
|
||||
} from "@/components/ui/card";
|
||||
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form"
|
||||
|
||||
import { useGUsuarioSaveHook } from "../../../_hooks/g_usuario/useGUsuarioSaveHook"
|
||||
|
||||
type FormValues = z.infer<typeof UsuarioFormSchema>
|
||||
|
||||
export default function UsuarioFormularioPage() {
|
||||
|
||||
const { usuario, saveUsuario } = useGUsuarioSaveHook();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(UsuarioFormSchema),
|
||||
defaultValues: {
|
||||
login: '',
|
||||
nome_completo: '',
|
||||
funcao: '',
|
||||
email: '',
|
||||
cpf: ''
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(values: FormValues) {
|
||||
saveUsuario(values);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="login"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Login</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nome_completo"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nome Completo</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="funcao"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Função</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="cpf"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Cpf</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="submit">
|
||||
Salvar
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
'use client'
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
} from "@/components/ui/card"
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
|
||||
import Usuario from "../../_interfaces/IGUsuario";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import { useGUsuarioIndexHook } from "../../_hooks/g_usuario/useGUsuarioIndexHook";
|
||||
import { useEffect } from "react";
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
|
||||
export default function UsuarioPage() {
|
||||
|
||||
const { usuarios, fetchUsuarios } = useGUsuarioIndexHook();
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsuarios();
|
||||
}, []);
|
||||
|
||||
if (!usuarios) return <Loading type={2} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="text-2xl font-semibold">
|
||||
Usuarios
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<Button asChild>
|
||||
<Link href="/usuarios/formulario">
|
||||
+ Usuário
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="text-center">
|
||||
#
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Situação
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
CPF
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Login / Sigla / Nome
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Função
|
||||
</TableHead>
|
||||
<TableHead></TableHead>
|
||||
<TableHead></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{usuarios.map((usuario: Usuario) => (
|
||||
<TableRow key={usuario.usuario_id} className="cursor-pointer">
|
||||
<TableCell className="text-center">
|
||||
{usuario.usuario_id}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{usuario.situacao}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{usuario.cpf}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="font-semibold text-xs">
|
||||
{usuario.login} - {usuario.sigla}
|
||||
</div>
|
||||
<div className="text-base">
|
||||
{usuario.nome_completo}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="text-base">
|
||||
{usuario.funcao}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button asChild>
|
||||
<Link href={`/usuarios/${usuario.usuario_id}/detalhes`}>
|
||||
Detalhes
|
||||
</Link>
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
export default async function GUsuarioDeleteData(usuarioId: number) {
|
||||
|
||||
const api = new API();
|
||||
|
||||
const response = await api.send({
|
||||
'method': Methods.DELETE,
|
||||
'endpoint': `administrativo/usuarios/${usuarioId}`
|
||||
});
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
export default async function GUsuarioIndexData() {
|
||||
|
||||
const api = new API();
|
||||
|
||||
const response = await api.send({
|
||||
'method': Methods.GET,
|
||||
'endpoint': `administrativo/usuarios/`
|
||||
});
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
'use server'
|
||||
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
import API from "@/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/usuarios/login`,
|
||||
body: form
|
||||
});
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
export default async function GUsuarioReadData(usuarioId: number) {
|
||||
|
||||
const api = new API();
|
||||
|
||||
const response = await api.send({
|
||||
'method': Methods.GET,
|
||||
'endpoint': `administrativo/usuarios/${usuarioId}`
|
||||
});
|
||||
|
||||
return response
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
'use server'
|
||||
|
||||
import API from "@/services/api/Api";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
export default async function GUsuarioSaveData(form: any) {
|
||||
|
||||
const api = new API();
|
||||
|
||||
const response = await api.send({
|
||||
'method': Methods.POST,
|
||||
'endpoint': `administrativo/usuarios/`,
|
||||
'body': form
|
||||
});
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioIndex from "../../_services/g_usuario/GUsuarioIndex";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioIndexHook = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuarios, setUsuarios] = useState<Usuario[] | null>(null);
|
||||
|
||||
const fetchUsuarios = async () => {
|
||||
|
||||
const response = await GUsuarioIndex();
|
||||
|
||||
setUsuarios(response.data);
|
||||
|
||||
// Define os dados do componente de resposta (toast, modal, etc)
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuarios, fetchUsuarios }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioRead from "../../_services/g_usuario/GUsuarioRead";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioReadHooks = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuario, setUsuario] = useState<Usuario>();
|
||||
|
||||
const fetchUsuario = async (Usuario: Usuario) => {
|
||||
|
||||
const response = await GUsuarioRead(Usuario.usuario_id);
|
||||
|
||||
setUsuario(response.data);
|
||||
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuario, fetchUsuario }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
'use client'
|
||||
|
||||
import { useState } from "react"
|
||||
import Usuario from "../../_interfaces/IGUsuario"
|
||||
import GUsuarioSave from "../../_services/g_usuario/GUsuarioSave";
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
|
||||
export const useGUsuarioSaveHook = () => {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [usuario, setUsuario] = useState<Usuario>();
|
||||
|
||||
const saveUsuario = async (Usuario: any) => {
|
||||
|
||||
const response = await GUsuarioSave(Usuario);
|
||||
|
||||
setUsuario(response.data);
|
||||
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { usuario, saveUsuario }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
export default interface Usuario {
|
||||
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,
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const UsuarioFormSchema = z.object({
|
||||
trocarsenha: z.string().optional(),
|
||||
login: z.string().optional(),
|
||||
senha: z.string().optional(),
|
||||
situacao: z.string().optional(),
|
||||
nome_completo: z.string().optional(),
|
||||
funcao: z.string().optional(),
|
||||
assina: z.string().optional(),
|
||||
sigla: z.string().optional(),
|
||||
usuario_tab: z.string().optional(),
|
||||
ultimo_login: z.string().optional(),
|
||||
ultimo_login_regs: z.string().optional(),
|
||||
data_expiracao: z.string().optional(),
|
||||
senha_anterior: z.string().optional(),
|
||||
andamento_padrao: z.string().optional(),
|
||||
lembrete_pergunta: z.string().optional(),
|
||||
lembrete_resposta: z.string().optional(),
|
||||
andamento_padrao2: z.string().optional(),
|
||||
receber_mensagem_arrolamento: z.string().optional(),
|
||||
email: z.string().optional(),
|
||||
assina_certidao: z.string().optional(),
|
||||
receber_email_penhora: z.string().optional(),
|
||||
foto: z.string().optional(),
|
||||
nao_receber_chat_todos: z.string().optional(),
|
||||
pode_alterar_caixa: z.string().optional(),
|
||||
receber_chat_certidao_online: z.string().optional(),
|
||||
receber_chat_cancelamento: z.string().optional(),
|
||||
cpf: z.string().optional(),
|
||||
somente_leitura: z.string().optional(),
|
||||
receber_chat_envio_onr: z.string().optional(),
|
||||
tipo_usuario: z.string().optional(),
|
||||
senha_api: z.string().optional(),
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
'use server'
|
||||
|
||||
import GUsuarioIndexData from "../../_data/g_usuario/GUsuarioIndexData"
|
||||
|
||||
export default async function GUsuarioIndex() {
|
||||
|
||||
const response = await GUsuarioIndexData();
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
'use server'
|
||||
|
||||
import {
|
||||
cookies
|
||||
} from "next/headers";
|
||||
|
||||
import GUsuarioLoginData from "../../_data/g_usuario/GUsuarioLoginData"
|
||||
import { redirect } from "next/navigation";
|
||||
import empty from "@/actions/validations/empty";
|
||||
|
||||
export default async function GUsuarioLoginService(form: any) {
|
||||
|
||||
const response = await GUsuarioLoginData(form);
|
||||
|
||||
// Verifica se localizou o usuário
|
||||
if (response.data.usuario_id <= 0) {
|
||||
|
||||
return {
|
||||
'code': 404,
|
||||
'message': 'Não foi localizado o usuário'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set("access_token", response.data.token, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "strict",
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24,
|
||||
});
|
||||
|
||||
redirect("/usuarios");
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'use server'
|
||||
|
||||
import GUsuarioReadData from "../../_data/g_usuario/GUsuarioReadData";
|
||||
|
||||
export default async function GUsuarioRead(usuarioId: number) {
|
||||
|
||||
// Verifica se o id informado é válido
|
||||
if (usuarioId <= 0) {
|
||||
return {
|
||||
'code': 400,
|
||||
'message': 'Usuário informado inválido',
|
||||
}
|
||||
}
|
||||
|
||||
return await GUsuarioReadData(usuarioId);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
'use server'
|
||||
|
||||
import GUsuarioSaveData from "../../_data/g_usuario/GUsuarioSaveData";
|
||||
|
||||
export default async function GUsuarioSave(form: any) {
|
||||
|
||||
return await GUsuarioSaveData(form);
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TTBAndamentoServicoIndex from '@/packages/administrativo/components/TTBAndamentoServico/TTBAndamentoServicoIndex';
|
||||
|
||||
export default function TAtoParteTipo() {
|
||||
return <TTBAndamentoServicoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TAtoParteTipoIndex from '@/packages/administrativo/components/TAtoParteTipo/TAtoParteTipoIndex';
|
||||
|
||||
export default function TAtoParteTipo() {
|
||||
return <TAtoParteTipoIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBBairroIndex from '@/packages/administrativo/components/GTBBairro/GTBBairroIndex';
|
||||
|
||||
export default function GCidadePage() {
|
||||
return <GTBBairroIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GCartorioIndex from '@/packages/administrativo/components/GCartorio/GCartorioIndex';
|
||||
|
||||
export default function GCartorioPage() {
|
||||
return <GCartorioIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecTipoNaturezaIndex from '@/packages/administrativo/components/TCensecTipoNatureza/TCensecTipoNaturezaIndex';
|
||||
|
||||
export default function TCensecTipoNaturezaPage() {
|
||||
return <TCensecTipoNaturezaIndex />;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export default function TCensecPage() {
|
||||
return <div></div>;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecQualidadeIndex from '@/packages/administrativo/components/TCensecQualidade/TCensecQualidadeIndex';
|
||||
|
||||
export default function TCensecQualidadePage() {
|
||||
return <TCensecQualidadeIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecIndex from '@/packages/administrativo/components/TCensec/TCensecIndex';
|
||||
|
||||
export default function GTBEstadoCivilPage() {
|
||||
return <TCensecIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecNaturezaLitigioIndex from '@/packages/administrativo/components/TCensecNaturezaLitigio/TCensecNaturezaLitigioIndex';
|
||||
|
||||
export default function GCidadePage() {
|
||||
return <TCensecNaturezaLitigioIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecQualidadeIndex from '@/packages/administrativo/components/TCensecQualidade/TCensecQualidadeIndex';
|
||||
|
||||
export default function GTBEstadoCivilPage() {
|
||||
return <TCensecQualidadeIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TCensecTipoAtoIndex from '@/packages/administrativo/components/TCensecTipoAto/TCensecTipoAtoIndex';
|
||||
|
||||
export default function GTBEstadoCivilPage() {
|
||||
return <TCensecTipoAtoIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GCidadeIndex from '@/packages/administrativo/components/GCidade/GCidadeIndex';
|
||||
|
||||
export default function GCidadePage() {
|
||||
return <GCidadeIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GCalculoIndex from '@/packages/administrativo/components/GCalculo/GCalculoIndex';
|
||||
|
||||
export default function GEmolumentoPeriodoPage() {
|
||||
return <GCalculoIndex />;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
import GEmolumentoItemIndex from '@/packages/administrativo/components/GEmolumentoItem/GEmolumentoItemIndex';
|
||||
|
||||
export default function GGramaticaPage() {
|
||||
const params = useParams();
|
||||
|
||||
return (
|
||||
<GEmolumentoItemIndex
|
||||
emolumento_id={Number(params.emolumentoId)}
|
||||
emolumento_periodo_id={Number(params.emolumentoPeriodoId)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GEmolumentoIndex from '@/packages/administrativo/components/GEmolumento/GEmolumentoIndex';
|
||||
|
||||
export default function GEmolumentoPeriodoPage() {
|
||||
return <GEmolumentoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GEmolumentoPeriodoIndex from '@/packages/administrativo/components/GEmolumentoPeriodo/GEmolumentoPeriodoIndex';
|
||||
|
||||
export default function GEmolumentoPeriodoPage() {
|
||||
return <GEmolumentoPeriodoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GGramaticaIndex from '@/packages/administrativo/components/GGramatica/GGramaticaIndex';
|
||||
|
||||
export default function GGramaticaPage() {
|
||||
return <GGramaticaIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TImovelDashboard from '@/packages/administrativo/components/TImovel/TImovelDashboard';
|
||||
|
||||
export default function TImovelDashboardPage() {
|
||||
return <TImovelDashboard />;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TImovelIndex from '@/packages/administrativo/components/TImovel/TImovelIndex';
|
||||
|
||||
export default function TImovelRuralPage() {
|
||||
return (
|
||||
<TImovelIndex
|
||||
pageTitle="Imóveis Rurais"
|
||||
pageDescription="Gerenciamento de imóveis rurais"
|
||||
tipoClasse={3}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBTipoLogradouroIndex from '@/packages/administrativo/components/GTBTipoLogradouro/GTBTipoLogradouroIndex';
|
||||
|
||||
export default function GMedidaTipoPage() {
|
||||
return <GTBTipoLogradouroIndex />;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TImovelIndex from '@/packages/administrativo/components/TImovel/TImovelIndex';
|
||||
|
||||
export default function TImovelUrbanoPage() {
|
||||
return (
|
||||
<TImovelIndex
|
||||
pageTitle="Imóveis Urbanos"
|
||||
pageDescription="Gerenciamento de imóveis urbanos"
|
||||
tipoClasse={1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GMedidaTipoIndex from '@/packages/administrativo/components/GMedidaTipo/GMedidaTipoIndex';
|
||||
|
||||
export default function GMedidaTipoPage() {
|
||||
return <GMedidaTipoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GNaturezaIndex from '@/packages/administrativo/components/GNatureza/GNaturezaIndex';
|
||||
|
||||
export default function GNaturezaPage() {
|
||||
return <GNaturezaIndex sistema_id={2} />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TPessoaDashboard from '@/packages/administrativo/components/TPessoa/TPessoaDashboard';
|
||||
|
||||
export default function TPessoaDashboardPage() {
|
||||
return <TPessoaDashboard />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBEstadoCivilIndex from '@/packages/administrativo/components/GTBEstadoCivil/GTBEstadoCivilIndex';
|
||||
|
||||
export default function GTBEstadoCivilPage() {
|
||||
return <GTBEstadoCivilIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TPessoaFisicaIndex from '@/packages/administrativo/components/TPessoa/TPessoaFisica/TPessoaFisicaIndex';
|
||||
|
||||
export default function TPessoaFisica() {
|
||||
return <TPessoaFisicaIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import TPessoaJuridicaIndex from '@/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaIndex';
|
||||
|
||||
export default function TPessoaFisica() {
|
||||
return <TPessoaJuridicaIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBProfissaoIndex from '@/packages/administrativo/components/GTBProfissao/GTBProfissaoIndex';
|
||||
|
||||
export default function GTBEstadoCivilPage() {
|
||||
return <GTBProfissaoIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBRegimeBensIndex from '@/packages/administrativo/components/GTBRegimeBens/GTBRegimeBensIndex';
|
||||
|
||||
export default function GTBRegimeBensPage() {
|
||||
return <GTBRegimeBensIndex />;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import GTBRegimeComunhaoIndex from '@/packages/administrativo/components/GTBRegimeComunhao/GTBRegimeComunhaoIndex';
|
||||
|
||||
export default function GTBRegimeBensPage() {
|
||||
return <GTBRegimeComunhaoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TTBReconhecimentoTipoIndex from '@/packages/administrativo/components/TTBReconhecimentoTipo/TTBReconhecimentoTipoIndex';
|
||||
|
||||
export default function TAtoParteTipo() {
|
||||
return <TTBReconhecimentoTipoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GSeloGrupoIndex from '@/packages/administrativo/components/GSeloGrupo/GSeloGrupoIndex';
|
||||
|
||||
export default function GSeloGrupoPage() {
|
||||
return <GSeloGrupoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TServicoTipoIndex from '@/packages/administrativo/components/TServicoTipo/TServicoTipoIndex';
|
||||
|
||||
export default function TServicoTipoPage() {
|
||||
return <TServicoTipoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import GNaturezaTituloIndex from '@/packages/administrativo/components/GNaturezaTitulo/GNaturezaTituloIndex';
|
||||
|
||||
export default function GNaturezaPage() {
|
||||
return <GNaturezaTituloIndex sistema_id={2} />;
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import MainEditor from '@/components/MainEditor';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useTMinutaReadHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaReadHook';
|
||||
import { TMinutaInterface } from '@/packages/administrativo/interfaces/TMinuta/TMinutaInterface';
|
||||
import Loading from '@/shared/components/loading/loading';
|
||||
|
||||
export default function TMinutaDetalhes() {
|
||||
const params = useParams();
|
||||
const { tMinuta, fetchTMinuta } = useTMinutaReadHook();
|
||||
const [editorContent, setEditorContent] = useState<string | null>(null); // Inicialmente nulo até o texto ser carregado
|
||||
|
||||
useEffect(() => {
|
||||
if (params.id) {
|
||||
fetchTMinuta({ t_minuta_id: Number(params.id) } as TMinutaInterface);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (tMinuta?.texto) {
|
||||
setEditorContent(tMinuta.texto); // Atualiza o conteúdo assim que estiver disponível
|
||||
}
|
||||
}, [tMinuta]); // Dependência de `tMinuta` para que a atualização aconteça quando os dados chegarem
|
||||
|
||||
const handleEditorChange = (content: string) => {
|
||||
setEditorContent(content); // Atualiza o estado com o conteúdo do editor
|
||||
};
|
||||
|
||||
if (!tMinuta) return <Loading type={1} />;
|
||||
|
||||
// Renderiza o editor apenas se o texto foi carregado
|
||||
if (editorContent === null) {
|
||||
return <Loading type={1} />; // Pode mostrar um carregando ou qualquer outra coisa enquanto o conteúdo não está disponível
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<div className="mb-4 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">Descrição</div>
|
||||
<div className="text-xl">{tMinuta.descricao}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-semibold">Situação</div>
|
||||
<div className="text-xl">{tMinuta.situacao === 'A' ? 'Ativo' : 'Inativo'}</div>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<div className="text-2xl font-semibold">Texto</div>
|
||||
<MainEditor
|
||||
initialValue={editorContent} // Passa o conteúdo do editor
|
||||
onEditorChange={handleEditorChange} // Função que atualiza o estado
|
||||
margins={{
|
||||
top: '2',
|
||||
bottom: '2',
|
||||
left: '2',
|
||||
right: '2',
|
||||
}}
|
||||
size={{ width: 794, height: 1123 }} // Você pode ajustar o tamanho aqui
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import z from 'zod';
|
||||
|
||||
import MainEditor from '@/components/MainEditor';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useTMinutaSaveHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaSaveHook';
|
||||
import { TMinutaSchema } from '@/packages/administrativo/schemas/TMinuta/TMinutaSchema';
|
||||
|
||||
type FormValues = z.infer<typeof TMinutaSchema>;
|
||||
|
||||
export default function TMinutaForm() {
|
||||
const { tMinuta, saveTMinuta } = useTMinutaSaveHook();
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(TMinutaSchema),
|
||||
defaultValues: {
|
||||
natureza_id: undefined,
|
||||
descricao: '',
|
||||
situacao: 'A',
|
||||
texto: '',
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(values: FormValues) {
|
||||
saveTMinuta(values);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
{/* Descrição */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="descricao"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Descrição</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Digite a descrição da minuta" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Situação */}
|
||||
<Controller
|
||||
name="situacao"
|
||||
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>{field.value === 'A' ? 'Ativo' : 'Inativo'}</Label>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Editor de Texto */}
|
||||
<Controller
|
||||
name="texto"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<MainEditor
|
||||
initialValue={field.value || ''}
|
||||
onEditorChange={field.onChange}
|
||||
margins={{ top: '0', bottom: '0', left: '0', right: '0' }}
|
||||
size={{ width: 794, height: 1123 }}
|
||||
/>
|
||||
{form.formState.errors.texto && (
|
||||
<p className="mt-2 text-sm text-red-500">
|
||||
{form.formState.errors.texto.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">Salvar</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import TMinutaForm from '@/packages/administrativo/components/TMinuta/TMinutaForm';
|
||||
import TMinutaTable from '@/packages/administrativo/components/TMinuta/TMinutaTable';
|
||||
import { useTMinutaIndexHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaIndexHook';
|
||||
import { useTMinutaReadHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaReadHook';
|
||||
import { useTMinutaRemoveHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaRemoveHook';
|
||||
import { useTMinutaSaveHook } from '@/packages/administrativo/hooks/TMinuta/useTMinutaSaveHook';
|
||||
import { TMinutaInterface } from '@/packages/administrativo/interfaces/TMinuta/TMinutaInterface';
|
||||
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
||||
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
|
||||
import Loading from '@/shared/components/loading/loading';
|
||||
import Header from '@/shared/components/structure/Header';
|
||||
|
||||
export default function TMinutaPage() {
|
||||
// Hooks de leitura e escrita
|
||||
const { tMinuta, fetchTMinuta } = useTMinutaIndexHook();
|
||||
const { saveTMinuta } = useTMinutaSaveHook();
|
||||
const { removeTMinuta } = useTMinutaRemoveHook();
|
||||
|
||||
// Estados
|
||||
const [selectedMinuta, setSelectedMinuta] = useState<TMinutaInterface | null>(null);
|
||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||
const [itemToDelete, setItemToDelete] = useState<TMinutaInterface | null>(null);
|
||||
|
||||
// Hook de confirmação
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
|
||||
// Abertura do formulário
|
||||
const handleOpenForm = useCallback((data: TMinutaInterface | null) => {
|
||||
setSelectedMinuta(data);
|
||||
setIsFormOpen(true);
|
||||
}, []);
|
||||
|
||||
// Ação de clique em remover
|
||||
const handleConfirmDelete = useCallback(
|
||||
(item: TMinutaInterface) => {
|
||||
setItemToDelete(item);
|
||||
openConfirmDialog();
|
||||
},
|
||||
[openConfirmDialog],
|
||||
);
|
||||
|
||||
// Remoção da minuta após confirmação
|
||||
const handleDelete = useCallback(async () => {
|
||||
if (!itemToDelete) return;
|
||||
|
||||
await removeTMinuta(itemToDelete);
|
||||
await fetchTMinuta();
|
||||
setItemToDelete(null);
|
||||
handleCancel();
|
||||
}, [itemToDelete, removeTMinuta, fetchTMinuta, handleCancel]);
|
||||
|
||||
// Fetch inicial
|
||||
useEffect(() => {
|
||||
fetchTMinuta();
|
||||
}, []);
|
||||
|
||||
// Loading enquanto carrega
|
||||
if (tMinuta === undefined) {
|
||||
return <Loading type={2} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Cabeçalho */}
|
||||
<Header
|
||||
title="Minutas"
|
||||
description="Gerenciamento de minutas de atos notariais"
|
||||
buttonText="Nova Minuta"
|
||||
buttonAction={() => handleOpenForm(null)}
|
||||
/>
|
||||
|
||||
{/* Tabela */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<TMinutaTable data={tMinuta} onEdit={handleOpenForm} onDelete={handleConfirmDelete} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Diálogo de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir a minuta "${itemToDelete?.descricao}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import '../globals.css';
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
import { AppSidebar } from '@/components/app-sidebar';
|
||||
import { ThemeProvider } from '@/components/theme-provider';
|
||||
import { ResponseProvider } from '../_response/ResponseContext';
|
||||
import { AppSidebar } from "@/components/app-sidebar"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
|
|
@ -11,42 +11,45 @@ import {
|
|||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
|
||||
import Response from '../../shared/components/response/response';
|
||||
import { ResponseProvider } from '../../shared/components/response/ResponseContext';
|
||||
} from "@/components/ui/breadcrumb"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import {
|
||||
SidebarInset,
|
||||
SidebarProvider,
|
||||
SidebarTrigger,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import Response from "../_response/response";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: '--font-geist-mono',
|
||||
subsets: ['latin'],
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'SAAS - Orius Tecnologia',
|
||||
description: 'Evolução tecnológica com toque humano',
|
||||
icons: {
|
||||
icon: '/images/favicon.ico',
|
||||
},
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||
<ThemeProvider>
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<SidebarProvider>
|
||||
<AppSidebar />
|
||||
|
||||
<SidebarInset>
|
||||
<header className="mb-4 flex h-16 shrink-0 items-center gap-2 border-b-1 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
||||
<div className="flex items-center gap-2 px-4">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator
|
||||
|
|
@ -56,7 +59,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem className="hidden md:block">
|
||||
<BreadcrumbLink href="#">Building Your Application</BreadcrumbLink>
|
||||
<BreadcrumbLink href="#">
|
||||
Building Your Application
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator className="hidden md:block" />
|
||||
<BreadcrumbItem>
|
||||
|
|
@ -66,17 +71,15 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||
</Breadcrumb>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<ResponseProvider>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
{children}
|
||||
<Toaster richColors position="top-center" />
|
||||
<Toaster position="top-center" />
|
||||
<Response />
|
||||
</div>
|
||||
</ResponseProvider>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import useGUsuarioGetJWTHook from '@/shared/hooks/auth/useGUsuarioGetJWTHook';
|
||||
|
||||
export default function Page() {
|
||||
const { userAuthenticated } = useGUsuarioGetJWTHook();
|
||||
|
||||
// Inicializa time como null para renderizar só no cliente
|
||||
const [time, setTime] = useState<Date | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setTime(new Date()); // define data inicial no cliente
|
||||
const interval = setInterval(() => setTime(new Date()), 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Se ainda não temos a hora, renderiza nada para evitar mismatch
|
||||
if (!time || !userAuthenticated?.data) return null;
|
||||
|
||||
const hours = time.getHours();
|
||||
const greeting = hours < 12 ? 'Bom dia' : hours < 18 ? 'Boa tarde' : 'Boa noite';
|
||||
|
||||
const formattedDate = time.toLocaleDateString('pt-BR', {
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
});
|
||||
const formattedTime = time.toLocaleTimeString('pt-BR');
|
||||
|
||||
return (
|
||||
<main className="fixed inset-0 flex flex-col items-center justify-center overflow-hidden text-gray-800 select-none">
|
||||
<section className="max-w-md px-4 text-center">
|
||||
<h1 className="mb-2 text-4xl font-bold">
|
||||
{greeting}, <span className="text-blue-600">{userAuthenticated.data.nome}</span> 👋
|
||||
</h1>
|
||||
|
||||
<p className="mb-6 text-sm text-gray-600">
|
||||
Hoje é <strong>{formattedDate}</strong>, {formattedTime}
|
||||
</p>
|
||||
|
||||
<div className="rounded-2xl p-6">
|
||||
<p className="leading-relaxed text-gray-700">
|
||||
Que bom ter você de volta! 🎉 Aproveite o seu dia e continue alcançando seus objetivos.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TAtoForm from '@/packages/servicos/components/TAto/TAtoForm';
|
||||
|
||||
export default function TAtoFormPage() {
|
||||
return <TAtoForm />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TAtoIndex from '@/packages/servicos/components/TAto/TAtoIndex';
|
||||
|
||||
export default function TServicoAToPag() {
|
||||
return <TAtoIndex />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TServicoPedidoDashboard from '@/packages/servicos/components/TServicoPedido/TServicoPedidoDashboard';
|
||||
|
||||
export default function TServicoPedidoPage() {
|
||||
return <TServicoPedidoDashboard />;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
import TServicoPedidoDetails from '@/packages/servicos/components/TServicoPedido/TServicoPedidoDetails';
|
||||
|
||||
export default function TServicoPedidoDetailsPage() {
|
||||
const params = useParams();
|
||||
|
||||
return <TServicoPedidoDetails servico_pedido_id={Number(params.servicoPedidoId)} />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TServicoPedidoIndex from '@/packages/servicos/components/TServicoPedido/TServicoPedidoIndex';
|
||||
|
||||
export default function TServicoPedidoPage() {
|
||||
return <TServicoPedidoIndex />;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
import TServicoPedidoForm from '@/packages/servicos/components/TServicoPedido/TServicoPedidoForm';
|
||||
|
||||
export default function TServicoPedidoPage() {
|
||||
const params = useParams();
|
||||
|
||||
return <TServicoPedidoForm servico_pedido_id={Number(params.servicoPedidoId)} />;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import TServicoPedidoForm from '@/packages/servicos/components/TServicoPedido/TServicoPedidoForm';
|
||||
|
||||
export default function TServicoPedidoPage() {
|
||||
return <TServicoPedidoForm />;
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import '../../globals.css';
|
||||
import { Suspense } from 'react';
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "../../globals.css";
|
||||
import { Suspense } from "react";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: '--font-geist-mono',
|
||||
subsets: ['latin'],
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
|
@ -25,7 +25,9 @@ export default function RootLayout({
|
|||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { LoginForm } from '@/components/login-form';
|
||||
import { LoginForm } from "@/components/login-form"
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
|
|
@ -7,5 +7,5 @@ export default function LoginPage() {
|
|||
<LoginForm />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogAction,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp';
|
||||
|
||||
interface ConfirmExclusionProps {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
description?: string;
|
||||
expectedCode: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
onResendCode: () => void; // Função para reenviar o código
|
||||
}
|
||||
|
||||
export default function ConfirmExclusion({
|
||||
isOpen,
|
||||
title,
|
||||
description,
|
||||
expectedCode,
|
||||
confirmText = 'Continuar',
|
||||
cancelText = 'Cancelar',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
onResendCode, // A função para reenvio do código
|
||||
}: ConfirmExclusionProps) {
|
||||
const [code, setCode] = useState('');
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
const [isResending, setIsResending] = useState(false); // Novo estado para controle de envio do código
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
setCode(value);
|
||||
setIsValid(value === expectedCode);
|
||||
};
|
||||
|
||||
const handleResendCode = async () => {
|
||||
setIsResending(true);
|
||||
try {
|
||||
await onResendCode(); // Chamando a função de reenvio
|
||||
} catch (error) {
|
||||
console.error('Erro ao reenviar código', error);
|
||||
} finally {
|
||||
setIsResending(false); // Resetando o estado de envio
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog open={isOpen} onOpenChange={(open) => !open && onCancel()}>
|
||||
<AlertDialogContent className="mx-auto max-w-lg p-6">
|
||||
<AlertDialogHeader className="text-center">
|
||||
<AlertDialogTitle className="text-center text-xl font-semibold">{title}</AlertDialogTitle>
|
||||
{description && (
|
||||
<AlertDialogDescription className="text-muted-foreground mt-2 text-center text-sm">
|
||||
{description}
|
||||
</AlertDialogDescription>
|
||||
)}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<div className="text-muted-foreground space-y-4 py-4 text-sm">
|
||||
<div className="flex justify-center">
|
||||
<InputOTP maxLength={expectedCode.length} value={code} onChange={handleChange}>
|
||||
<InputOTPGroup className="flex gap-4">
|
||||
{expectedCode.split('').map((_, index) => (
|
||||
<InputOTPSlot
|
||||
key={index}
|
||||
index={index}
|
||||
className="h-12 w-12 rounded-lg border-2 border-gray-300 text-center text-lg focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
))}
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
|
||||
{/* Botão "Reenviar Código" */}
|
||||
<div className="mt-4 flex items-center justify-center gap-2 text-center">
|
||||
<AlertDialogDescription>Não recebeu o código?</AlertDialogDescription>
|
||||
<button
|
||||
onClick={handleResendCode}
|
||||
className={`cursor-pointer font-semibold text-blue-500 ${isResending ? 'cursor-not-allowed text-gray-400' : 'hover:text-blue-600'}`}
|
||||
disabled={isResending} // Desabilita o botão enquanto o código está sendo reenviado
|
||||
>
|
||||
{isResending ? 'Enviando...' : 'Reenviar Código'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={onCancel} className="text-gray-600 hover:text-gray-800">
|
||||
{cancelText}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onConfirm} disabled={!isValid}>
|
||||
{confirmText}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
|
||||
interface UseConfirmExclusionOptions {
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
expectedCode: string; // código que o usuário precisa digitar
|
||||
}
|
||||
|
||||
export function useConfirmExclusion({
|
||||
onConfirm,
|
||||
onCancel,
|
||||
expectedCode,
|
||||
}: UseConfirmExclusionOptions) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [code, setCode] = useState('');
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
|
||||
const openDialog = useCallback(() => setIsOpen(true), []);
|
||||
const closeDialog = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
setCode(''); // limpa o código quando fecha
|
||||
setIsValid(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
if (isValid) {
|
||||
onConfirm?.();
|
||||
closeDialog();
|
||||
}
|
||||
}, [isValid, onConfirm, closeDialog]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
onCancel?.();
|
||||
closeDialog();
|
||||
}, [onCancel, closeDialog]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: string) => {
|
||||
setCode(value);
|
||||
setIsValid(value === expectedCode);
|
||||
},
|
||||
[expectedCode],
|
||||
);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
code,
|
||||
isValid,
|
||||
openDialog,
|
||||
closeDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
handleChange,
|
||||
};
|
||||
}
|
||||
18
src/app/_components/loading/loading.tsx
Normal file
18
src/app/_components/loading/loading.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import SkeletonCard from "./skeletonCard";
|
||||
import SkeletonTable from "./skeletonTable";
|
||||
|
||||
export default function Loading({ type }: any) {
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
|
||||
return <SkeletonCard />;
|
||||
break;
|
||||
case 2:
|
||||
|
||||
return <SkeletonTable />
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
35
src/app/_components/loading/skeletonCard.tsx
Normal file
35
src/app/_components/loading/skeletonCard.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
export default function SkeletonCard() {
|
||||
|
||||
return (
|
||||
|
||||
<div className="space-y-6">
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-32 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-20 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
<div className="h-10 w-full bg-gray-200 dark:bg-slate-700 rounded-md animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="h-10 w-32 bg-gray-200 dark:bg-slate-700 rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
81
src/app/_components/loading/skeletonTable.tsx
Normal file
81
src/app/_components/loading/skeletonTable.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
export default function SkeletonTable() {
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
<div role="status" aria-busy="true" aria-label="Carregando tabela" className="p-4 w-full">
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-32 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-28 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-20 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-24 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-slate-700 rounded animate-pulse"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
'use client';
|
||||
|
||||
import React, { createContext, ReactNode, useContext, useState } from 'react';
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
interface ResponseState {
|
||||
message?: string;
|
||||
type?: 'toast' | 'modal' | 'alert' | null;
|
||||
status?: number;
|
||||
error?: string;
|
||||
detail?: string;
|
||||
message: string;
|
||||
type: 'toast' | 'modal' | 'alert' | null;
|
||||
status: number;
|
||||
}
|
||||
|
||||
interface ResponseContextProps {
|
||||
|
|
@ -19,11 +17,7 @@ interface ResponseContextProps {
|
|||
const ResponseContext = createContext<ResponseContextProps | undefined>(undefined);
|
||||
|
||||
export const ResponseProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const [response, setResponseState] = useState<ResponseState>({
|
||||
message: '',
|
||||
type: null,
|
||||
status: 0,
|
||||
});
|
||||
const [response, setResponseState] = useState<ResponseState>({ message: '', type: null, status : 0});
|
||||
|
||||
const setResponse = (value: ResponseState) => setResponseState(value);
|
||||
const clearResponse = () => setResponseState({ message: '', type: null, status : 0});
|
||||
20
src/app/_response/response.tsx
Normal file
20
src/app/_response/response.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// app/src/app/_response/response.tsx
|
||||
"use client";
|
||||
|
||||
import { useResponse } from "./ResponseContext";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function Response() {
|
||||
const { response, clearResponse } = useResponse();
|
||||
|
||||
useEffect(() => {
|
||||
switch (Number(response?.status)) {
|
||||
case 200:
|
||||
toast(response.message);
|
||||
break;
|
||||
}
|
||||
}, [response, clearResponse]);
|
||||
|
||||
return <div></div>;
|
||||
}
|
||||
|
|
@ -1,204 +1,115 @@
|
|||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: oklch(0.9911 0 0);
|
||||
--foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--popover: oklch(0.9881 0 0);
|
||||
--popover-foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--primary: oklch(0.721 0.1873 47.564);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.2988 0.0123 222.4429);
|
||||
--secondary-foreground: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.9551 0 0);
|
||||
--accent-foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.81 0.1 252);
|
||||
--chart-2: oklch(0.62 0.19 260);
|
||||
--chart-3: oklch(0.55 0.22 263);
|
||||
--chart-4: oklch(0.49 0.22 264);
|
||||
--chart-5: oklch(0.42 0.18 266);
|
||||
--sidebar: oklch(1 0 0);
|
||||
--sidebar-foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--sidebar-primary: oklch(0.2364 0.0083 240.2365);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--font-sans:
|
||||
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
|
||||
--font-mono:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
--radius: 0.625rem;
|
||||
--shadow-x: 0;
|
||||
--shadow-y: 1px;
|
||||
--shadow-blur: 3px;
|
||||
--shadow-spread: 0px;
|
||||
--shadow-opacity: 0.1;
|
||||
--shadow-color: oklch(0 0 0);
|
||||
--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
--tracking-normal: 0em;
|
||||
--spacing: 0.25rem;
|
||||
--shadow-offset-x: 0;
|
||||
--shadow-offset-y: 1px;
|
||||
--letter-spacing: 0em;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.1934 0.0062 236.9149);
|
||||
--foreground: oklch(0.9881 0 0);
|
||||
--card: oklch(0.2364 0.0083 240.2365);
|
||||
--card-foreground: oklch(0.9881 0 0);
|
||||
--popover: oklch(0.2988 0.0123 222.4429);
|
||||
--popover-foreground: oklch(0.9881 0 0);
|
||||
--primary: oklch(0.721 0.1873 47.564);
|
||||
--primary-foreground: oklch(0.2988 0.0123 222.4429);
|
||||
--secondary: oklch(0.2988 0.0123 222.4429);
|
||||
--secondary-foreground: oklch(1 0 0);
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.2988 0.0123 222.4429);
|
||||
--accent-foreground: oklch(1 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--destructive-foreground: oklch(0.985 0 0);
|
||||
--border: oklch(0.275 0 0);
|
||||
--input: oklch(0.325 0 0);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.81 0.1 252);
|
||||
--chart-2: oklch(0.62 0.19 260);
|
||||
--chart-3: oklch(0.55 0.22 263);
|
||||
--chart-4: oklch(0.49 0.22 264);
|
||||
--chart-5: oklch(0.42 0.18 266);
|
||||
--sidebar: oklch(0.2364 0.0083 240.2365);
|
||||
--sidebar-foreground: oklch(0.9881 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(0.275 0 0);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
--font-sans:
|
||||
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
|
||||
--font-mono:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
--radius: 0.625rem;
|
||||
--shadow-x: 0;
|
||||
--shadow-y: 1px;
|
||||
--shadow-blur: 3px;
|
||||
--shadow-spread: 0px;
|
||||
--shadow-opacity: 0.1;
|
||||
--shadow-color: oklch(0 0 0);
|
||||
--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
--shadow-offset-x: 0;
|
||||
--shadow-offset-y: 1px;
|
||||
--letter-spacing: 0em;
|
||||
--spacing: 0.25rem;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
|
||||
--font-sans:
|
||||
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-mono:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
--font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
--shadow-sm: var(--shadow-sm);
|
||||
--shadow: var(--shadow);
|
||||
--shadow-md: var(--shadow-md);
|
||||
--shadow-lg: var(--shadow-lg);
|
||||
--shadow-xl: var(--shadow-xl);
|
||||
--shadow-2xl: var(--shadow-2xl);
|
||||
--radius: 0.625rem;
|
||||
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
|
||||
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
|
||||
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
|
||||
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
|
||||
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
|
||||
--tracking-normal: var(--tracking-normal);
|
||||
--spacing: var(--spacing);
|
||||
--letter-spacing: var(--letter-spacing);
|
||||
--shadow-offset-y: var(--shadow-offset-y);
|
||||
--shadow-offset-x: var(--shadow-offset-x);
|
||||
--shadow-spread: var(--shadow-spread);
|
||||
--shadow-blur: var(--shadow-blur);
|
||||
--shadow-opacity: var(--shadow-opacity);
|
||||
--color-shadow-color: var(--shadow-color);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
|
@ -207,9 +118,8 @@
|
|||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
letter-spacing: var(--tracking-normal);
|
||||
}
|
||||
.bg-brand{
|
||||
background-color: #1a292f;
|
||||
background-color: #1A292F;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
// components/Loading.tsx
|
||||
export default function Loading() {
|
||||
return <span className="loading loading-spinner loading-xs"></span>;
|
||||
return (
|
||||
<span className="loading loading-spinner loading-xs"></span>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import React from 'react';
|
||||
|
||||
// 1. Define as propriedades que nosso componente vai receber
|
||||
interface MainEditorProps {
|
||||
initialValue: string;
|
||||
onEditorChange: (content: string) => void;
|
||||
margins: {
|
||||
top: string;
|
||||
bottom: string;
|
||||
left: string;
|
||||
right: string;
|
||||
};
|
||||
size: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
const MainEditor: React.FC<MainEditorProps> = ({ initialValue, onEditorChange, margins, size }) => {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Editor
|
||||
apiKey="sny4ncto4hf42akdz2eqss2tqd0loo439vfttpuydjc2kqpi"
|
||||
value={initialValue}
|
||||
onEditorChange={onEditorChange}
|
||||
init={{
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
menubar: false,
|
||||
browser_spellcheck: true,
|
||||
contextmenu: false,
|
||||
plugins: [
|
||||
'advlist',
|
||||
'autolink',
|
||||
'lists',
|
||||
'link',
|
||||
'image',
|
||||
'charmap',
|
||||
'preview',
|
||||
'anchor',
|
||||
'searchreplace',
|
||||
'visualblocks',
|
||||
'fullscreen',
|
||||
'insertdatetime',
|
||||
'media',
|
||||
'table',
|
||||
'code',
|
||||
'codesample',
|
||||
'help',
|
||||
'wordcount',
|
||||
'autosave',
|
||||
'quickbars',
|
||||
'quickbars link',
|
||||
'quickbars image editimage',
|
||||
'pagebreak',
|
||||
'nonbreaking',
|
||||
'advlist',
|
||||
'autolink',
|
||||
],
|
||||
autosave_ask_before_unload: true,
|
||||
toolbar:
|
||||
'undo redo styles removeformat formatselect fontfamily fontsize forecolor link quickimage bold italic underline align bullist numlist outdent indent removeformat preview fullscreen searchreplace help code codesample quicktable pagebreak nonbreaking charmap customTemplates',
|
||||
formats: {
|
||||
linhatopo: {
|
||||
selector: 'div',
|
||||
classes: 'borda-superior',
|
||||
},
|
||||
linhainferior: {
|
||||
selector: 'div',
|
||||
classes: 'borda-inferior',
|
||||
},
|
||||
},
|
||||
|
||||
style_formats: [
|
||||
{
|
||||
title: 'Estilos de Borda',
|
||||
items: [
|
||||
{ title: 'Linha Superior', format: 'linhatopo' },
|
||||
{ title: 'Linha Inferior', format: 'linhainferior' },
|
||||
],
|
||||
},
|
||||
],
|
||||
setup: (editor: any) => {
|
||||
const customTemplates = [
|
||||
{
|
||||
title: 'Qualificação das Partes (Casamento)',
|
||||
description: 'Insere o bloco de qualificação para contraentes.',
|
||||
content: `<p><strong>QUALIFICAÇÃO DOS CONTRAENTES:</strong> Ele, brasileiro, solteiro, maior, [Profissão], portador da CI nº [RG], inscrito no CPF sob o nº [CPF], residente e domiciliado em [Endereço]. Ela, brasileira, solteira, maior, [Profissão], portadora da CI nº [RG], inscrita no CPF sob o nº [CPF], residente e domiciliada em [Endereço].</p><br>`,
|
||||
},
|
||||
{
|
||||
title: 'Cláusula de Regime de Bens',
|
||||
description: 'Cláusula padrão de Comunhão Parcial de Bens.',
|
||||
content:
|
||||
'<p>O regime de bens adotado é o da <strong>Comunhão Parcial de Bens</strong>, nos termos dos artigos 1.658 e seguintes do Código Civil brasileiro.</p><br>',
|
||||
},
|
||||
{
|
||||
title: 'Cláusula de Encerramento (Selo)',
|
||||
description: 'Texto final com espaço para o selo digital.',
|
||||
content:
|
||||
'<p>O referido é verdade e dou fé. Emitida nesta data. Selo Digital de Fiscalização: [Número do Selo]</p>',
|
||||
},
|
||||
];
|
||||
editor.ui.registry.addMenuButton('customTemplates', {
|
||||
text: 'Modelos', // O texto que aparecerá no botão
|
||||
fetch: (callback: any) => {
|
||||
const items = customTemplates.map((template) => ({
|
||||
type: 'menuitem',
|
||||
text: template.title,
|
||||
onAction: () => {
|
||||
// 3. Ação que acontece ao clicar no item do menu: insere o conteúdo
|
||||
editor.insertContent(template.content);
|
||||
},
|
||||
}));
|
||||
callback(items);
|
||||
},
|
||||
});
|
||||
},
|
||||
quickbars_selection_toolbar:
|
||||
'bold italic underline | fontfamily | fontsize | quicklink blockquote | quicklink',
|
||||
quickbars_insert_toolbar:
|
||||
'bold italic underline fontfamily fontsize quicklink blockquote quicklink quickimage quicktable hr',
|
||||
quickbars_image_toolbar:
|
||||
'alignleft aligncenter alignright | rotateleft rotateright | imageoptions',
|
||||
fontsize_formats:
|
||||
'4pt 5pt 6pt 7pt 8pt 9pt 10pt 12pt 14pt 16pt 18pt 20pt 22pt 24pt 26pt 28pt 30pt 32pt 34pt 36pt',
|
||||
font_family_formats: ` Times New Roman=Times New Roman, Times, serif; Arial=Arial, Helvetica, sans-serif; Calibri=Calibri, sans-serif; Courier New=Courier New, Courier, monospace; Georgia=Georgia, serif; Verdana=Verdana, Geneva, sans-serif;`,
|
||||
fullscreen_native: true,
|
||||
content_style: `
|
||||
body {
|
||||
font-family: 'Times New Roman', Times, serif;
|
||||
font-size: 12pt;
|
||||
background: #fff;
|
||||
margin: ${margins.top}cm ${margins.right}cm ${margins.bottom}cm ${margins.left}cm;
|
||||
}
|
||||
.mce-pagebreak { /* Estiliza a linha da quebra de página no editor */
|
||||
border-top: 1px dashed #bbb;
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.borda-superior {
|
||||
border-top: 1px solid #000;
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.borda-inferior {
|
||||
border-bottom: 1px solid #000;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainEditor;
|
||||
|
|
@ -1,261 +1,167 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
AudioWaveform,
|
||||
BookOpen,
|
||||
Bot,
|
||||
Command,
|
||||
Frame,
|
||||
GalleryVerticalEnd,
|
||||
HouseIcon,
|
||||
SquareMousePointer,
|
||||
Map,
|
||||
PieChart,
|
||||
Settings2,
|
||||
SquareTerminal,
|
||||
UsersIcon,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import * as React from 'react';
|
||||
} from "lucide-react"
|
||||
|
||||
import { NavMain } from '@/components/nav-main';
|
||||
import { NavProjects } from '@/components/nav-projects';
|
||||
import { NavUser } from '@/components/nav-user';
|
||||
import { NavMain } from "@/components/nav-main"
|
||||
import { NavProjects } from "@/components/nav-projects"
|
||||
import { NavUser } from "@/components/nav-user"
|
||||
import { TeamSwitcher } from "@/components/team-switcher"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarRail,
|
||||
} from '@/components/ui/sidebar';
|
||||
import useGUsuarioGetJWTHook from '@/shared/hooks/auth/useGUsuarioGetJWTHook';
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
// This is sample data.
|
||||
const data = {
|
||||
teams: [],
|
||||
user: {
|
||||
name: "shadcn",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/shadcn.jpg",
|
||||
},
|
||||
teams: [
|
||||
{
|
||||
name: "Acme Inc",
|
||||
logo: GalleryVerticalEnd,
|
||||
plan: "Enterprise",
|
||||
},
|
||||
{
|
||||
name: "Acme Corp.",
|
||||
logo: AudioWaveform,
|
||||
plan: "Startup",
|
||||
},
|
||||
{
|
||||
name: "Evil Corp.",
|
||||
logo: Command,
|
||||
plan: "Free",
|
||||
},
|
||||
],
|
||||
navMain: [
|
||||
{
|
||||
title: 'Administrativo',
|
||||
url: '#',
|
||||
title: "Administrativo",
|
||||
url: "#",
|
||||
icon: SquareTerminal,
|
||||
isActive: false,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: 'Usuários',
|
||||
url: '/usuarios/',
|
||||
title: "Usuários",
|
||||
url: "/usuarios/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Servicos',
|
||||
url: '#',
|
||||
icon: SquareMousePointer,
|
||||
isActive: false,
|
||||
items: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
url: '/servicos/dashboard/',
|
||||
},
|
||||
{
|
||||
title: 'Pedidos',
|
||||
url: '/servicos/pedidos/',
|
||||
},
|
||||
{
|
||||
title: 'Atos',
|
||||
url: '/servicos/atos/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Pessoas',
|
||||
url: '#',
|
||||
icon: UsersIcon,
|
||||
isActive: false,
|
||||
items: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
url: '/administrativo/pessoas/dashboard',
|
||||
},
|
||||
{
|
||||
title: 'Físicas',
|
||||
url: '/administrativo/pessoas/fisicas',
|
||||
},
|
||||
{
|
||||
title: 'Jurídicas',
|
||||
url: '/administrativo/pessoas/juridicas',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Imóveis',
|
||||
url: '#',
|
||||
icon: HouseIcon,
|
||||
isActive: false,
|
||||
items: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
url: '/administrativo/imoveis/dashboard',
|
||||
},
|
||||
{
|
||||
title: 'Urbanos',
|
||||
url: '/administrativo/imoveis/urbanos',
|
||||
},
|
||||
{
|
||||
title: 'Rurais',
|
||||
url: '/administrativo/imoveis/rurais',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Cadastros',
|
||||
url: '#',
|
||||
title: "Models",
|
||||
url: "#",
|
||||
icon: Bot,
|
||||
items: [
|
||||
{
|
||||
title: 'Reconhecimentos',
|
||||
url: '/administrativo/reconhecimentos/',
|
||||
title: "Genesis",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Andamentos',
|
||||
url: '/administrativo/andamentos/',
|
||||
title: "Explorer",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Profissões',
|
||||
url: '/administrativo/pessoas/profissoes/',
|
||||
title: "Quantum",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Regimes/Bens',
|
||||
url: '/administrativo/pessoas/regimes-bens/',
|
||||
title: "Documentation",
|
||||
url: "#",
|
||||
icon: BookOpen,
|
||||
items: [
|
||||
{
|
||||
title: "Introduction",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Tipos de Logradouros',
|
||||
url: '/administrativo/imoveis/tipos-logradouro',
|
||||
title: "Get Started",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Estado Civil',
|
||||
url: '/administrativo/pessoas/estados-civis',
|
||||
title: "Tutorials",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Regimes/Comunhão',
|
||||
url: '/administrativo/pessoas/regimes-comunhao/',
|
||||
title: "Changelog",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Tipo de Medida',
|
||||
url: '/administrativo/medidas/tipos',
|
||||
title: "Settings",
|
||||
url: "#",
|
||||
icon: Settings2,
|
||||
items: [
|
||||
{
|
||||
title: "General",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Cidades',
|
||||
url: '/administrativo/cidades/',
|
||||
title: "Team",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Bairro',
|
||||
url: '/administrativo/bairros',
|
||||
title: "Billing",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: 'Minuta',
|
||||
url: '/administrativo/minutas/',
|
||||
},
|
||||
{
|
||||
title: 'Minuta/Naturezas',
|
||||
url: '/administrativo/minutas/naturezas',
|
||||
},
|
||||
{
|
||||
title: 'Censec/Tipo do Ato',
|
||||
url: '/administrativo/centrais/censec/tipos-atos',
|
||||
},
|
||||
{
|
||||
title: 'Censec/Qualidades',
|
||||
url: '/administrativo/centrais/censec/qualidades',
|
||||
},
|
||||
{
|
||||
title: 'Censec/Centrais',
|
||||
url: '/administrativo/centrais/censec/centrais',
|
||||
},
|
||||
{
|
||||
title: 'Censec/Natureza Litígio',
|
||||
url: '/administrativo/centrais/censec/naturezas-litigios',
|
||||
},
|
||||
{
|
||||
title: 'Tipos/Serviços',
|
||||
url: '/administrativo/tipos-servicos',
|
||||
},
|
||||
{
|
||||
title: 'Atos/Partes Tipos',
|
||||
url: '/administrativo/atos/partes-tipos',
|
||||
},
|
||||
{
|
||||
title: 'Valores de Serviços',
|
||||
url: '/administrativo/valores-de-servicos',
|
||||
},
|
||||
{
|
||||
title: 'Gramatica',
|
||||
url: '/administrativo/gramatica',
|
||||
},
|
||||
{
|
||||
title: 'Cartório',
|
||||
url: '/administrativo/cartorio',
|
||||
},
|
||||
{
|
||||
title: 'Financeiro/Periodo',
|
||||
url: '/administrativo/financeiro/periodos',
|
||||
},
|
||||
{
|
||||
title: 'Financeiro/Emolumentos',
|
||||
url: '/administrativo/financeiro/emolumentos',
|
||||
},
|
||||
{
|
||||
title: 'Selos/Grupos',
|
||||
url: '/administrativo/selos/grupos',
|
||||
},
|
||||
{
|
||||
title: 'Financeiro/Cálculo Rápido',
|
||||
url: '/administrativo/financeiro/calculo-rapido',
|
||||
title: "Limits",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
name: 'Escritura Publica de Compra e Venda',
|
||||
url: '#',
|
||||
name: "Design Engineering",
|
||||
url: "#",
|
||||
icon: Frame,
|
||||
},
|
||||
{
|
||||
name: "Sales & Marketing",
|
||||
url: "#",
|
||||
icon: PieChart,
|
||||
},
|
||||
{
|
||||
name: "Travel",
|
||||
url: "#",
|
||||
icon: Map,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const { userAuthenticated } = useGUsuarioGetJWTHook();
|
||||
return (
|
||||
<Sidebar collapsible="icon" {...props}>
|
||||
<SidebarHeader>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<a href="#">
|
||||
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
<GalleryVerticalEnd className="size-4" />
|
||||
<Image
|
||||
src="/images/logo-abb.png"
|
||||
alt="Logo do site"
|
||||
width={100}
|
||||
height={100}
|
||||
className="rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 leading-none">
|
||||
<span className="font-semibold">Orius Tecnologia</span>
|
||||
<span className="">25.9.1</span>
|
||||
</div>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
<TeamSwitcher teams={data.teams} />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={data.navMain} />
|
||||
<NavProjects projects={data.projects} />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
{userAuthenticated?.data ? <NavUser user={userAuthenticated.data} /> : 'Carregando...'}
|
||||
<NavUser user={data.user} />
|
||||
</SidebarFooter>
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,113 +1,171 @@
|
|||
'use client';
|
||||
'use client'
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import z from 'zod';
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { UsuarioFormSchema } from "@/app/(protected)/(administrativo)/_schemas/GUsuarioSchema"
|
||||
import z from "zod"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import GUsuarioLoginService from "@/app/(protected)/(administrativo)/_services/g_usuario/GUsuarioLogin"
|
||||
import { useForm } from "react-hook-form"
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from "./ui/form"
|
||||
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { GUsuarioLoginSchema } from '@/packages/administrativo/schemas/GUsuario/GUsuarioLoginSchema';
|
||||
import GUsuarioLoginService from '@/packages/administrativo/services/GUsuario/GUsuarioLogin';
|
||||
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
|
||||
type FormValues = z.infer<typeof UsuarioFormSchema>
|
||||
|
||||
import { Button } from './ui/button';
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from './ui/form';
|
||||
|
||||
type FormValues = z.infer<typeof GUsuarioLoginSchema>;
|
||||
|
||||
export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
export function LoginForm({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(GUsuarioLoginSchema),
|
||||
resolver: zodResolver(UsuarioFormSchema),
|
||||
defaultValues: {
|
||||
login: '',
|
||||
senha_api: '',
|
||||
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);
|
||||
};
|
||||
async function onSubmit(values: FormValues) {
|
||||
const data = await GUsuarioLoginService(values);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
||||
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
|
||||
<Card className="overflow-hidden p-0">
|
||||
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3 p-6 md:p-8">
|
||||
<div className="mb-6 flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold">Bem vindo de volta!</h1>
|
||||
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="p-6 md:p-8 gap-3">
|
||||
|
||||
<div className="flex flex-col gap-6">
|
||||
|
||||
<div className="flex flex-col items-center text-center">
|
||||
|
||||
<h1 className="text-2xl font-bold">
|
||||
|
||||
Bem vindo de volta!
|
||||
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground text-balance">
|
||||
|
||||
Entre na sua conta Orius Tecnologia.
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="login"
|
||||
render={({ field }) => (
|
||||
|
||||
<FormItem>
|
||||
<FormLabel>Login</FormLabel>
|
||||
<FormLabel>
|
||||
Login
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="text" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="senha_api"
|
||||
render={({ field }) => (
|
||||
|
||||
<FormItem>
|
||||
<FormLabel>Senha</FormLabel>
|
||||
<FormLabel>
|
||||
Senha
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Botão de loading */}
|
||||
<LoadingButton
|
||||
text="Entrar"
|
||||
textLoading="Aguarde..."
|
||||
type="submit"
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full my-3">
|
||||
Entrar
|
||||
</Button>
|
||||
|
||||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||
|
||||
<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">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-smartphone-icon lucide-smartphone">
|
||||
<rect width="14" height="20" x="5" y="2" rx="2" ry="2" /><path d="M12 18h.01" />
|
||||
</svg>
|
||||
|
||||
<span className="sr-only">
|
||||
|
||||
Chamar no Whatsapp
|
||||
|
||||
</span>
|
||||
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" type="button" className="w-full">
|
||||
Chamar no Local
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-map-pin-icon lucide-map-pin">
|
||||
<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /><circle cx="12" cy="10" r="3" />
|
||||
</svg>
|
||||
|
||||
<span className="sr-only">
|
||||
|
||||
Chamar no Whatsapp
|
||||
|
||||
</span>
|
||||
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</Form>
|
||||
|
||||
<div className="bg-brand relative hidden items-center justify-center md:flex">
|
||||
<div className="bg-brand relative hidden md:flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/logo-login.svg"
|
||||
alt="Logo"
|
||||
|
|
@ -116,13 +174,18 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
|
|||
className="object-contain dark:brightness-[0.2] dark:grayscale"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</CardContent>
|
||||
|
||||
</Card>
|
||||
|
||||
<div className="text-muted-foreground text-center text-xs">
|
||||
Ao clicar você concorda com <a href="#">Nossos termos de serviços</a> e{' '}
|
||||
<a href="#">Políticas de Privacidade</a>.
|
||||
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
|
||||
|
||||
Ao clicar você concordar com <a href="#">Nossos termos de serviços</a>{" "}e <a href="#">Políticas de Privacidade</a>.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import { ChevronRight, type LucideIcon } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { ChevronRight, type LucideIcon } from "lucide-react"
|
||||
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible"
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
|
|
@ -13,25 +16,25 @@ import {
|
|||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
} from '@/components/ui/sidebar';
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export function NavMain({
|
||||
items,
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
icon?: LucideIcon;
|
||||
isActive?: boolean;
|
||||
title: string
|
||||
url: string
|
||||
icon?: LucideIcon
|
||||
isActive?: boolean
|
||||
items?: {
|
||||
title: string;
|
||||
url: string;
|
||||
}[];
|
||||
}[];
|
||||
title: string
|
||||
url: string
|
||||
}[]
|
||||
}[]
|
||||
}) {
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>SAAS</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<Collapsible
|
||||
|
|
@ -53,9 +56,9 @@ export function NavMain({
|
|||
{item.items?.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton asChild>
|
||||
<Link href={subItem.url}>
|
||||
<a href={subItem.url}>
|
||||
<span>{subItem.title}</span>
|
||||
</Link>
|
||||
</a>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
|
|
@ -66,5 +69,5 @@ export function NavMain({
|
|||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import { Folder, Forward, MoreHorizontal, Trash2, type LucideIcon } from 'lucide-react';
|
||||
import {
|
||||
Folder,
|
||||
Forward,
|
||||
MoreHorizontal,
|
||||
Trash2,
|
||||
type LucideIcon,
|
||||
} from "lucide-react"
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -8,7 +14,7 @@ import {
|
|||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
|
|
@ -17,22 +23,22 @@ import {
|
|||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from '@/components/ui/sidebar';
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export function NavProjects({
|
||||
projects,
|
||||
}: {
|
||||
projects: {
|
||||
name: string;
|
||||
url: string;
|
||||
icon: LucideIcon;
|
||||
}[];
|
||||
name: string
|
||||
url: string
|
||||
icon: LucideIcon
|
||||
}[]
|
||||
}) {
|
||||
const { isMobile } = useSidebar();
|
||||
const { isMobile } = useSidebar()
|
||||
|
||||
return (
|
||||
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
||||
<SidebarGroupLabel>Escrituras</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>Projects</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{projects.map((item) => (
|
||||
<SidebarMenuItem key={item.name}>
|
||||
|
|
@ -51,8 +57,8 @@ export function NavProjects({
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-48 rounded-lg"
|
||||
side={isMobile ? 'bottom' : 'right'}
|
||||
align={isMobile ? 'end' : 'start'}
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align={isMobile ? "end" : "start"}
|
||||
>
|
||||
<DropdownMenuItem>
|
||||
<Folder className="text-muted-foreground" />
|
||||
|
|
@ -79,5 +85,5 @@ export function NavProjects({
|
|||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import { ChevronsUpDown, LogOut, Moon, Sparkles, Sun } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useCallback, useState } from 'react';
|
||||
import {
|
||||
BadgeCheck,
|
||||
Bell,
|
||||
ChevronsUpDown,
|
||||
CreditCard,
|
||||
LogOut,
|
||||
Sparkles,
|
||||
} from "lucide-react"
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/components/ui/avatar"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -13,131 +22,93 @@ import {
|
|||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from '@/components/ui/sidebar';
|
||||
import { useGUsuarioLogoutHook } from '@/packages/administrativo/hooks/GUsuario/useGUsuarioLogoutHook';
|
||||
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
|
||||
import GUsuarioAuthenticatedInterface from '@/shared/interfaces/GUsuarioAuthenticatedInterface';
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export function NavUser({ user }: { user: GUsuarioAuthenticatedInterface }) {
|
||||
// Hook para encerrar sessão
|
||||
const { logoutUsuario } = useGUsuarioLogoutHook();
|
||||
|
||||
// Controle de exibição do formulário de confirmação
|
||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
|
||||
|
||||
const { isMobile } = useSidebar();
|
||||
|
||||
// Tema (claro/escuro) - next-themes
|
||||
const { theme, setTheme } = useTheme();
|
||||
const isDark = theme === 'dark';
|
||||
|
||||
const handleConfirmOpen = useCallback(() => {
|
||||
setIsConfirmOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleLogoutConfirm = useCallback(() => {
|
||||
logoutUsuario();
|
||||
}, [logoutUsuario]);
|
||||
|
||||
const handleLogoutCancel = useCallback(() => {
|
||||
setIsConfirmOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleToggleTheme = useCallback(() => {
|
||||
setTheme(isDark ? 'light' : 'dark');
|
||||
}, [isDark, setTheme]);
|
||||
|
||||
if (!user) {
|
||||
return 'Carregando...';
|
||||
export function NavUser({
|
||||
user,
|
||||
}: {
|
||||
user: {
|
||||
name: string
|
||||
email: string
|
||||
avatar: string
|
||||
}
|
||||
}) {
|
||||
const { isMobile } = useSidebar()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
<AvatarImage src={user.sigla} alt={user.nome} />
|
||||
<AvatarFallback className="rounded-lg">{user.sigla}</AvatarFallback>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.nome}</span>
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs">{user.email}</span>
|
||||
</div>
|
||||
|
||||
<ChevronsUpDown className="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
side={isMobile ? 'bottom' : 'right'}
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
<AvatarImage src={user.sigla} alt={user.nome} />
|
||||
<AvatarFallback className="rounded-lg">{user.sigla}</AvatarFallback>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.nome}</span>
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuGroup>
|
||||
{/* Alternância de tema */}
|
||||
<DropdownMenuItem className="cursor-pointer" onClick={handleToggleTheme}>
|
||||
{isDark ? <Sun className="mr-2 h-4 w-4" /> : <Moon className="mr-2 h-4 w-4" />}
|
||||
{isDark ? 'Modo claro' : 'Modo escuro'}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem className="cursor-pointer">
|
||||
<Sparkles className="mr-2 h-4 w-4" />
|
||||
Configurações
|
||||
<DropdownMenuItem>
|
||||
<Sparkles />
|
||||
Upgrade to Pro
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem className="cursor-pointer" onClick={handleConfirmOpen}>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<BadgeCheck />
|
||||
Account
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CreditCard />
|
||||
Billing
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Bell />
|
||||
Notifications
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogOut />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Log-out"
|
||||
description="Atenção"
|
||||
message="Deseja realmente encerrar a sessão? Dados não salvos serão perdidos."
|
||||
confirmText="Sim, sair"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleLogoutConfirm}
|
||||
onCancel={handleLogoutCancel}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue