Compare commits

..

1 commit

1027 changed files with 2725 additions and 50411 deletions

View file

@ -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
View file

@ -39,5 +39,3 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
/src/config/app.json

View file

@ -1,9 +0,0 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"arrowParens": "always",
"plugins": ["prettier-plugin-tailwindcss"]
}

View file

@ -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"]

View file

@ -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>

View file

@ -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',
},
},
];

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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"
}
}

View file

@ -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

View file

@ -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
View 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);
}
}

View 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
View file

@ -0,0 +1,6 @@
import appConfig from '../../config/app.json';
export default class Json {
static execute() {
return appConfig;
}
}

View 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;
}

View file

@ -0,0 +1,9 @@
export default function empty(data: any) {
if (!data || !data === null || !data === undefined) {
return true;
}
return false;
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
}

View file

@ -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;
}

View file

@ -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 }
}

View file

@ -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 }
}

View file

@ -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 }
}

View file

@ -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,
}

View file

@ -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(),
});

View file

@ -0,0 +1,11 @@
'use server'
import GUsuarioIndexData from "../../_data/g_usuario/GUsuarioIndexData"
export default async function GUsuarioIndex() {
const response = await GUsuarioIndexData();
return response;
}

View file

@ -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");
}

View file

@ -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);
}

View file

@ -0,0 +1,9 @@
'use server'
import GUsuarioSaveData from "../../_data/g_usuario/GUsuarioSaveData";
export default async function GUsuarioSave(form: any) {
return await GUsuarioSaveData(form);
}

View file

@ -1,5 +0,0 @@
import TTBAndamentoServicoIndex from '@/packages/administrativo/components/TTBAndamentoServico/TTBAndamentoServicoIndex';
export default function TAtoParteTipo() {
return <TTBAndamentoServicoIndex />;
}

View file

