diff --git a/README.md b/README.md index 3f945f3..593ff60 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,21 @@ NEXT_PUBLIC_ORIUS_APP_STATE=GO NEXT_PUBLIC_ORIUS_APP_API_URL= 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": ["/**"] +} + +npm run dev:debug diff --git a/next.config.ts b/next.config.ts index 74b4bab..e731bfa 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,9 +12,6 @@ const nextConfig = { eslint: { ignoreDuringBuilds: true }, typescript: { ignoreBuildErrors: true }, - // Removido o experimental.runtime (incompatível no Next 15) - // O runtime agora é definido por rota ou via middleware (Edge/Node.js), - // mas por padrão, tudo roda em Node.js quando "output: standalone" está ativo. }; module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json index 276c819..356caec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "@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", @@ -400,6 +401,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", @@ -4254,6 +4262,24 @@ "react": ">= 16.8.0" } }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4273,6 +4299,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true, "license": "MIT" }, "node_modules/d3-array": { @@ -4396,127 +4423,6 @@ "node": ">=12" } }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4612,12 +4518,6 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4690,16 +4590,6 @@ "node": ">=0.10.0" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5615,15 +5505,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-equals": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.2.tgz", - "integrity": "sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -6187,15 +6068,6 @@ "node": ">=12" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -7082,12 +6954,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -8009,21 +7875,6 @@ } } }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -9000,9 +8851,6 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", diff --git a/package.json b/package.json index fac3244..c86c680 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "version": "25.9.1", "private": true, "scripts": { - "dev": "next dev --turbopack", + "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" @@ -65,6 +66,7 @@ "@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", diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts index 723a667..036eb2f 100644 --- a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -2,48 +2,15 @@ import { GCalculoServico } from '@/packages/administrativo/data/GCalculo/GCalculoServicoData'; import GCalculoServicoInterface from '@/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface'; -import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; async function executeGCalculoServicoService( payload: GCalculoServicoInterface, - data: TServicoItemPedidoAddInterface, ) { + const response = await GCalculoServico(payload); - if (response.status == 404 || response.status == 400) { - return { - status: response.status, - message: 'Erro ao processar dados', - }; - } - - const item = { - emolumento_id: response.data.emolumento_id, - emolumento_item_id: response.data.emolumento_item_id ?? null, - servico_tipo_id: data.servico_tipo.servico_tipo_id ?? 0, - tipo_item: data.servico_tipo.tipo_item ?? '', - descricao: data.servico_tipo.descricao ?? '', - tabela: data.servico_tipo?.descricao ?? '', - situacao: 'F', - qtd: 1, - valor: response.data.valor_total ?? 0, - emolumento: response.data.valor_emolumento ?? 0, - fundesp: response.data.valor_fundos ?? 0, - taxa_judiciaria: response.data.taxa_judiciaria ?? 0, - valor_iss: response.data.valor_iss ?? 0, - pessoa_id: data?.pessoa?.pessoa_id ?? null, - subview: {}, - }; - - if (data?.pessoa?.pessoa_id) { - item.subview = { - servico: data.servico_tipo, - pessoa: data.pessoa, - }; - } - - return item; + return response; } export const GCalculoServicoService = withClientErrorHandler(executeGCalculoServicoService); diff --git a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts b/src/packages/servicos/actions/TServicoPedido/handleServicoTipoPessoaSelection.ts similarity index 87% rename from src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts rename to src/packages/servicos/actions/TServicoPedido/handleServicoTipoPessoaSelection.ts index d217baf..8ce6983 100644 --- a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts +++ b/src/packages/servicos/actions/TServicoPedido/handleServicoTipoPessoaSelection.ts @@ -1,6 +1,6 @@ import HandleSelectTServicoTipoInterface from './HandleSelectTServicoTipoInterface'; -export default function HandleSelectTServicoTipoAction({ +export default function handleServicoTipoPessoaSelection({ servico, emolumento, onOpenPessoaForm, diff --git a/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemLocalPrepare.ts b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemLocalPrepare.ts new file mode 100644 index 0000000..0f8e6c0 --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemLocalPrepare.ts @@ -0,0 +1,30 @@ + +export default function TServicoPedidoItemLocalPrepare(data, response) { + + const item = { + emolumento_id: response.data.emolumento_id, + emolumento_item_id: response.data.emolumento_item_id ?? null, + servico_tipo_id: data.servico_tipo.servico_tipo_id ?? 0, + tipo_item: data.servico_tipo.tipo_item ?? '', + descricao: data.servico_tipo.descricao ?? '', + tabela: data.servico_tipo?.descricao ?? '', + situacao: 'F', + qtd: 1, + valor: response.data.valor_total ?? 0, + emolumento: response.data.valor_emolumento ?? 0, + fundesp: response.data.valor_fundos ?? 0, + taxa_judiciaria: response.data.taxa_judiciaria ?? 0, + valor_iss: response.data.valor_iss ?? 0, + pessoa_id: data?.pessoa?.pessoa_id ?? null, + }; + + if (data?.pessoa?.pessoa_id) { + item.subview = { + servico: data.servico_tipo, + pessoa: data.pessoa, + }; + } + + return item + +} \ No newline at end of file diff --git a/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts index a7382f5..f8e8e96 100644 --- a/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts +++ b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts @@ -1,6 +1,7 @@ import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; export default function TServicoPedidoItemPreparePayload(data: TServicoItemPedidoAddInterface) { + data.qtd = 1; data.valor_documento = 0; @@ -26,4 +27,4 @@ export default function TServicoPedidoItemPreparePayload(data: TServicoItemPedid quantidade: data.qtd, emolumento_id: data.emolumento.emolumento_id, }; -} +} \ No newline at end of file diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx index fcae7c9..ebd4264 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx @@ -2,6 +2,7 @@ import { Minus, Plus } from 'lucide-react'; import React, { memo } from 'react'; +import { useWatch } from 'react-hook-form'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -17,12 +18,13 @@ import TPessoaTableFormSubview from '@/packages/administrativo/components/TPesso import TServicoItemPedidoFormTableInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; -function TServicoItemPedidoFormTableComponent({ - data, - form, - params, -}: TServicoItemPedidoFormTableInterface) { - console.log(data); +function TServicoItemPedidoFormTableComponent({ form, params, handleChangeQtd }: TServicoItemPedidoFormTableInterface) { + + const { control } = form; + + // Sempre “ouve” o campo itens do formulário + const data = useWatch({ control, name: 'itens' }) || []; + return (
@@ -37,66 +39,71 @@ function TServicoItemPedidoFormTableComponent({ Qtd. + - {data?.length ? ( - data.map((item, index) => { - return ( - - {/* Linha principal */} - - -
-
-
- {GetCapitalize(item.descricao)} -
-
{GetCapitalize(item.tabela)}
+ {data.length ? ( + data.map((item, index) => ( + + + +
+
+
+ {GetCapitalize(item.descricao)}
+
{GetCapitalize(item.tabela)}
- - R$ {item.emolumento ?? '---'} - R$ {item.taxa_judiciaria ?? '---'} - R$ {item.fundesp ?? '---'} - R$ {item.valor_iss ?? '---'} - R$ {item.valor ?? '---'} - -
- - - -
+
+
+ R$ {item.emolumento ?? '---'} + R$ {item.taxa_judiciaria ?? '---'} + R$ {item.fundesp ?? '---'} + R$ {item.valor_iss ?? '---'} + R$ {item.valor ?? '---'} + +
+ + + +
+
+
+ + {item.subview && ( + + + - {/* SubView */} - {item.subview && ( - - - - - - )} -
- ); - }) + )} + + )) ) : ( diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index f0c81a1..82783c9 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -1,8 +1,8 @@ 'use client'; -import { CreditCard, Package, UserSquare2 } from 'lucide-react'; -import { useRouter } from 'next/navigation'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { CreditCardIcon, PackageIcon, UserSquare2Icon } from 'lucide-react'; +import { useMemo } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; @@ -21,295 +21,23 @@ import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmol import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; import TPessoaTableFormDialog from '@/packages/administrativo/components/TPessoa/TPessoaTableFormDialog'; import TServicoTipoSelect from '@/packages/administrativo/components/TServicoTipo/TServicoTipoSelect'; -import TPessoaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaInterface'; -import HandleSelectTServicoTipoAction from '@/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction'; import { TServicoItemPedidoFormTable } from '@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable'; -import { useTServicoItemPedidoCalculoHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook'; -import { useTServicoItemPedidoIndexHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook'; -import { useTServicoItemPedidoLocalAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook'; -import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; -import { useTServicoPedidoLoadParamsHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoLoadParamsHook'; -import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; -import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook'; -import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; +import useTServicoPedidoFormControllerHook from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormControllerHook'; import { TServicoPedidoFormInterface } from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface'; -import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; -import { TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema'; import { FormatCPFCNPJForm } from '@/shared/actions/CPF/FormatCPFCNPJForm'; import { UnmaskCPFCNPJForm } from '@/shared/actions/CPF/UnmaskCPFCNPJForm'; -import { parseNumberInput } from '@/shared/actions/form/parseNumberInput'; import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog'; import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; -import { useResponse } from '@/shared/components/response/ResponseContext'; import { StepNavigator, - StepNavigatorRef, - StepSection, + StepSection } from '@/shared/components/step/stepNavigator'; import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; -import { SituacoesEnum } from '@/shared/enums/SituacoesEnum'; export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedidoFormInterface) { - const router = useRouter(); - const form = useTServicoPedidoFormHook({}); - const { setValue, reset, watch } = form; - const [isSaving, setIsSaving] = useState(false); - const [isAdding, setIsAdding] = useState(false); - const [isPessoaFormOpen, setIsPessoaFormOpen] = useState(false); - const [isSaveConfirmOpen, setIsSaveConfirmOpen] = useState(false); - const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false); - const [selectedPessoaTipo, setSelectedPessoaTipo] = useState(''); - const [shouldKeepFormOpen, setShouldKeepFormOpen] = useState(false); - - const ref = useRef(null); - - // Controles de formulário - const handleClosePessoaForm = useCallback(() => setIsPessoaFormOpen(false), []); - const handleOpenSaveConfirm = useCallback(() => setIsSaveConfirmOpen(true), []); - const handleCloseSaveConfirm = useCallback(() => setIsSaveConfirmOpen(false), []); - - // Hooks - // const playSuccess = useSoundHook("/sounds/success.mp3"); - const { setResponse } = useResponse(); - const { saveTServicoPedido } = useTServicoPedidoSaveHook(); - const { showTServicoPedido } = useTServicoPedidoShowHook(); - const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = - useTServicoItemPedidoLocalAddHook(setValue); - const { addTServicoItemPedido } = useTServicoItemPedidoCalculoHook(setValue); - const { indexTServicoItemPedido } = useTServicoItemPedidoIndexHook(); - const { TServicoPedidoParams, loadParamsTServicoPedido } = useTServicoPedidoLoadParamsHook(); - - // Acompanha as alterações, nos campos definidos - const selectedServicoTipo = watch('servico_tipo'); - const selectedEmolumento = watch('emolumento'); - - const handleFormError = useCallback((errors: any) => { - console.group('Erros de validação do formulário'); - console.log('Campos com erro:', errors); - console.groupEnd(); - }, []); - - // Envia a requisição para a API - const handleSavePedido = useCallback( - async (data: TServicoPedidoFormValues) => { - // Ativa o botão de loading - setIsSaving(true); - - // Converte o tipo do formulário (Zod) para o tipo da API (Interface) - const payload: TServicoPedidoInterface = { - ...data, - situacao: data.situacao as unknown as SituacoesEnum, - }; - - const response = await saveTServicoPedido(payload); - - setIsSaving(false); - - // Desativa o botão de loading - setIsSaving(false); - - // Verifica se devo redirecionar a pagina - if (response?.servico_pedido_id > 0) { - // Toca o som do sistema - // playSuccess() - } - - // Verifica se devo redirecionar a pagina - if (response?.servico_pedido_id > 0 && !shouldKeepFormOpen) { - router.replace(`/servicos/balcao/detalhes/${response.servico_pedido_id}`); - } - }, - [saveTServicoPedido, shouldKeepFormOpen], - ); - - // Modal de confirmação de serviço - const handleSubmitWithConfirmation = useCallback(() => { - // Envia o formulário - form.handleSubmit(handleSavePedido, handleFormError)(); - }, [form, handleSavePedido, handleFormError]); - - // Busca os itens do Pedido - const fetchPedidoItens = useCallback( - async (id: number) => { - const pedidoItens = { - servico_pedido_id: id, - }; - - // Busca os itens do pedido - const response = await indexTServicoItemPedido(pedidoItens); - - // Verifica se os dados foram localizados - if (response?.data?.length) { - // Atualiza os dados dos itens locais - setLocalTServicoItemPedido(response.data); - - // Atualiza os itens do formulário - setValue('itens', response.data); - } - }, - [indexTServicoItemPedido, setValue, setLocalTServicoItemPedido], - ); - - // Busca o pedido Principal - const fetchPedido = useCallback(async () => { - // Busca o pedido principal - const response = await showTServicoPedido({ servico_pedido_id }); - - // Verifica se o pedido foi localizado - if (response?.servico_pedido_id) { - // Atualiza os dados do formulário - reset(response); - - // Carrega os itens do pedido - fetchPedidoItens(response.servico_pedido_id); - } - }, [servico_pedido_id, showTServicoPedido, reset, fetchPedidoItens]); - - const handleAddItemWithPessoa = useCallback( - async (selectedTPessoa: TPessoaInterface) => { - // Habilita o loading - setIsAdding(true); - - // Constroi um novo item - const newItem = await addTServicoItemPedido({ - servico_tipo: selectedServicoTipo, - emolumento: selectedEmolumento, - pessoa: selectedTPessoa, - }); - - // Verifica se existe um novo item - if (!newItem) return; - - // Obtem o indice atual - const index = TServicoItemPedidoLocal.length; - - // Define a posição do item - newItem.index = index; - - // Atualiza o estado - localAddTServicoItemPedido(newItem); - - // Atualiza os itens do formulário - form.setValue(`itens.${index}`, newItem); - - // Desabilita o loading - setIsAdding(false); - }, - [ - addTServicoItemPedido, - selectedServicoTipo, - selectedEmolumento, - TServicoItemPedidoLocal.length, - localAddTServicoItemPedido, - form, - ], - ); - - // Controla o formulário de cancelamento de pedido - const handleOpenCancelDialog = useCallback(async () => { - // Fecha a confirmação - setIsCancelDialogOpen(true); - }, []); - - // Controle de redirecionamento - const handleConfirmCancel = useCallback(async () => { - // Redireciona o usuário - router.replace(`/servicos/balcao/`); - }, []); - - // Controle do formulário de cancelamento do Pedido - const handleCloseCancelDialog = useCallback(async () => { - // Fecha o formulário - setIsCancelDialogOpen(false); - }, []); - - // Controle de itens - const handleAddItemBasic = useCallback(async () => { - setIsAdding(true); - - // Prepara e valida os dados de item do pedido - const payload: TServicoItemPedidoAddInterface = { - servico_tipo: selectedServicoTipo, - emolumento: selectedEmolumento, - }; - - // Verifica se os dados foram criados corretamente - if (!payload) return; - - // Obtem o resultado da adição do item - const newItem = await addTServicoItemPedido(payload); - - // Se tiver um novo item, adiciona o mesmo na tela - if (newItem) localAddTServicoItemPedido(newItem); - - setIsAdding(false); - }, [addTServicoItemPedido, selectedServicoTipo, selectedEmolumento, localAddTServicoItemPedido]); - - // Habilita o formulário de pessoas - const handleOpenPessoaForm = useCallback((tipoPessoa: string) => { - setSelectedPessoaTipo(tipoPessoa); - setIsPessoaFormOpen(true); - }, []); - - // Adiciona o item a tabela e verifica se deve ou não montar a subview da linha da tabela - const handleSelectServicoTipo = useCallback(() => { - const response = HandleSelectTServicoTipoAction({ - servico: selectedServicoTipo, - emolumento: selectedEmolumento, - onOpenPessoaForm: handleOpenPessoaForm, - onAddItem: handleAddItemBasic, - }); - - // Verifica se existem erros - if (response?.status) { - setResponse(response); - } - }, [selectedServicoTipo, selectedEmolumento, handleOpenPessoaForm, handleAddItemBasic]); - - // Cálculo automático dos totais - const calcularTotais = useCallback(() => { - if (!TServicoItemPedidoLocal || !TServicoItemPedidoLocal.length) { - setValue('valor_pedido', 0); - setValue('valor_pago', 0); - return; - } - - const total = TServicoItemPedidoLocal.reduce((acc, item) => { - const valor = Number(item.valor ?? 0); - return acc + valor; - }, 0); - - setValue('valor_pedido', total, { shouldDirty: true }); - - // opcional: manter valor pago igual ao pedido - const valorPagoAtual = watch('valor_pago'); - if (!valorPagoAtual || valorPagoAtual === 0) { - setValue('valor_pago', total, { shouldDirty: true }); - } - }, [TServicoItemPedidoLocal, setValue, watch]); - - // Dispara a busca do pedido - useEffect(() => { - // Se existir pedido_id, busca o pedido - if (servico_pedido_id) fetchPedido(); - }, [servico_pedido_id, fetchPedido]); - - // Dispara a busca de itens - useEffect(() => { - // Dispara a busca dos itens - setValue('itens', TServicoItemPedidoLocal, { shouldDirty: true }); - }, [TServicoItemPedidoLocal, setValue]); - - // Dispara a busca de parâmetros - useEffect(() => { - loadParamsTServicoPedido(); - }, []); - - // Monitora mudanças na lista de itens - useEffect(() => { - calcularTotais(); - }, [TServicoItemPedidoLocal, calcularTotais]); + const tServicoPedidoController = useTServicoPedidoFormControllerHook(servico_pedido_id) + const { form } = tServicoPedidoController // Memoriza os dados para não renderizar novamente const sections: StepSection[] = useMemo( @@ -317,21 +45,21 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido { key: 'pedido', id: 'selectPedido', - icon: , + icon: , title: 'Pedido', description: 'Dados gerais do pedido.', }, { key: 'servicoPedidoItem', id: 'selectServicoPedidoItem', - icon: , + icon: , title: 'Itens', description: 'Itens/serviços do pedido.', }, { key: 'payment', id: 'selectPayment', - icon: , + icon: , title: 'Pagamento', description: 'Forma e dados de pagamento.', }, @@ -496,15 +224,16 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
@@ -563,20 +292,23 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido ( - - Valor do Pedido - - field.onChange(parseNumberInput(e))} - readOnly={true} - /> - - - - )} + render={({ field }) => { + const valorPedido = form.watch('valor_pedido'); + return ( + + Valor do Pedido + + + + + + ); + }} />
@@ -590,7 +322,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido field.onChange(parseNumberInput(e))} + onChange={(e) => field.onChange(Number(e.target.value))} /> @@ -623,7 +355,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido { - setShouldKeepFormOpen(checked); + tServicoPedidoController.setShouldKeepFormOpen(checked); }} /> @@ -650,15 +382,15 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido className="w-full" variant="outline" type="button" - onClick={handleOpenCancelDialog} + onClick={tServicoPedidoController.handleOpenCancelDialog} > Cancelar @@ -668,41 +400,41 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido {/* Cofirmação de envio de dados */} - {isSaveConfirmOpen && ( + {tServicoPedidoController.isSaveConfirmOpen && ( )} {/* Confirma o cancelamento do pedido */} - {isCancelDialogOpen && ( + {tServicoPedidoController.isCancelDialogOpen && ( )} {/* Modal TPessoa */} - {isPessoaFormOpen && ( + {tServicoPedidoController.isPessoaFormOpen && ( )} diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts index e684870..cb8bd77 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts @@ -1,14 +1,16 @@ 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { FieldValues, Path, PathValue, UseFormSetValue } from 'react-hook-form'; import { GCalculoServicoService } from '@/packages/administrativo/services/GCalculo/GCalculoServicoService'; +import TServicoPedidoItemLocalPrepare from '@/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemLocalPrepare'; import TServicoPedidoItemPreparePayload from '@/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; import TServicoItemPedidoCalculoResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; import { useResponse } from '@/shared/components/response/ResponseContext'; + export function useTServicoItemPedidoCalculoHook( setValue?: UseFormSetValue, ) { @@ -17,8 +19,9 @@ export function useTServicoItemPedidoCalculoHook([]); + const [shouldSync, setShouldSync] = useState(false); - const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { + const calculoTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { const payload = TServicoPedidoItemPreparePayload(data); if (payload.status) { @@ -26,39 +29,41 @@ export function useTServicoItemPedidoCalculoHook { - const safePrev = Array.isArray(prev) ? prev : []; + const itemLocal = TServicoPedidoItemLocalPrepare(data, response); - const novoItem = response; + if (!itemLocal) return; - if (!novoItem) return safePrev; + // Atualiza apenas o estado local + setTServicoItemPedido((prev) => [...prev, itemLocal]); - const novoArray = [...safePrev, novoItem]; + // Marca para sincronizar com o formulário + setShouldSync(true); - if (setValue) { - setValue( - 'itens' as Path, - novoArray as unknown as PathValue>, - { shouldDirty: true }, - ); - } - - return novoArray; - }); - - return response; + return itemLocal; }; + // Efeito seguro para sincronizar com o react-hook-form após o render + useEffect(() => { + if (setValue && shouldSync) { + setValue( + 'itens' as Path, + TServicoItemPedido as unknown as PathValue>, + { shouldDirty: true }, + ); + setShouldSync(false); + } + }, [setValue, shouldSync, TServicoItemPedido]); + return { TServicoItemPedido, setTServicoItemPedido, - addTServicoItemPedido, + calculoTServicoItemPedido, }; } diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts deleted file mode 100644 index bd3aee0..0000000 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts +++ /dev/null @@ -1,47 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { FieldValues, Path, PathValue, UseFormSetValue } from 'react-hook-form'; - -import TServicoItemPedidoCalculoResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; - -export function useTServicoItemPedidoLocalAddHook( - setValue?: UseFormSetValue, -) { - const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState< - TServicoItemPedidoCalculoResponseInterface[] - >([]); - - const localAddTServicoItemPedido = (item: TServicoItemPedidoCalculoResponseInterface) => { - setLocalTServicoItemPedido((prev) => { - const updated = [...prev, item]; - - if (setValue) { - setValue( - 'itens' as Path, - updated as unknown as PathValue>, - ); - } - - return updated; - }); - }; - - const localClearTServicoItemPedido = () => { - setLocalTServicoItemPedido([]); - - if (setValue) { - setValue( - 'itens' as Path, - [] as unknown as PathValue>, - ); - } - }; - - return { - TServicoItemPedidoLocal, - localAddTServicoItemPedido, - setLocalTServicoItemPedido, - localClearTServicoItemPedido, - }; -} diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalHandleHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalHandleHook.ts new file mode 100644 index 0000000..dd88027 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalHandleHook.ts @@ -0,0 +1,55 @@ +'use client'; + +import { useCallback, useEffect, useState } from 'react'; +import { FieldValues, Path, PathValue, UseFormSetValue } from 'react-hook-form'; + +import TServicoItemPedidoCalculoResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; + +export function useTServicoItemPedidoLocalHandleHook( + setValue?: UseFormSetValue, +) { + const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState< + TServicoItemPedidoCalculoResponseInterface[] + >([]); + + // Estado auxiliar para indicar que o form deve ser atualizado + const [shouldSync, setShouldSync] = useState(false); + + // Adiciona item localmente (apenas atualiza o state) + const localAddTServicoItemPedido = (item: TServicoItemPedidoCalculoResponseInterface) => { + setLocalTServicoItemPedido((prev) => [...prev, item]); + setShouldSync(true); + }; + + // Remove item por índice + const localRemoveTServicoItemPedido = useCallback((index: number) => { + setLocalTServicoItemPedido((prev) => prev.filter((_, i) => i !== index)); + setShouldSync(true); + }, []); + + // Limpa os itens + const localClearTServicoItemPedido = () => { + setLocalTServicoItemPedido([]); + setShouldSync(true); + }; + + // Efeito responsável por sincronizar o formulário depois da atualização + useEffect(() => { + if (setValue && shouldSync) { + setValue( + 'itens' as Path, + TServicoItemPedidoLocal as unknown as PathValue>, + { shouldDirty: true }, + ); + setShouldSync(false); + } + }, [setValue, shouldSync, TServicoItemPedidoLocal]); + + return { + TServicoItemPedidoLocal, + localAddTServicoItemPedido, + localRemoveTServicoItemPedido, + setLocalTServicoItemPedido, + localClearTServicoItemPedido, + }; +} diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormControllerHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormControllerHook.ts new file mode 100644 index 0000000..2fb3333 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormControllerHook.ts @@ -0,0 +1,363 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import TPessoaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaInterface'; +import { useTServicoItemPedidoCalculoHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook'; +import { useTServicoItemPedidoIndexHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook'; +import { useTServicoItemPedidoLocalHandleHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalHandleHook'; +import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; +import { useTServicoPedidoLoadParamsHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoLoadParamsHook'; +import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; +import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook'; +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema'; +import { useResponse } from '@/shared/components/response/ResponseContext'; +import { StepNavigatorRef } from '@/shared/components/step/stepNavigator'; +import { SituacoesEnum } from '@/shared/enums/SituacoesEnum'; +import { TipoPessoaEnum } from '@/shared/enums/TipoPessoaEnum'; + +export default function useTServicoPedidoFormControllerHook(servico_pedido_id?: number) { + + const router = useRouter(); + const form = useTServicoPedidoFormHook({}); + const { setValue, reset, watch } = form; + + const [isSaving, setIsSaving] = useState(false); + const [isAdding, setIsAdding] = useState(false); + const [isPessoaFormOpen, setIsPessoaFormOpen] = useState(false); + const [isSaveConfirmOpen, setIsSaveConfirmOpen] = useState(false); + const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false); + const [selectedPessoaTipo, setSelectedPessoaTipo] = useState(''); + const [shouldKeepFormOpen, setShouldKeepFormOpen] = useState(false); + + const ref = useRef(null); + + // Controles de formulário + const handleClosePessoaForm = useCallback(() => setIsPessoaFormOpen(false), []); + const handleOpenSaveConfirm = useCallback(() => setIsSaveConfirmOpen(true), []); + const handleCloseSaveConfirm = useCallback(() => setIsSaveConfirmOpen(false), []); + + // Hooks + // const playSuccess = useSoundHook("/sounds/success.mp3"); + const { setResponse } = useResponse(); + const { saveTServicoPedido } = useTServicoPedidoSaveHook(); + const { showTServicoPedido } = useTServicoPedidoShowHook(); + const { TServicoItemPedidoLocal, localAddTServicoItemPedido, localRemoveTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalHandleHook(setValue); + const { calculoTServicoItemPedido } = useTServicoItemPedidoCalculoHook(setValue); + const { indexTServicoItemPedido } = useTServicoItemPedidoIndexHook(); + const { TServicoPedidoParams, loadParamsTServicoPedido } = useTServicoPedidoLoadParamsHook(); + + // Acompanha as alterações, nos campos definidos + const selectedServicoTipo = watch('servico_tipo'); + const selectedEmolumento = watch('emolumento'); + + const handleFormError = useCallback((errors: any) => { + console.group('Erros de validação do formulário'); + console.log('Campos com erro:', errors); + console.groupEnd(); + }, []); + + // Envia a requisição para a API + const handleSavePedido = useCallback( + async (data: TServicoPedidoFormValues) => { + // Ativa o botão de loading + setIsSaving(true); + + // Converte o tipo do formulário (Zod) para o tipo da API (Interface) + const payload: TServicoPedidoInterface = { + ...data, + situacao: data.situacao as unknown as SituacoesEnum, + }; + + const response = await saveTServicoPedido(payload); + + // Desativa o botão de loading + setIsSaving(false); + + // Verifica se devo redirecionar a pagina + if (response?.servico_pedido_id > 0) { + // Toca o som do sistema + // playSuccess() + } + + // Verifica se devo redirecionar a pagina + if (response?.servico_pedido_id > 0 && !shouldKeepFormOpen) { + router.replace(`/servicos/balcao/detalhes/${response.servico_pedido_id}`); + } + }, + [saveTServicoPedido, shouldKeepFormOpen], + ); + + // Modal de confirmação de serviço + const handleSubmitWithConfirmation = useCallback(() => { + // Envia o formulário + form.handleSubmit(handleSavePedido, handleFormError)(); + }, [form, handleSavePedido, handleFormError]); + + // Busca os itens do Pedido + const fetchPedidoItens = useCallback( + async (id: number) => { + const pedidoItens = { + servico_pedido_id: id, + }; + + // Busca os itens do pedido + const response = await indexTServicoItemPedido(pedidoItens); + + // Verifica se os dados foram localizados + if (response?.data?.length) { + // Atualiza os dados dos itens locais + setLocalTServicoItemPedido(response.data); + + // Atualiza os itens do formulário + setValue('itens', response.data); + } + }, + [indexTServicoItemPedido, setValue, setLocalTServicoItemPedido], + ); + + // Busca o pedido Principal + const fetchPedido = useCallback(async () => { + // Busca o pedido principal + const response = await showTServicoPedido({ servico_pedido_id }); + + // Verifica se o pedido foi localizado + if (response?.servico_pedido_id) { + // Atualiza os dados do formulário + reset(response); + + // Carrega os itens do pedido + fetchPedidoItens(response.servico_pedido_id); + } + }, [servico_pedido_id, showTServicoPedido, reset, fetchPedidoItens]); + + const handleAddItemWithPessoa = useCallback( + async (selectedTPessoa: TPessoaInterface) => { + handleAddItem(selectedTPessoa) + }, [selectedEmolumento, selectedServicoTipo]); + + // Controla o formulário de cancelamento de pedido + const handleOpenCancelDialog = useCallback(async () => { + // Fecha a confirmação + setIsCancelDialogOpen(true); + }, []); + + // Controle de redirecionamento + const handleConfirmCancel = useCallback(async () => { + // Redireciona o usuário + router.replace(`/servicos/balcao/`); + }, []); + + // Controle do formulário de cancelamento do Pedido + const handleCloseCancelDialog = useCallback(async () => { + // Fecha o formulário + setIsCancelDialogOpen(false); + }, []); + + const handleRemoveLocalItem = useCallback(async (index: number) => { + + localRemoveTServicoItemPedido(index) + + }, []); + + // Controle de itens + const handleAddItem = useCallback(async (selectedTPessoa?: TPessoaInterface) => { + + setIsAdding(true); + + // Prepara e valida os dados de item do pedido + const payload = { + servico_tipo: selectedServicoTipo, + emolumento: selectedEmolumento, + }; + + // Se existir pessoa, adiciona ao payload + if (selectedTPessoa) { + payload.pessoa = selectedTPessoa; + } + + // Obtem o resultado da adição do item + const item = await calculoTServicoItemPedido(payload); + + // Verifica se foi realizado o calculo + if (!item) { + setResponse({ + status: 422, + detail: 'Não foi localizado item para o serviço', + }) + setIsAdding(false); + return; + } + + // Define índice e adiciona localmente + const index = TServicoItemPedidoLocal.length; + + // Guarda o indice + item.index = index + + // Adiciona o item calculo localmente + localAddTServicoItemPedido(item) + + // Define os itens + form.setValue(`itens.${index}`, item); + + setIsAdding(false); + + }, [calculoTServicoItemPedido, selectedServicoTipo, selectedEmolumento, localAddTServicoItemPedido]); + + // Habilita o formulário de pessoas + const handleOpenPessoaForm = useCallback((tipoPessoa: string) => { + setSelectedPessoaTipo(tipoPessoa); + setIsPessoaFormOpen(true); + }, []); + + // Adiciona o item a tabela e verifica se deve ou não montar a subview da linha da tabela + const handleSelectServicoTipo = useCallback(() => { + + const tipoPessoa = [TipoPessoaEnum.FISICA, TipoPessoaEnum.JURIDICA] + + // Verifica se o emolumento e o tipo de serviço foram selecionados + if (!selectedServicoTipo || !selectedEmolumento) { + setResponse({ + status: 422, + detail: 'Serviço e emolumento devem ser selecionados', + }) + return; + } + + // Verifica se deve selecionar pessoas + switch (tipoPessoa.includes(selectedServicoTipo.tipo_pessoa)) { + + // Habilita o formulário + case true: + + handleOpenPessoaForm(selectedServicoTipo.tipo_pessoa); + break; + + // Adiciona direto + default: + + handleAddItem(); + break; + + } + + }, [selectedServicoTipo, selectedEmolumento, handleOpenPessoaForm, handleAddItem]); + + const calcularTotais = useCallback(() => { + const itens = form.getValues('itens') || []; + + if (!itens.length) { + setValue('valor_pedido', 0, { shouldDirty: true }); + setValue('valor_pago', 0, { shouldDirty: true }); + return; + } + + const total = itens.reduce((acc, item) => acc + Number(item.valor ?? 0), 0); + + // Atualiza sempre o valor do pedido + setValue('valor_pedido', total, { shouldDirty: true }); + + // Atualiza o valor pago apenas se estiver vazio, null, undefined ou 0 + const valorPagoAtual = Number(form.getValues('valor_pago')) || 0; + if (!valorPagoAtual) { + setValue('valor_pago', total, { shouldDirty: true }); + } + }, [form, setValue]); + + // Incremente ou decrementa a quantidade + const handleChangeQtd = useCallback( + (index: number, delta: number) => { + const currentItens = form.getValues('itens') || []; + const currentItem = currentItens[index]; + if (!currentItem) return; + + const currentQtd = Number(currentItem.qtd ?? 1); + const newQtd = currentQtd + delta; + + // Se quantidade for 0 ou menor, remove o item + if (newQtd <= 0) { + const filteredItens = currentItens.filter((_, i) => i !== index); + form.setValue('itens', filteredItens, { shouldDirty: true }); + localRemoveTServicoItemPedido(index); + calcularTotais(); + return; + } + + const multiplier = newQtd / currentQtd; + + const updatedItem = { + ...currentItem, + qtd: newQtd, + emolumento: (Number(currentItem.emolumento) * multiplier).toFixed(2), + taxa_judiciaria: (Number(currentItem.taxa_judiciaria) * multiplier).toFixed(2), + fundesp: (Number(currentItem.fundesp) * multiplier).toFixed(2), + valor_iss: (Number(currentItem.valor_iss) * multiplier).toFixed(2), + valor: (Number(currentItem.valor) * multiplier).toFixed(2), + }; + + const updatedItens = [...currentItens]; + updatedItens[index] = updatedItem; + + form.setValue('itens', updatedItens, { shouldDirty: true }); + calcularTotais(); + }, + [form, localRemoveTServicoItemPedido, calcularTotais], + ); + + // Dispara a busca do pedido + useEffect(() => { + // Se existir pedido_id, busca o pedido + if (servico_pedido_id) fetchPedido(); + }, [servico_pedido_id, fetchPedido]); + + // Dispara a busca de itens + useEffect(() => { + // Dispara a busca dos itens + setValue('itens', TServicoItemPedidoLocal, { shouldDirty: true }); + }, [TServicoItemPedidoLocal, setValue]); + + // Dispara a busca de parâmetros + useEffect(() => { + loadParamsTServicoPedido(); + }, []); + + // Monitora mudanças na lista de itens + useEffect(() => { + calcularTotais(); + }, [TServicoItemPedidoLocal, calcularTotais]); + + return { + form, + ref, + isSaving, + isAdding, + isPessoaFormOpen, + isSaveConfirmOpen, + isCancelDialogOpen, + shouldKeepFormOpen, + selectedPessoaTipo, + TServicoItemPedidoLocal, + TServicoPedidoParams, + + // setters diretos + setIsSaveConfirmOpen, + setIsCancelDialogOpen, + setShouldKeepFormOpen, + + // handlers principais + handleSavePedido, + handleSelectServicoTipo, + handleSubmitWithConfirmation, + handleCloseSaveConfirm, + handleOpenCancelDialog, + handleConfirmCancel, + handleCloseCancelDialog, + handleClosePessoaForm, + handleAddItemWithPessoa, + handleOpenSaveConfirm, + handleChangeQtd + }; +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts index 8f68df3..14efcef 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts @@ -1,12 +1,13 @@ import { UseFormReturn } from 'react-hook-form'; import TServicoItemPedidoAddResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; +import { TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema'; import GConfigInterface from '@/shared/interfaces/GConfigInterface'; -import { TServicoPedidoFormValues } from '../../schemas/TServicoPedido/TServicoPedidoFormSchema'; export default interface TServicoItemPedidoFormTableInterface { data: TServicoItemPedidoAddResponseInterface[]; form: UseFormReturn; params: GConfigInterface[]; + handleChangeQtd: any; } diff --git a/src/shared/enums/TipoPessoaEnum.ts b/src/shared/enums/TipoPessoaEnum.ts new file mode 100644 index 0000000..e5c774d --- /dev/null +++ b/src/shared/enums/TipoPessoaEnum.ts @@ -0,0 +1,4 @@ +export const TipoPessoaEnum = { + FISICA: "F", + JURIDICA: "J" +} as const \ No newline at end of file