@ -1,5 +0,0 @@
import TAtoParteTipoIndex from '@/packages/administrativo/components/TAtoParteTipo/TAtoParteTipoIndex';
export default function TAtoParteTipo() {
return <TAtoParteTipoIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBBairroIndex from '@/packages/administrativo/components/GTBBairro/GTBBairroIndex';
export default function GCidadePage() {
return <GTBBairroIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GCartorioIndex from '@/packages/administrativo/components/GCartorio/GCartorioIndex';
export default function GCartorioPage() {
return <GCartorioIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecTipoNaturezaIndex from '@/packages/administrativo/components/TCensecTipoNatureza/TCensecTipoNaturezaIndex';
export default function TCensecTipoNaturezaPage() {
return <TCensecTipoNaturezaIndex />;
}

View file

@ -1,3 +0,0 @@
export default function TCensecPage() {
return <div></div>;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecQualidadeIndex from '@/packages/administrativo/components/TCensecQualidade/TCensecQualidadeIndex';
export default function TCensecQualidadePage() {
return <TCensecQualidadeIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecIndex from '@/packages/administrativo/components/TCensec/TCensecIndex';
export default function GTBEstadoCivilPage() {
return <TCensecIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecNaturezaLitigioIndex from '@/packages/administrativo/components/TCensecNaturezaLitigio/TCensecNaturezaLitigioIndex';
export default function GCidadePage() {
return <TCensecNaturezaLitigioIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecQualidadeIndex from '@/packages/administrativo/components/TCensecQualidade/TCensecQualidadeIndex';
export default function GTBEstadoCivilPage() {
return <TCensecQualidadeIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TCensecTipoAtoIndex from '@/packages/administrativo/components/TCensecTipoAto/TCensecTipoAtoIndex';
export default function GTBEstadoCivilPage() {
return <TCensecTipoAtoIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GCidadeIndex from '@/packages/administrativo/components/GCidade/GCidadeIndex';
export default function GCidadePage() {
return <GCidadeIndex />;
}

View file

@ -1,5 +0,0 @@
import GCalculoIndex from '@/packages/administrativo/components/GCalculo/GCalculoIndex';
export default function GEmolumentoPeriodoPage() {
return <GCalculoIndex />;
}

View file

@ -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)}
/>
);
}

View file

@ -1,5 +0,0 @@
import GEmolumentoIndex from '@/packages/administrativo/components/GEmolumento/GEmolumentoIndex';
export default function GEmolumentoPeriodoPage() {
return <GEmolumentoIndex />;
}

View file

@ -1,5 +0,0 @@
import GEmolumentoPeriodoIndex from '@/packages/administrativo/components/GEmolumentoPeriodo/GEmolumentoPeriodoIndex';
export default function GEmolumentoPeriodoPage() {
return <GEmolumentoPeriodoIndex />;
}

View file

@ -1,5 +0,0 @@
import GGramaticaIndex from '@/packages/administrativo/components/GGramatica/GGramaticaIndex';
export default function GGramaticaPage() {
return <GGramaticaIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TImovelDashboard from '@/packages/administrativo/components/TImovel/TImovelDashboard';
export default function TImovelDashboardPage() {
return <TImovelDashboard />;
}

View file

@ -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}
/>
);
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBTipoLogradouroIndex from '@/packages/administrativo/components/GTBTipoLogradouro/GTBTipoLogradouroIndex';
export default function GMedidaTipoPage() {
return <GTBTipoLogradouroIndex />;
}

View file

@ -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}
/>
);
}

View file

@ -1,7 +0,0 @@
'use client';
import GMedidaTipoIndex from '@/packages/administrativo/components/GMedidaTipo/GMedidaTipoIndex';
export default function GMedidaTipoPage() {
return <GMedidaTipoIndex />;
}

View file

@ -1,5 +0,0 @@
import GNaturezaIndex from '@/packages/administrativo/components/GNatureza/GNaturezaIndex';
export default function GNaturezaPage() {
return <GNaturezaIndex sistema_id={2} />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TPessoaDashboard from '@/packages/administrativo/components/TPessoa/TPessoaDashboard';
export default function TPessoaDashboardPage() {
return <TPessoaDashboard />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBEstadoCivilIndex from '@/packages/administrativo/components/GTBEstadoCivil/GTBEstadoCivilIndex';
export default function GTBEstadoCivilPage() {
return <GTBEstadoCivilIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TPessoaFisicaIndex from '@/packages/administrativo/components/TPessoa/TPessoaFisica/TPessoaFisicaIndex';
export default function TPessoaFisica() {
return <TPessoaFisicaIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import TPessoaJuridicaIndex from '@/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaIndex';
export default function TPessoaFisica() {
return <TPessoaJuridicaIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBProfissaoIndex from '@/packages/administrativo/components/GTBProfissao/GTBProfissaoIndex';
export default function GTBEstadoCivilPage() {
return <GTBProfissaoIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBRegimeBensIndex from '@/packages/administrativo/components/GTBRegimeBens/GTBRegimeBensIndex';
export default function GTBRegimeBensPage() {
return <GTBRegimeBensIndex />;
}

View file

@ -1,7 +0,0 @@
'use client';
import GTBRegimeComunhaoIndex from '@/packages/administrativo/components/GTBRegimeComunhao/GTBRegimeComunhaoIndex';
export default function GTBRegimeBensPage() {
return <GTBRegimeComunhaoIndex />;
}

View file

@ -1,5 +0,0 @@
import TTBReconhecimentoTipoIndex from '@/packages/administrativo/components/TTBReconhecimentoTipo/TTBReconhecimentoTipoIndex';
export default function TAtoParteTipo() {
return <TTBReconhecimentoTipoIndex />;
}

View file

@ -1,5 +0,0 @@
import GSeloGrupoIndex from '@/packages/administrativo/components/GSeloGrupo/GSeloGrupoIndex';
export default function GSeloGrupoPage() {
return <GSeloGrupoIndex />;
}

View file

@ -1,5 +0,0 @@
import TServicoTipoIndex from '@/packages/administrativo/components/TServicoTipo/TServicoTipoIndex';
export default function TServicoTipoPage() {
return <TServicoTipoIndex />;
}

View file

@ -1,5 +0,0 @@
import GNaturezaTituloIndex from '@/packages/administrativo/components/GNaturezaTitulo/GNaturezaTituloIndex';
export default function GNaturezaPage() {
return <GNaturezaTituloIndex sistema_id={2} />;
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);

View file

@ -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>
);
}

View file

@ -1,5 +0,0 @@
import TAtoForm from '@/packages/servicos/components/TAto/TAtoForm';
export default function TAtoFormPage() {
return <TAtoForm />;
}

View file

@ -1,5 +0,0 @@
import TAtoIndex from '@/packages/servicos/components/TAto/TAtoIndex';
export default function TServicoAToPag() {
return <TAtoIndex />;
}

View file

@ -1,5 +0,0 @@
import TServicoPedidoDashboard from '@/packages/servicos/components/TServicoPedido/TServicoPedidoDashboard';
export default function TServicoPedidoPage() {
return <TServicoPedidoDashboard />;
}

View file

@ -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)} />;
}

View file

@ -1,5 +0,0 @@
import TServicoPedidoIndex from '@/packages/servicos/components/TServicoPedido/TServicoPedidoIndex';
export default function TServicoPedidoPage() {
return <TServicoPedidoIndex />;
}

View file

@ -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)} />;
}

View file

@ -1,5 +0,0 @@
import TServicoPedidoForm from '@/packages/servicos/components/TServicoPedido/TServicoPedidoForm';
export default function TServicoPedidoPage() {
return <TServicoPedidoForm />;
}

View file

@ -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>
);
}

View file

@ -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>
);
)
}

View file

@ -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>
);
}

View file

@ -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,
};
}

View 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;
}
}

View 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>
);
}

View 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>
);
}

View file

@ -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});

View 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>;
}

View file

@ -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;
}
}

View file

@ -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>
)
}

View file

@ -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;

View file

@ -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>
);
)
}

View file

@ -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>
);
)
}

View file

@ -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>
);
)
}

View file

@ -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>
);
)
}

View file

@ -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