From 8d446867175c35bf250a466e6781fc5f463f0668 Mon Sep 17 00:00:00 2001 From: keven Date: Thu, 30 Oct 2025 15:58:48 -0300 Subject: [PATCH 01/14] =?UTF-8?q?[MVPTN-37]=20feat(CRUD):=20Criar=20parcia?= =?UTF-8?q?lmente=20o=20formul=C3=A1rio=20e=20implementa=20a=20listagem=20?= =?UTF-8?q?de=20dados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../servicos/balcao/pedido/page.tsx | 89 +--- .../components/GUsuario/GUsuarioSelect.tsx | 114 ++++++ .../GUsuario/GUsuarioSelectInterface.ts | 6 + .../TServicoPedido/TServicoPedidoColumns.tsx | 79 +++- .../TServicoPedidoForm copy.tsx | 110 +++++ .../TServicoPedido/TServicoPedidoForm.tsx | 384 ++++++++++++++---- .../TServicoPedido/TServicoPedidoIndex.tsx | 6 +- .../TServicoPedido/TServicoPedidoTable.tsx | 6 +- .../TServicoPedidoDeleteData.ts | 2 +- .../TServicoPedido/TServicoPedidoIndexData.ts | 2 +- .../TServicoPedido/TServicoPedidoSaveData.ts | 2 +- .../TServicoPedidoFormInterface.ts | 4 +- .../TServicoPedido/TServicoPedidoInterface.ts | 2 + .../TServicoPedido/TServicoPedidoSchema.ts | 29 +- .../ServicosPedidosSituacoesBadge.tsx | 96 +++++ .../ServicosPedidosSituacoesSelect.tsx | 73 ++++ src/shared/components/structure/Header.tsx | 36 +- .../enums/ServicosPedidosSituacoesEnum.ts | 5 + 18 files changed, 831 insertions(+), 214 deletions(-) create mode 100644 src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx create mode 100644 src/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface.ts create mode 100644 src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx create mode 100644 src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge.tsx create mode 100644 src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesSelect.tsx create mode 100644 src/shared/enums/ServicosPedidosSituacoesEnum.ts diff --git a/src/app/(protected)/servicos/balcao/pedido/page.tsx b/src/app/(protected)/servicos/balcao/pedido/page.tsx index 12d4839..ceb16b2 100644 --- a/src/app/(protected)/servicos/balcao/pedido/page.tsx +++ b/src/app/(protected)/servicos/balcao/pedido/page.tsx @@ -1,90 +1,7 @@ -import { Card, CardContent } from "@/components/ui/card"; -import TServicoItemPedidoResumo from "@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoResumo"; +import TServicoPedidoForm from "@/packages/servicos/components/TServicoPedido/TServicoPedidoForm" -const itens = [ - { - id: 1, - descricao: "Reconhecimento de Firma", - valor: 12.50, - }, - { - id: 2, - descricao: "Autenticação de Cópia", - valor: 6.00, - }, - { - id: 3, - descricao: "Procuração Pública", - valor: 98.75, - }, - { - id: 4, - descricao: "Certidão de Escritura", - valor: 42.30, - }, - { - id: 5, - descricao: "Registro de Documento", - valor: 73.10, - }, -] - - -export default function PedidoPage() { +export default function TServicoPedidoPage() { return ( -
-
-
- - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
-
-
-
-
- -
-
-
+ ) } \ No newline at end of file diff --git a/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx b/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx new file mode 100644 index 0000000..856e772 --- /dev/null +++ b/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx @@ -0,0 +1,114 @@ +'use client'; + +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import { useGUsuarioIndexHook } from '@/packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook'; +import GUsuarioSelectInterface from '@/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; + + +export default function GUsuarioSelect({ field }: GUsuarioSelectInterface) { + const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const { usuarios, fetchUsuarios } = useGUsuarioIndexHook(); + + /** + * Efeito para buscar os dados apenas uma vez. + * useCallback evita recriação desnecessária da função. + */ + const loadData = useCallback(async () => { + if (usuarios?.length) return; + setIsLoading(true); + await fetchUsuarios(); + setIsLoading(false); + }, [usuarios?.length, fetchUsuarios]); + + useEffect(() => { + loadData(); + }, [loadData]); + + /** + * Memoriza o bairro selecionado para evitar reprocessamentos. + */ + const selected = useMemo( + () => usuarios?.find((b) => String(b.usuario_id) === String(field?.value ?? '')), + [usuarios, field?.value], + ); + + /** + * Manipulador de seleção com verificação segura. + */ + const handleSelect = useCallback( + (bairroId: string | number) => { + if (!field?.onChange) return; + field.onChange(bairroId); + setOpen(false); + }, + [field], + ); + + return ( + + + + + + + + + + + + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} + + + {usuarios?.map((item) => ( + handleSelect(item.usuario_id)} + > + + {GetCapitalize(item.nome_completo ?? '')} + + ))} + + + + + + ); +} diff --git a/src/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface.ts b/src/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface.ts new file mode 100644 index 0000000..bd695f8 --- /dev/null +++ b/src/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface.ts @@ -0,0 +1,6 @@ +export default interface GTBairroSelectInterface { + field?: { + value?: number | string; + onChange?: (value: string | number) => void; + }; +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx index 7dc34b7..8c883bd 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx @@ -10,8 +10,13 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; +import { FormatCPF } from '@/shared/actions/CPF/FormatCPF'; +import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; +import GetNameInitials from '@/shared/actions/text/GetNameInitials'; +import empty from '@/shared/actions/validations/empty'; import { SortableHeader } from '@/shared/components/dataTable/SortableHeader'; +import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; import TServicoPedidoInterface from '../../interfaces/TServicoPedido/TServicoPedidoInterface'; @@ -22,17 +27,75 @@ export default function TServicoPedidoColumns( return [ // ID { - accessorKey: 'gramatica_id', + accessorKey: 'servico_pedido_id', header: ({ column }) => SortableHeader('ID', column), - cell: ({ row }) => Number(row.getValue('gramatica_id')), + cell: ({ row }) => Number(row.getValue('servico_pedido_id')), enableSorting: true, }, - // Descrição + // Data Pedido { - accessorKey: 'palavra', - header: ({ column }) => SortableHeader('Palavra', column), - cell: ({ row }) => GetCapitalize(String(row.getValue('palavra') || '')), + accessorKey: 'data_pedido', + header: ({ column }) => SortableHeader('Data', column), + cell: ({ row }) => FormatDateTime(row.getValue('data_pedido')), + }, + + // Apresentante + { + accessorKey: 'apresentante', + header: ({ column }) => SortableHeader('Apresentante', column), + cell: ({ row }) => { + const data = row.original; + return ( +
+ {/* Foto ou Iniciais */} +
+ + {GetNameInitials(data.apresentante)} + +
+
+
{data.apresentante || '-'}
+
+ {empty(data.cpfcnpj_apresentante) ? '---' : FormatCPF(data.cpfcnpj_apresentante)} +
+
+
+ ); + }, + sortingFn: (a, b) => + (a.original.apresentante?.toLowerCase() || '').localeCompare(b.original.apresentante?.toLowerCase() || ''), + }, + + // Situação + { + accessorKey: 'situacao', + header: ({ column }) => SortableHeader('Situação', column), + cell: ({ row }) => { + return ( + + ) + }, + }, + + // Valor Pedido + { + accessorKey: 'valor_pedido', + header: ({ column }) => SortableHeader('Total', column), + cell: ({ row }) => { + return ( +
+ R$ {row.getValue('valor_pedido') || '---'} +
+ ); + }, + }, + + // Usuário + { + accessorKey: 'login', + header: ({ column }) => SortableHeader('Operador', column), + cell: ({ row }) => GetCapitalize(String(row.getValue('login') || '')), }, // Ações @@ -41,7 +104,6 @@ export default function TServicoPedidoColumns( header: 'Ações', cell: ({ row }) => { const natureza = row.original; - return ( @@ -49,16 +111,13 @@ export default function TServicoPedidoColumns( - onEdit(natureza, true)}> Editar - - onDelete(natureza, true)} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx new file mode 100644 index 0000000..ea3e993 --- /dev/null +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx @@ -0,0 +1,110 @@ +'use client'; + +import { useEffect } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; +import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; + +import { useTServicoPedidoFormHook } from '../../hooks/TServicoPedido/useTServicoPedidoFormHook'; +import { TServicoPedidoFormInterface } from '../../interfaces/TServicoPedido/TServicoPedidoFormInterface'; + +/** + * Formulário de cadastro/edição de Natureza + * Baseado nos campos da tabela G_NATUREZA + */ +export default function TServicoPedidoForm({ + isOpen, + data, + onClose, + onSave, + buttonIsLoading, +}: TServicoPedidoFormInterface) { + const form = useTServicoPedidoFormHook({}); + + // Atualiza o formulário quando recebe dados para edição + useEffect(() => { + ResetFormIfData(form, data); + }, [data, form]); + + function onError(error: any) { + console.log('Erro no formulário:', error); + } + + return ( + { + if (!open) onClose(null, false); + }} + > + + + Formulário de Gramática + + Formulário de Gramática + + + {/* Formulário principal */} +
+ + {/* GRID MOBILE FIRST */} +
+ {/* Palavra */} +
+ ( + + Palavra + + + + + + )} + /> +
+
+ {/* Rodapé */} + + + + + + +
+ +
+
+ ); +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index ea3e993..80ce376 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -1,110 +1,324 @@ 'use client'; -import { useEffect } from 'react'; +import { CreditCard, Package, UserSquare2 } from 'lucide-react'; +import * as React from 'react'; -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Form, FormControl, FormField, FormItem, FormLabel, - FormMessage, + FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import { cn } from '@/lib/utils'; +import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; +import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; -import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; -import { useTServicoPedidoFormHook } from '../../hooks/TServicoPedido/useTServicoPedidoFormHook'; -import { TServicoPedidoFormInterface } from '../../interfaces/TServicoPedido/TServicoPedidoFormInterface'; +type StepKey = 'pedido' | 'servicoPedidoItem' | 'payment'; -/** - * Formulário de cadastro/edição de Natureza - * Baseado nos campos da tabela G_NATUREZA - */ -export default function TServicoPedidoForm({ - isOpen, - data, - onClose, - onSave, - buttonIsLoading, -}: TServicoPedidoFormInterface) { +const SECTION_ORDER: { key: StepKey; id: string }[] = [ + { key: 'pedido', id: 'selectPedido' }, + { key: 'servicoPedidoItem', id: 'selectServicoPedidoItem' }, + { key: 'payment', id: 'selectPayment' }, +]; + +const SCROLL_OFFSET = 16; +const SPY_LOCK_MS = 600; + +export default function TServicoPedidoForm() { + const data = {}; const form = useTServicoPedidoFormHook({}); - // Atualiza o formulário quando recebe dados para edição - useEffect(() => { - ResetFormIfData(form, data); - }, [data, form]); + const [active, setActive] = React.useState('pedido'); + // submit e error do RHF + function onSubmit(values: any) { + console.log('Submit:', values); + } function onError(error: any) { console.log('Erro no formulário:', error); } + // === Bloqueio do scroll-spy durante rolagem programática === + const spyLockedRef = React.useRef(false); + const spyTimerRef = React.useRef(null); + + const lockSpy = React.useCallback(() => { + spyLockedRef.current = true; + if (spyTimerRef.current) window.clearTimeout(spyTimerRef.current); + spyTimerRef.current = window.setTimeout(() => { + spyLockedRef.current = false; + }, SPY_LOCK_MS) as unknown as number; + }, []); + + const scrollToSection = React.useCallback( + (id: string) => { + const el = document.getElementById(id); + if (!el) return; + lockSpy(); + const y = el.getBoundingClientRect().top + window.scrollY - SCROLL_OFFSET; + window.scrollTo({ top: y, behavior: 'smooth' }); + }, + [lockSpy] + ); + + // Scroll spy + React.useEffect(() => { + const handler = () => { + if (spyLockedRef.current) return; + + let current: StepKey = 'pedido'; + let best = Number.POSITIVE_INFINITY; + + for (const { key, id } of SECTION_ORDER) { + const el = document.getElementById(id); + if (!el) continue; + const dist = Math.abs(el.getBoundingClientRect().top - SCROLL_OFFSET - 8); + if (dist < best) { + best = dist; + current = key; + } + } + if (current !== active) setActive(current); + }; + + handler(); + window.addEventListener('scroll', handler, { passive: true }); + return () => window.removeEventListener('scroll', handler); + }, [active]); + + // Atualiza o formulário quando recebe dados para edição + React.useEffect(() => { + ResetFormIfData(form, data); + }, [data, form]); + return ( - { - if (!open) onClose(null, false); - }} - > - - - Formulário de Gramática - - Formulário de Gramática - - - {/* Formulário principal */} -
- - {/* GRID MOBILE FIRST */} -
- {/* Palavra */} -
- ( - - Palavra - - - - - - )} - /> -
+ + +
+ {/* Sidebar - Sticky */} +
+ + {/* Conteúdo */} +
+
+ {/* Seção: Pedido */} + + + Pedido + + +
+
+ ( + + Escrevente/Tabelião + + + + )} + /> +
+
+ ( + + Apresentante + + + + + + )} + /> +
+
+ ( + + CPF + + + + + + )} + /> +
+
+ ( + + Pessoa presente no selo + + + + + + )} + /> +
+
+ ( + + CPF + + + + + + )} + /> +
+
+
+
+ + {/* Seção: Itens */} + + + Itens + + +
+
+ ( + + Escrevente/Tabelião + + + + )} + /> +
+
+
+
+ + {/* Seção: Pagamento */} + + + Pagamento + + ... + +
+
+ + + + ); +} + +/* ---------- Components auxiliares ---------- */ + +function StepLink({ + active, + onClick, + icon, + title, + description, +}: { + active?: boolean; + onClick?: () => void; + icon: React.ReactNode; + title: string; + description: string; +}) { + return ( + ); } diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx index dedf0e0..0d54fdf 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx @@ -109,7 +109,7 @@ export default function TServicoPedidoIndex() { * Busca inicial dos dados */ useEffect(() => { - // indexTServicoPedido(); + indexTServicoPedido(); }, []); return ( @@ -119,9 +119,7 @@ export default function TServicoPedidoIndex() { title={'Pedidos'} description={'Pedidos de Autenticação/Reconhecimento'} buttonText={'Novo pedido'} - buttonAction={() => { - handleOpenForm(null); - }} + href='/servicos/balcao/pedido' /> {/* Tabela de andamentos */} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx index 6b31591..29bd154 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx @@ -1,9 +1,9 @@ 'use client'; +import TServicoPedidoTableInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoTableInterface'; import { DataTable } from '@/shared/components/dataTable/DataTable'; import TServicoPedidoColumns from './TServicoPedidoColumns'; -import TServicoPedidoTableInterface from '../../interfaces/TServicoPedido/TServicoPedidoTableInterface'; /** * Componente principal da tabela de Naturezas @@ -15,8 +15,8 @@ export default function TServicoPedidoTable({ data, onEdit, onDelete }: TServico ); diff --git a/src/packages/servicos/data/TServicoPedido/TServicoPedidoDeleteData.ts b/src/packages/servicos/data/TServicoPedido/TServicoPedidoDeleteData.ts index 226517a..f6f9cb3 100644 --- a/src/packages/servicos/data/TServicoPedido/TServicoPedidoDeleteData.ts +++ b/src/packages/servicos/data/TServicoPedido/TServicoPedidoDeleteData.ts @@ -10,7 +10,7 @@ async function executeTServicoPedidoDeleteData(data: TServicoPedidoInterface): P return api.send({ method: Methods.DELETE, - endpoint: `servico/t_servico_pedido/${data.servico_pedido_id}`, + endpoint: `servicos/balcao/t_servico_pedido/${data.servico_pedido_id}`, }); } diff --git a/src/packages/servicos/data/TServicoPedido/TServicoPedidoIndexData.ts b/src/packages/servicos/data/TServicoPedido/TServicoPedidoIndexData.ts index 5cfa3e9..fe48526 100644 --- a/src/packages/servicos/data/TServicoPedido/TServicoPedidoIndexData.ts +++ b/src/packages/servicos/data/TServicoPedido/TServicoPedidoIndexData.ts @@ -8,7 +8,7 @@ async function executeTServicoPedidoIndexData(): Promise { return api.send({ method: Methods.GET, - endpoint: `servico/t_servico_pedido/`, + endpoint: `servicos/balcao/t_servico_pedido/`, }); } diff --git a/src/packages/servicos/data/TServicoPedido/TServicoPedidoSaveData.ts b/src/packages/servicos/data/TServicoPedido/TServicoPedidoSaveData.ts index f3eed76..2123ed6 100644 --- a/src/packages/servicos/data/TServicoPedido/TServicoPedidoSaveData.ts +++ b/src/packages/servicos/data/TServicoPedido/TServicoPedidoSaveData.ts @@ -15,7 +15,7 @@ async function executeTServicoPedidoSaveData(data: TServicoPedidoInterface): Pro // Executa a requisição para a API com o método apropriado e envia os dados no corpo return api.send({ method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar - endpoint: `servico/t_servico_pedido/${data.servico_pedido_id || ''}`, // endpoint dinâmico + endpoint: `servicos/balcao/t_servico_pedido/${data.servico_pedido_id || ''}`, // endpoint dinâmico body: data, // payload enviado para a API }); } diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts index cd375ef..827f2f1 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts @@ -1,9 +1,7 @@ -import { TServicoPedidoFormValues } from '../../schemas/TServicoPedido/TServicoPedidoSchema'; +import { TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema'; export interface TServicoPedidoFormInterface { - isOpen: boolean; data: TServicoPedidoFormValues | null; - onClose: (item: null, isFormStatus: boolean) => void; onSave: (data: TServicoPedidoFormValues) => void; buttonIsLoading: boolean; } diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index c2e3d06..b0221f0 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -12,4 +12,6 @@ export default interface TServicoPedidoInterface { apresentante?: string; nfse_id?: number; cpfcnpj_apresentante?: string; + selo_pessoa_nome?: string; + selo_pessoa_cpfcnpj?: string; } \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 8fffc16..302fe6e 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -1,19 +1,22 @@ import z from "zod"; export const TServicoPedidoSchema = z.object({ - servico_pedido_id: z.number().optional, - valor_pedido: z.number().optional, - valor_pago: z.number().optional, - usuario_id: z.number().optional, - data_pedido: z.string().optional, - mensalista_livrocaixa_id: z.number().optional, - observacao: z.string().optional, - escrevente_id: z.number().optional, - situacao: z.string().optional, - estornado: z.string().optional, - apresentante: z.string().optional, - nfse_id: z.number().optional, - cpfcnpj_apresentante: z.string().optional, + servico_pedido_id: z.number().optional(), + valor_pedido: z.number().optional(), + valor_pago: z.number().optional(), + usuario_id: z.number().optional(), + data_pedido: z.string().optional(), + mensalista_livrocaixa_id: z.number().optional(), + observacao: z.string().optional(), + escrevente_id: z.number().optional(), + situacao: z.string().optional(), + estornado: z.string().optional(), + apresentante: z.string().optional(), + nfse_id: z.number().optional(), + cpfcnpj_apresentante: z.string().optional(), + login: z.string().optional(), + selo_pessoa_nome: z.string().optional(), + selo_pessoa_cpfcnpj: z.string().optional(), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge.tsx b/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge.tsx new file mode 100644 index 0000000..5c90c99 --- /dev/null +++ b/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { AlertCircleIcon, BadgeCheckIcon, CheckIcon } from 'lucide-react'; +import * as React from 'react'; + +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; +import { ServicosPedidosSituacoesEnum } from '@/shared/enums/ServicosPedidosSituacoesEnum'; + +type Situacao = keyof typeof ServicosPedidosSituacoesEnum; + +type Props = { + situacao?: Situacao | null; // 'A' | 'F' | 'C' + showLabel?: boolean; // exibir texto? (default: true) + compact?: boolean; // menor altura/tamanho (default: false) + className?: string; // classes extras +}; + +type Cfg = { + label: string; + icon: React.ComponentType>; + bgClass: string; // fundo "subtle" + textClass: string; // cor do texto/ícone +}; + +const CONFIG: Record = { + A: { + label: ServicosPedidosSituacoesEnum.A, // Aberto + icon: AlertCircleIcon, + bgClass: 'bg-blue-50 dark:bg-blue-900/20', + textClass: 'text-blue-700 dark:text-blue-300', + }, + F: { + label: ServicosPedidosSituacoesEnum.F, // Fechado + icon: BadgeCheckIcon, + bgClass: 'bg-emerald-50 dark:bg-emerald-900/20', + textClass: 'text-emerald-700 dark:text-emerald-300', + }, + C: { + label: ServicosPedidosSituacoesEnum.C, // Cancelado + icon: CheckIcon, // troque por XIcon se preferir + bgClass: 'bg-red-50 dark:bg-red-900/20', + textClass: 'text-red-700 dark:text-red-300', + }, +}; + +export function ServicosPedidosSituacoesBadge({ + situacao, + showLabel = true, + compact = false, + className, +}: Props) { + if (!situacao || !(situacao in CONFIG)) { + return ( + + + {showLabel ? ( + Indefinido + ) : ( + Indefinido + )} + + ); + } + + const { label, icon: Icon, bgClass, textClass } = CONFIG[situacao]; + + return ( + " + 'border border-gray-100 dark:border-gray-700 text-gray-900 dark:text-gray-50 rounded-md gap-1.5', + bgClass, + compact ? 'h-5 px-2 text-[11px]' : 'h-6 px-2.5 text-xs', + className + )} + > + {/* Ícone e label com cor semântica, como "" */} + + {showLabel ? ( + {label} + ) : ( + {label} + )} + + ); +} diff --git a/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesSelect.tsx b/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesSelect.tsx new file mode 100644 index 0000000..bb622b0 --- /dev/null +++ b/src/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesSelect.tsx @@ -0,0 +1,73 @@ +import { Command } from 'cmdk'; +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import React from 'react'; + +import { Button } from '@/components/ui/button'; +import { + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import { ServicosPedidosSituacoesEnum } from '@/shared/enums/ServicosPedidosSituacoesEnum'; + +export default function SituacoesSelect({ field }: any) { + const [open, setOpen] = React.useState(false); + + const options = Object.entries(ServicosPedidosSituacoesEnum).map(([value, label]) => ({ + value, + label, + })); + + return ( + + + + + + + + + + + Nenhum resultado encontrado. + + {options.map((item) => ( + { + field.onChange(item.value); + setOpen(false); + }} + > + + {item.label} + + ))} + + + + + + ); +} diff --git a/src/shared/components/structure/Header.tsx b/src/shared/components/structure/Header.tsx index d7c0763..b462cbe 100644 --- a/src/shared/components/structure/Header.tsx +++ b/src/shared/components/structure/Header.tsx @@ -1,4 +1,7 @@ +'use client'; + import { PlusIcon } from 'lucide-react'; +import Link from 'next/link'; import { Button } from '@/components/ui/button'; @@ -6,21 +9,40 @@ interface HeaderProps { title: string; description: string; buttonText: string; - buttonAction: (...args: any[]) => void; + href?: `/${string}`; + buttonAction?: () => void; + className?: string; } -export default function Header({ title, description, buttonText, buttonAction }: HeaderProps) { +export default function Header({ + title, + description, + buttonText, + href, + buttonAction, + className, +}: HeaderProps) { return ( -
+

{title}

{description}

- + + {href ? ( + + ) : ( + + )}
); diff --git a/src/shared/enums/ServicosPedidosSituacoesEnum.ts b/src/shared/enums/ServicosPedidosSituacoesEnum.ts new file mode 100644 index 0000000..0ebc674 --- /dev/null +++ b/src/shared/enums/ServicosPedidosSituacoesEnum.ts @@ -0,0 +1,5 @@ +export enum ServicosPedidosSituacoesEnum { + A = 'Aberto', + F = 'Fechado', + C = 'Cancelado', +} From e50818e52a110bc2b1eeceba097a89505a40934d Mon Sep 17 00:00:00 2001 From: keven Date: Tue, 4 Nov 2025 08:16:28 -0300 Subject: [PATCH 02/14] =?UTF-8?q?backup:=20Backup=20de=20c=C3=B3digo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 33 + package.json | 1 + .../(t_servico_tipo)/servicos-tipo/page.tsx | 14 +- .../t_servico_tipo/TServicoTipoForm.tsx | 36 +- .../detalhes/[servicoPedidoId]/page.tsx | 16 + src/app/globals.css | 230 ++++--- .../TServicoTipo/TServicoTipoSelect.tsx | 114 ++++ .../TServicoTipo/TServicoTipoEditData.ts | 2 +- .../TServicoTipo/TServicoTipoIndexData.ts | 0 .../TServicoTipo/TServicoTipoRemoveData.ts | 2 +- .../TServicoTipo/TServicoTipoSaveData.ts | 3 +- .../TServicoTipo}/useTServicoTipoEditHook.ts | 4 +- .../TServicoTipo}/useTServicoTipoReadHook.ts | 4 +- .../useTServicoTipoRemoveHook.ts | 4 +- .../TServicoTipo}/useTServicoTipoSaveHook.ts | 6 +- .../TServicoTipoSelectInterface.ts | 6 + .../TServicoTipo}/TServicoTipoEditService.ts | 4 +- .../TServicoTipo}/TServicoTipoIndexService.ts | 2 +- .../TServicoTipoRemoveService.ts | 4 +- .../TServicoTipo}/TServicoTipoSaveService.ts | 4 +- .../TServicoItemPedidoFormColumns.tsx | 133 ++++ .../TServicoItemPedidoFormTable.tsx | 24 + .../TServicoItemPedidoList.tsx | 65 ++ .../TServicoPedido/TServicoPedidoColumns.tsx | 19 +- .../TServicoPedido/TServicoPedidoDetails.tsx | 157 +++++ .../TServicoPedidoDetailsPagamento.tsx | 83 +++ .../TServicoPedidoForm copy.tsx | 110 ---- .../TServicoPedido/TServicoPedidoForm.tsx | 585 ++++++++++++------ .../TServicoItemPedidoIndexData.ts | 9 +- .../TServicoItemPedidoShowData.ts | 16 + .../TServicoPedido/TServicoPedidoShowData.ts | 16 + .../useTServicoItemPedidoIndexHook.ts | 22 +- .../useTServicoItemPedidoShowHook.ts | 28 + .../useTServicoPedidoShowHook.ts | 29 + ...ServicoItemPedidoIndexResponseInterface.ts | 11 + .../TServicoItemPedidoListInterface.ts | 5 + .../TServicoPedidoDetailsInterface.ts | 3 + ...TServicoPedidoDetailsPagamentoInterface.ts | 8 + .../TServicoPedido/TServicoPedidoInterface.ts | 2 + .../TServicoItemPedidoIndexService.ts | 9 +- .../TServicoItemPedidoShowService.ts | 17 + .../TServicoPedidoShowService.ts | 12 + .../tipoPagamento/TipoPagamentoSelect.tsx | 85 +++ src/shared/enums/TipoPagamentoEnum.ts | 6 + src/shared/hooks/useRunOnceByKey.ts | 18 + 45 files changed, 1518 insertions(+), 443 deletions(-) create mode 100644 src/app/(protected)/servicos/balcao/detalhes/[servicoPedidoId]/page.tsx create mode 100644 src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx rename src/{app/(protected)/(cadastros)/cadastros/_data => packages/administrativo/data}/TServicoTipo/TServicoTipoEditData.ts (89%) rename src/{app/(protected)/(cadastros)/cadastros/_data => packages/administrativo/data}/TServicoTipo/TServicoTipoIndexData.ts (100%) rename src/{app/(protected)/(cadastros)/cadastros/_data => packages/administrativo/data}/TServicoTipo/TServicoTipoRemoveData.ts (89%) rename src/{app/(protected)/(cadastros)/cadastros/_data => packages/administrativo/data}/TServicoTipo/TServicoTipoSaveData.ts (91%) rename src/{app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo => packages/administrativo/hooks/TServicoTipo}/useTServicoTipoEditHook.ts (81%) rename src/{app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo => packages/administrativo/hooks/TServicoTipo}/useTServicoTipoReadHook.ts (85%) rename src/{app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo => packages/administrativo/hooks/TServicoTipo}/useTServicoTipoRemoveHook.ts (81%) rename src/{app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo => packages/administrativo/hooks/TServicoTipo}/useTServicoTipoSaveHook.ts (82%) create mode 100644 src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface.ts rename src/{app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo => packages/administrativo/services/TServicoTipo}/TServicoTipoEditService.ts (79%) rename src/{app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo => packages/administrativo/services/TServicoTipo}/TServicoTipoIndexService.ts (90%) rename src/{app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo => packages/administrativo/services/TServicoTipo}/TServicoTipoRemoveService.ts (79%) rename src/{app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo => packages/administrativo/services/TServicoTipo}/TServicoTipoSaveService.ts (80%) create mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx create mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx create mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx create mode 100644 src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx create mode 100644 src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx delete mode 100644 src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx create mode 100644 src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoShowData.ts create mode 100644 src/packages/servicos/data/TServicoPedido/TServicoPedidoShowData.ts create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoShowHook.ts create mode 100644 src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook.ts create mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts create mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts create mode 100644 src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsInterface.ts create mode 100644 src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts create mode 100644 src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoShowService.ts create mode 100644 src/packages/servicos/services/TServicoPedido/TServicoPedidoShowService.ts create mode 100644 src/shared/components/tipoPagamento/TipoPagamentoSelect.tsx create mode 100644 src/shared/enums/TipoPagamentoEnum.ts create mode 100644 src/shared/hooks/useRunOnceByKey.ts diff --git a/package-lock.json b/package-lock.json index 9c71c6f..d61e6db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@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.7", @@ -1992,6 +1993,38 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", diff --git a/package.json b/package.json index 1fe27a2..534cfa0 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@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.7", diff --git a/src/app/(protected)/(cadastros)/cadastros/(t_servico_tipo)/servicos-tipo/page.tsx b/src/app/(protected)/(cadastros)/cadastros/(t_servico_tipo)/servicos-tipo/page.tsx index 5215447..c976323 100644 --- a/src/app/(protected)/(cadastros)/cadastros/(t_servico_tipo)/servicos-tipo/page.tsx +++ b/src/app/(protected)/(cadastros)/cadastros/(t_servico_tipo)/servicos-tipo/page.tsx @@ -1,26 +1,26 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; import { Card, CardContent } from '@/components/ui/card'; +import { useCallback, useEffect, useState } from 'react'; import Loading from '@/shared/components/loading/loading'; // Componentes específicos para TServicoTipo -import TServicoTipoTable from '../../_components/t_servico_tipo/TServicoTipoTable'; import TServicoTipoForm from '../../_components/t_servico_tipo/TServicoTipoForm'; +import TServicoTipoTable from '../../_components/t_servico_tipo/TServicoTipoTable'; // Hooks específicos para TServicoTipo -import { useTServicoTipoReadHook } from '../../_hooks/t_servico_tipo/useTServicoTipoReadHook'; -import { useTServicoTipoSaveHook } from '../../_hooks/t_servico_tipo/useTServicoTipoSaveHook'; -import { useTServicoTipoRemoveHook } from '../../_hooks/t_servico_tipo/useTServicoTipoRemoveHook'; -import { useTServicoTipoEditHook } from '../../_hooks/t_servico_tipo/useTServicoTipoEditHook'; +import { useTServicoTipoEditHook } from '../../../../../../packages/administrativo/hooks/TServicoTipo/useTServicoTipoEditHook'; +import { useTServicoTipoReadHook } from '../../../../../../packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook'; +import { useTServicoTipoRemoveHook } from '../../../../../../packages/administrativo/hooks/TServicoTipo/useTServicoTipoRemoveHook'; +import { useTServicoTipoSaveHook } from '../../../../../../packages/administrativo/hooks/TServicoTipo/useTServicoTipoSaveHook'; import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog'; import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog'; // Interface específica para TServicoTipo -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; import Header from '@/shared/components/structure/Header'; +import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; export default function TServicoTipoPage() { // Hooks para leitura, salvamento e remoção diff --git a/src/app/(protected)/(cadastros)/cadastros/_components/t_servico_tipo/TServicoTipoForm.tsx b/src/app/(protected)/(cadastros)/cadastros/_components/t_servico_tipo/TServicoTipoForm.tsx index 37ac514..e9da655 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_components/t_servico_tipo/TServicoTipoForm.tsx +++ b/src/app/(protected)/(cadastros)/cadastros/_components/t_servico_tipo/TServicoTipoForm.tsx @@ -1,11 +1,7 @@ 'use client'; -import React from 'react'; -import z from 'zod'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useState } from 'react'; import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { Dialog, DialogClose, @@ -15,8 +11,6 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { CirclePlus, DollarSign, Settings, SquarePen, Trash } from 'lucide-react'; import { Form, FormControl, @@ -26,13 +20,10 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { Checkbox } from '@/components/ui/checkbox'; import { Select, SelectContent, - SelectItem, - SelectTrigger, - SelectValue, + SelectItem } from '@/components/ui/select'; import { Table, @@ -42,23 +33,28 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { CirclePlus, DollarSign, Settings, SquarePen, Trash } from 'lucide-react'; +import React, { useState } from 'react'; +import { useForm } from 'react-hook-form'; -import { TServicoTipoSchema, TServicoTipoFormValues } from '../../_schemas/TServicoTipoSchema'; -import { useEffect } from 'react'; -import GMarcacaoTipoSelect from '@/packages/administrativo/components/GMarcacaoTipo/GMarcacaoTipoSelect'; -import GEmolumentoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoSelect'; import { useGEmolumentoItemReadHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook'; +import { useTServicoEtiquetaReadHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaReadHook'; +import { useTServicoEtiquetaRemoveHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaRemoveHook'; +import { useTServicoEtiquetaSaveHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaSaveHook'; import { GEmolumentoItemReadInterface } from '@/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoItemReadInterface'; -import CategoriaServicoSelect from '@/shared/components/categoriaServicoSelect/CategoriaServicoSelect'; import CCaixaServicoSelect from '@/packages/administrativo/components/CCaixaServico/CCaixaServicoSelect'; -import { TServicoTipoSaveData } from '../../_data/TServicoTipo/TServicoTipoSaveData'; +import GEmolumentoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoSelect'; +import GMarcacaoTipoSelect from '@/packages/administrativo/components/GMarcacaoTipo/GMarcacaoTipoSelect'; import TTBReconhecimentoTipoSelect from '@/packages/administrativo/components/TTBReconhecimentoTipo/TTBReconhecimentoTipoSelect'; +import CategoriaServicoSelect from '@/shared/components/categoriaServicoSelect/CategoriaServicoSelect'; import { ConfirmacaoCheckBox } from '@/shared/components/confirmacao/ConfirmacaoCheckBox'; import ConfirmacaoSelect from '@/shared/components/confirmacao/ConfirmacaoSelect'; import { TipoPessoaSelect } from '@/shared/components/tipoPessoa/tipoPessoaSelect'; -import { useTServicoEtiquetaReadHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaReadHook'; -import { useTServicoEtiquetaSaveHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaSaveHook'; -import { useTServicoEtiquetaRemoveHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_etiqueta/useTServicoEtiquetaRemoveHook'; +import { useEffect } from 'react'; +import { TServicoTipoSaveData } from '../../../../../../packages/administrativo/data/TServicoTipo/TServicoTipoSaveData'; +import { TServicoTipoFormValues, TServicoTipoSchema } from '../../_schemas/TServicoTipoSchema'; // Propriedades esperadas pelo componente interface Props { diff --git a/src/app/(protected)/servicos/balcao/detalhes/[servicoPedidoId]/page.tsx b/src/app/(protected)/servicos/balcao/detalhes/[servicoPedidoId]/page.tsx new file mode 100644 index 0000000..a69a920 --- /dev/null +++ b/src/app/(protected)/servicos/balcao/detalhes/[servicoPedidoId]/page.tsx @@ -0,0 +1,16 @@ +'use client' + +import { useParams } from "next/navigation"; + +import TServicoPedidoDetails from "@/packages/servicos/components/TServicoPedido/TServicoPedidoDetails"; + +export default function TServicoPedidoDetailsPage() { + + const params = useParams(); + + return ( + + ) +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 988ebfb..3b3b53c 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -69,116 +69,166 @@ } :root { - --radius: 0.375rem; - --background: oklch(1 0 0); - --foreground: oklch(0.2686 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.2686 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.2686 0 0); - --primary: oklch(0.7686 0.1647 70.0804); - --primary-foreground: oklch(0 0 0); - --secondary: oklch(0.967 0.0029 264.5419); - --secondary-foreground: oklch(0.4461 0.0263 256.8018); - --muted: oklch(0.9846 0.0017 247.8389); - --muted-foreground: oklch(0.551 0.0234 264.3637); - --accent: oklch(0.9869 0.0214 95.2774); - --accent-foreground: oklch(0.4732 0.1247 46.2007); - --destructive: oklch(0.6368 0.2078 25.3313); - --border: oklch(0.9276 0.0058 264.5313); - --input: oklch(0.9276 0.0058 264.5313); - --ring: oklch(0.7686 0.1647 70.0804); - --chart-1: oklch(0.7686 0.1647 70.0804); - --chart-2: oklch(0.6658 0.1574 58.3183); - --chart-3: oklch(0.5553 0.1455 48.9975); - --chart-4: oklch(0.4732 0.1247 46.2007); - --chart-5: oklch(0.4137 0.1054 45.9038); - --sidebar: oklch(0.9846 0.0017 247.8389); - --sidebar-foreground: oklch(0.2686 0 0); - --sidebar-primary: oklch(0.7686 0.1647 70.0804); - --sidebar-primary-foreground: oklch(1 0 0); - --sidebar-accent: oklch(0.9869 0.0214 95.2774); - --sidebar-accent-foreground: oklch(0.4732 0.1247 46.2007); - --sidebar-border: oklch(0.9276 0.0058 264.5313); - --sidebar-ring: oklch(0.7686 0.1647 70.0804); - --destructive-foreground: oklch(1 0 0); + --background: #f8fafc; + --foreground: #1e293b; + --card: #ffffff; + --card-foreground: #1e293b; + --popover: #ffffff; + --popover-foreground: #1e293b; + --primary: #1a292f; + --primary-foreground: #ffffff; + --secondary: #e5e7eb; + --secondary-foreground: #162227; + --muted: #f3f4f6; + --muted-foreground: #6b7280; + --accent: #e0e7ff; + --accent-foreground: #2b454f; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #d1d5db; + --input: #d1d5db; + --ring: #2b454f; + --chart-1: #f26f28; + --chart-2: #f26418; + --chart-3: #e75a0d; + --chart-4: #c14b0b; + --chart-5: #9a3c09; + --sidebar: #f3f4f6; + --sidebar-foreground: #1e293b; + --sidebar-primary: #2b454f; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #1a292f; + --sidebar-accent-foreground: #374151; + --sidebar-border: #ffffff; + --sidebar-ring: #2b454f; --font-sans: Inter, sans-serif; - --font-serif: Source Serif 4, serif; + --font-serif: Merriweather, serif; --font-mono: JetBrains Mono, monospace; - --shadow-color: hsl(0 0% 0%); - --shadow-opacity: 0.1; + --radius: 0.5rem; + --shadow-x: 0px; + --shadow-y: 4px; --shadow-blur: 8px; --shadow-spread: -1px; - --shadow-offset-x: 0px; - --shadow-offset-y: 4px; - --letter-spacing: 0em; - --spacing: 0.25rem; + --shadow-opacity: 0.1; + --shadow-color: hsl(0 0% 0%); --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1); - --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1); - --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 2px 4px -2px hsl(0 0% 0% / 0.1); - --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 4px 6px -2px hsl(0 0% 0% / 0.1); - --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 8px 10px -2px hsl(0 0% 0% / 0.1); + --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); + --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); + --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10); + --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10); + --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10); --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25); --tracking-normal: 0em; + --spacing: 0.25rem; } .dark { - --background: oklch(0.2046 0 0); - --foreground: oklch(0.9219 0 0); - --card: oklch(0.2686 0 0); - --card-foreground: oklch(0.9219 0 0); - --popover: oklch(0.2686 0 0); - --popover-foreground: oklch(0.9219 0 0); - --primary: oklch(0.7686 0.1647 70.0804); - --primary-foreground: oklch(0 0 0); - --secondary: oklch(0.2686 0 0); - --secondary-foreground: oklch(0.9219 0 0); - --muted: oklch(0.2686 0 0); - --muted-foreground: oklch(0.7155 0 0); - --accent: oklch(0.4732 0.1247 46.2007); - --accent-foreground: oklch(0.9243 0.1151 95.7459); - --destructive: oklch(0.6368 0.2078 25.3313); - --border: oklch(0.3715 0 0); - --input: oklch(0.3715 0 0); - --ring: oklch(0.7686 0.1647 70.0804); - --chart-1: oklch(0.8369 0.1644 84.4286); - --chart-2: oklch(0.6658 0.1574 58.3183); - --chart-3: oklch(0.4732 0.1247 46.2007); - --chart-4: oklch(0.5553 0.1455 48.9975); - --chart-5: oklch(0.4732 0.1247 46.2007); - --sidebar: oklch(0.1684 0 0); - --sidebar-foreground: oklch(0.9219 0 0); - --sidebar-primary: oklch(0.7686 0.1647 70.0804); - --sidebar-primary-foreground: oklch(1 0 0); - --sidebar-accent: oklch(0.4732 0.1247 46.2007); - --sidebar-accent-foreground: oklch(0.9243 0.1151 95.7459); - --sidebar-border: oklch(0.3715 0 0); - --sidebar-ring: oklch(0.7686 0.1647 70.0804); - --destructive-foreground: oklch(1 0 0); - --radius: 0.375rem; + --background: #0f172a; + --foreground: #e2e8f0; + --card: #1e293b; + --card-foreground: #e2e8f0; + --popover: #1e293b; + --popover-foreground: #e2e8f0; + --primary: #818cf8; + --primary-foreground: #0f172a; + --secondary: #2d3748; + --secondary-foreground: #d1d5db; + --muted: #152032; + --muted-foreground: #9ca3af; + --accent: #374151; + --accent-foreground: #d1d5db; + --destructive: #ef4444; + --destructive-foreground: #0f172a; + --border: #4b5563; + --input: #4b5563; + --ring: #818cf8; + --chart-1: #818cf8; + --chart-2: #6366f1; + --chart-3: #4f46e5; + --chart-4: #4338ca; + --chart-5: #3730a3; + --sidebar: #1e293b; + --sidebar-foreground: #e2e8f0; + --sidebar-primary: #818cf8; + --sidebar-primary-foreground: #0f172a; + --sidebar-accent: #374151; + --sidebar-accent-foreground: #d1d5db; + --sidebar-border: #4b5563; + --sidebar-ring: #818cf8; --font-sans: Inter, sans-serif; - --font-serif: Source Serif 4, serif; + --font-serif: Merriweather, serif; --font-mono: JetBrains Mono, monospace; - --shadow-color: hsl(0 0% 0%); - --shadow-opacity: 0.1; + --radius: 0.5rem; + --shadow-x: 0px; + --shadow-y: 4px; --shadow-blur: 8px; --shadow-spread: -1px; - --shadow-offset-x: 0px; - --shadow-offset-y: 4px; - --letter-spacing: 0em; - --spacing: 0.25rem; + --shadow-opacity: 0.1; + --shadow-color: hsl(0 0% 0%); --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1); - --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 1px 2px -2px hsl(0 0% 0% / 0.1); - --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 2px 4px -2px hsl(0 0% 0% / 0.1); - --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 4px 6px -2px hsl(0 0% 0% / 0.1); - --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.1), 0px 8px 10px -2px hsl(0 0% 0% / 0.1); + --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); + --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); + --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10); + --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10); + --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10); --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25); } +@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: var(--font-sans); + --font-mono: var(--font-mono); + --font-serif: var(--font-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); +} + @layer base { * { @apply border-border outline-ring/50; diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx new file mode 100644 index 0000000..9aa3642 --- /dev/null +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -0,0 +1,114 @@ +'use client'; + +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import { useTServicoTipoReadHook } from '@/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook'; +import TServicoTipoSelectInterface from '@/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; + + +export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterface) { + const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const { tServicoTipo = [], fetchTServicoTipo } = useTServicoTipoReadHook(); + + /** + * Efeito para buscar os dados apenas uma vez. + * useCallback evita recriação desnecessária da função. + */ + const loadData = useCallback(async () => { + if (tServicoTipo.length) return; + setIsLoading(true); + await fetchTServicoTipo(); + setIsLoading(false); + }, [tServicoTipo.length, fetchTServicoTipo]); + + useEffect(() => { + loadData(); + }, [loadData]); + + /** + * Memoriza o bairro selecionado para evitar reprocessamentos. + */ + const selected = useMemo( + () => tServicoTipo.find((b) => String(b.servico_tipo_id) === String(field?.value ?? '')), + [tServicoTipo, field?.value], + ); + + /** + * Manipulador de seleção com verificação segura. + */ + const handleSelect = useCallback( + (bairroId: string | number) => { + if (!field?.onChange) return; + field.onChange(bairroId); + setOpen(false); + }, + [field], + ); + + return ( + + + + + + + + + + + + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} + + + {tServicoTipo.map((item) => ( + handleSelect(Number(item.servico_tipo_id))} + > + + {GetCapitalize(item.descricao ?? '')} + + ))} + + + + + + ); +} diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoEditData.ts b/src/packages/administrativo/data/TServicoTipo/TServicoTipoEditData.ts similarity index 89% rename from src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoEditData.ts rename to src/packages/administrativo/data/TServicoTipo/TServicoTipoEditData.ts index ab2d56d..d08b582 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoEditData.ts +++ b/src/packages/administrativo/data/TServicoTipo/TServicoTipoEditData.ts @@ -5,7 +5,7 @@ import API from '@/shared/services/api/Api'; // import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; // // Importa a interface tipada que define a estrutura dos dados do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; // Alterado de GCidadeInterface +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Alterado de GCidadeInterface // Importa função que encapsula chamadas assíncronas e trata erros automaticamente import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoIndexData.ts b/src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts similarity index 100% rename from src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoIndexData.ts rename to src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoRemoveData.ts b/src/packages/administrativo/data/TServicoTipo/TServicoTipoRemoveData.ts similarity index 89% rename from src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoRemoveData.ts rename to src/packages/administrativo/data/TServicoTipo/TServicoTipoRemoveData.ts index aa5c6c3..92778de 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoRemoveData.ts +++ b/src/packages/administrativo/data/TServicoTipo/TServicoTipoRemoveData.ts @@ -5,7 +5,7 @@ import API from '@/shared/services/api/Api'; // import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; // // Importa a interface tipada que define a estrutura dos dados do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; // Alterado de GCidadeInterface +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Alterado de GCidadeInterface // Importa função que encapsula chamadas assíncronas e trata erros automaticamente import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoSaveData.ts b/src/packages/administrativo/data/TServicoTipo/TServicoTipoSaveData.ts similarity index 91% rename from src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoSaveData.ts rename to src/packages/administrativo/data/TServicoTipo/TServicoTipoSaveData.ts index 53f6d83..b00b40a 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/TServicoTipo/TServicoTipoSaveData.ts +++ b/src/packages/administrativo/data/TServicoTipo/TServicoTipoSaveData.ts @@ -2,13 +2,12 @@ import API from '@/shared/services/api/Api'; // // Importa o esquema de validação de dados para tipos de serviço -import { TServicoTipoFormValues } from '../../_schemas/TServicoTipoSchema'; +import { TServicoTipoFormValues } from '../../../../app/(protected)/(cadastros)/cadastros/_schemas/TServicoTipoSchema'; // Importa o enum que contém os métodos HTTP disponíveis (GET, POST, PUT, DELETE) import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; // // Importa a interface tipada que define a estrutura dos dados do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; // Interface alterada // Importa função que encapsula chamadas assíncronas e trata erros automaticamente import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoEditHook.ts b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoEditHook.ts similarity index 81% rename from src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoEditHook.ts rename to src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoEditHook.ts index b0094f8..70deeab 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoEditHook.ts +++ b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoEditHook.ts @@ -1,10 +1,10 @@ import { useResponse } from '@/shared/components/response/ResponseContext'; // Contexto global para gerenciar respostas da API // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Função que Edit o tipo de serviço via API -import { TServicoTipoEditData } from '../../_data/TServicoTipo/TServicoTipoEditData'; +import { TServicoTipoEditData } from '../../data/TServicoTipo/TServicoTipoEditData'; // Hook customizado para remoção de tipos de serviço export const useTServicoTipoEditHook = () => { diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoReadHook.ts b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts similarity index 85% rename from src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoReadHook.ts rename to src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts index b827959..8c3b1f5 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoReadHook.ts +++ b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts @@ -2,10 +2,10 @@ import { useResponse } from '@/shared/components/response/ResponseContext'; // C import { useState } from 'react'; // Serviço que busca a lista de tipos de serviço (TServicoTipoIndexService) -import { TServicoTipoIndexService } from '../../_services/t_servico_tipo/TServicoTipoIndexService'; +import { TServicoTipoIndexService } from '../../services/TServicoTipo/TServicoTipoIndexService'; // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Hook customizado para leitura de dados de tipos de serviço export const useTServicoTipoReadHook = () => { diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoRemoveHook.ts b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoRemoveHook.ts similarity index 81% rename from src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoRemoveHook.ts rename to src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoRemoveHook.ts index e0f8d5d..25fe956 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoRemoveHook.ts +++ b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoRemoveHook.ts @@ -1,10 +1,10 @@ import { useResponse } from '@/shared/components/response/ResponseContext'; // Contexto global para gerenciar respostas da API // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Função que remove o tipo de serviço via API -import { TServicoTipoRemoveData } from '../../_data/TServicoTipo/TServicoTipoRemoveData'; +import { TServicoTipoRemoveData } from '../../data/TServicoTipo/TServicoTipoRemoveData'; // Hook customizado para remoção de tipos de serviço export const useTServicoTipoRemoveHook = () => { diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoSaveHook.ts b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoSaveHook.ts similarity index 82% rename from src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoSaveHook.ts rename to src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoSaveHook.ts index 964d127..0ba369f 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/t_servico_tipo/useTServicoTipoSaveHook.ts +++ b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoSaveHook.ts @@ -1,11 +1,11 @@ -import { useState } from 'react'; import { useResponse } from '@/shared/components/response/ResponseContext'; +import { useState } from 'react'; // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Serviço que salva os dados do tipo de serviço -import { TServicoTipoSaveService } from '../../_services/t_servico_tipo/TServicoTipoSaveService'; +import { TServicoTipoSaveService } from '../../services/TServicoTipo/TServicoTipoSaveService'; export const useTServicoTipoSaveHook = () => { const { setResponse } = useResponse(); diff --git a/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface.ts b/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface.ts new file mode 100644 index 0000000..42d5533 --- /dev/null +++ b/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface.ts @@ -0,0 +1,6 @@ +export default interface TServicoTipoSelectInterface { + field?: { + value?: number | string; + onChange?: (value: string | number) => void; + }; +} diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoEditService.ts b/src/packages/administrativo/services/TServicoTipo/TServicoTipoEditService.ts similarity index 79% rename from src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoEditService.ts rename to src/packages/administrativo/services/TServicoTipo/TServicoTipoEditService.ts index 9e0ab2e..99e6bee 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoEditService.ts +++ b/src/packages/administrativo/services/TServicoTipo/TServicoTipoEditService.ts @@ -1,10 +1,10 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // Função que envolve qualquer ação assíncrona para capturar e tratar erros do cliente -import { TServicoTipoEditData } from '../../_data/TServicoTipo/TServicoTipoEditData'; +import { TServicoTipoEditData } from '../../data/TServicoTipo/TServicoTipoEditData'; // Função que remove os dados do tipo de serviço via API -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Interface tipada do tipo de serviço // Função assíncrona que executa a remoção de um tipo de serviço diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoIndexService.ts b/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts similarity index 90% rename from src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoIndexService.ts rename to src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts index d8654ca..cb4860c 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoIndexService.ts +++ b/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts @@ -1,7 +1,7 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // Função que envolve qualquer ação assíncrona para capturar e tratar erros do cliente -import { TServicoTipoIndexData } from '../../_data/TServicoTipo/TServicoTipoIndexData'; +import { TServicoTipoIndexData } from '../../data/TServicoTipo/TServicoTipoIndexData'; // Função que retorna os dados da lista de tipos de serviço (chamada à API ou mock) // Função assíncrona que executa a chamada para buscar os dados dos tipos de serviço diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoRemoveService.ts b/src/packages/administrativo/services/TServicoTipo/TServicoTipoRemoveService.ts similarity index 79% rename from src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoRemoveService.ts rename to src/packages/administrativo/services/TServicoTipo/TServicoTipoRemoveService.ts index 4376393..0fe26e2 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoRemoveService.ts +++ b/src/packages/administrativo/services/TServicoTipo/TServicoTipoRemoveService.ts @@ -1,10 +1,10 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // Função que envolve qualquer ação assíncrona para capturar e tratar erros do cliente -import { TServicoTipoRemoveData } from '../../_data/TServicoTipo/TServicoTipoRemoveData'; +import { TServicoTipoRemoveData } from '../../data/TServicoTipo/TServicoTipoRemoveData'; // Função que remove os dados do tipo de serviço via API -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Interface tipada do tipo de serviço // Função assíncrona que executa a remoção de um tipo de serviço diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoSaveService.ts b/src/packages/administrativo/services/TServicoTipo/TServicoTipoSaveService.ts similarity index 80% rename from src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoSaveService.ts rename to src/packages/administrativo/services/TServicoTipo/TServicoTipoSaveService.ts index 770d824..966d210 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/t_servico_tipo/TServicoTipoSaveService.ts +++ b/src/packages/administrativo/services/TServicoTipo/TServicoTipoSaveService.ts @@ -2,10 +2,10 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // Função que salva os dados do tipo de serviço via API (ou mock) -import { TServicoTipoSaveData } from '../../_data/TServicoTipo/TServicoTipoSaveData'; +import { TServicoTipoSaveData } from '../../data/TServicoTipo/TServicoTipoSaveData'; // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../_interfaces/TServicoTipoInterface'; +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Função assíncrona que executa o salvamento de um tipo de serviço async function executeTServicoTipoSaveService(data: TServicoTipoInterface) { diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx new file mode 100644 index 0000000..558ef73 --- /dev/null +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -0,0 +1,133 @@ +import { ColumnDef } from '@tanstack/react-table'; +import { Minus, Plus } from 'lucide-react'; + +import { Button } from '@/components/ui/button'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; +import { SortableHeader } from '@/shared/components/dataTable/SortableHeader'; + +/** Permite atualizar a linha externamente, caso o DataTable forneça meta.updateData */ +type TableMeta = { + updateData?: (rowIndex: number, columnId: string, value: unknown) => void; +}; + +export default function TServicoItemPedidoFormColumns( + onEdit: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, + onDelete: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, +): ColumnDef[] { + return [ + // servico + { + accessorKey: 'servico', + header: ({ column }) => SortableHeader('Serviço / Tabela', column), + cell: ({ row }) => { + const data = row.original; + return ( +
+
+
+ {GetCapitalize(data.servico)} +
+
+ {GetCapitalize(data.tabela)} +
+
+
+ ); + }, + sortingFn: (a, b) => + (a.original.servico?.toLowerCase() || '').localeCompare( + b.original.servico?.toLowerCase() || '', + ), + }, + + // emolumento + { + accessorKey: 'emolumento', + header: ({ column }) => SortableHeader('Emolumento', column), + cell: ({ row }) =>
R$ {row.getValue('emolumento') || '---'}
, + }, + + // taxa_judiciaria + { + accessorKey: 'taxa_judiciaria', + header: ({ column }) => SortableHeader('Tx. Judiciária', column), + cell: ({ row }) =>
R$ {row.getValue('taxa_judiciaria') || '---'}
, + }, + + // fundesp + { + accessorKey: 'fundesp', + header: ({ column }) => SortableHeader('Fundesp 21%', column), + cell: ({ row }) =>
R$ {row.getValue('fundesp') || '---'}
, + }, + + // valor_iss + { + accessorKey: 'valor_iss', + header: ({ column }) => SortableHeader('ISS 5%', column), + cell: ({ row }) =>
R$ {row.getValue('valor_iss') || '---'}
, + }, + + // total + { + accessorKey: 'valor', + header: ({ column }) => SortableHeader('Total', column), + cell: ({ row }) =>
R$ {row.getValue('valor') || '---'}
, + }, + + // quantidade (componente solicitado) + { + accessorKey: 'quantidade', + header: ({ column }) => SortableHeader('Qtd.', column), + enableSorting: false, + size: 160, + cell: ({ row, table }) => { + const value = Number(row.getValue('quantidade') ?? 1); + const min = 0; + const max = 999; + + const updateData = (table.options.meta as TableMeta | undefined)?.updateData; + + const setNext = (next: number) => { + if (updateData) updateData(row.index, 'quantidade', next); + }; + + const dec = () => setNext(Math.max(min, value - 1)); + const inc = () => setNext(Math.min(max, value + 1)); + + return ( +
+ + +
+ {value} +
+ + +
+ ); + }, + }, + ]; +} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx new file mode 100644 index 0000000..55d1c74 --- /dev/null +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx @@ -0,0 +1,24 @@ +'use client'; + +import TServicoItemPedidoTableInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoTableInterface'; +import { DataTable } from '@/shared/components/dataTable/DataTable'; + +import TServicoItemPedidoFormColumns from './TServicoItemPedidoFormColumns'; + + +/** + * Componente principal da tabela de Naturezas + */ +export default function TServicoItemPedidoFormTable({ data, onEdit, onDelete }: TServicoItemPedidoTableInterface) { + const columns = TServicoItemPedidoFormColumns(onEdit, onDelete); + return ( +
+ +
+ ); +} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx new file mode 100644 index 0000000..3490cfa --- /dev/null +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import TServicoItemPedidoListInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface'; + +export default function TServicoItemPedidoList({ items }: TServicoItemPedidoListInterface) { + const money = (v: unknown) => + new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(v || 0)); + + return ( + + + Itens: {items?.length} + + + {/* Altura máxima + scroll vertical */} +
+ {items.map((item) => ( +
+ {/* Descrição */} +
+

+ {item.descricao} +

+
+ # {item.servico_itempedido_id} +
+
+ {/* Valores (grid compacto) */} +
+
+
Emolumento
+
{money(item.emolumento)}
+
+ +
+
Tx. Judiciária
+
{money(item.taxa_judiciaria)}
+
+ +
+
ISS
+
{money(item.valor_iss)}
+
+ +
+
Fundesp
+
{money(item.fundesp)}
+
+ +
+
Total
+
{money(item.valor)}
+
+
+
+ ))} +
+
+
+ ); +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx index 8c883bd..1c8e769 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoColumns.tsx @@ -1,5 +1,6 @@ import { ColumnDef } from '@tanstack/react-table'; -import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react'; +import { EllipsisIcon, EyeIcon, PencilIcon, Trash2Icon } from 'lucide-react'; +import Link from 'next/link'; import { Button } from '@/components/ui/button'; import { @@ -103,7 +104,7 @@ export default function TServicoPedidoColumns( id: 'actions', header: 'Ações', cell: ({ row }) => { - const natureza = row.original; + const servicoPedido = row.original; return ( @@ -113,15 +114,19 @@ export default function TServicoPedidoColumns( - onEdit(natureza, true)}> + + + + Detalhes + + + + onEdit(servicoPedido, true)}> Editar - onDelete(natureza, true)} - > + onDelete(servicoPedido, true)}> Remover diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx new file mode 100644 index 0000000..ce3d06b --- /dev/null +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx @@ -0,0 +1,157 @@ +'use client'; + + +import { CalendarIcon, ClockIcon, Pencil } from 'lucide-react'; +import { useCallback, useEffect } from 'react'; + +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Separator } from '@/components/ui/separator'; +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { FormatCPF } from '@/shared/actions/CPF/FormatCPF'; +import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; +import GetNameInitials from '@/shared/actions/text/GetNameInitials'; + +import { useTServicoItemPedidoIndexHook } from '../../hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook'; +import { useTServicoPedidoShowHook } from '../../hooks/TServicoPedido/useTServicoPedidoShowHook'; +import TServicoItemPedidoList from '../TServicoItemPedido/TServicoItemPedidoList'; +import TServicoPedidoDetailsPagamento from './TServicoPedidoDetailsPagamento'; + + + +export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPedidoInterface) { + + const { TServicoItemPedido, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() + const { TServicoPedido, showTServicoPedido } = useTServicoPedidoShowHook() + + const TServicoPedidoShowData = useCallback(async () => { + const servicoPedido: TServicoPedidoInterface = { + servico_pedido_id: servico_pedido_id + } + const response = await showTServicoPedido(servicoPedido) + if (response.servico_pedido_id) { + TServicoPedidoItemIndexData(response.servico_pedido_id) + } + }) + + const TServicoPedidoItemIndexData = useCallback(async (servico_pedido_id: number) => { + const servicoPedido: TServicoPedidoInterface = { + servico_pedido_id: servico_pedido_id + } + await indexTServicoItemPedido(servicoPedido) + }) + + useEffect(() => { + TServicoPedidoShowData() + }, []) + + return ( +
+
+

+ Pedido: #{TServicoPedido?.servico_pedido_id} +

+
+ +
+
+ {/* Main */} +
+
+ {/* Left column */} +
+ + +
+ {/* Right column (sidebar) */} +
+ + + + Apresentante + + + + {/* Header com avatar e link */} +
+ {/* Foto ou Iniciais */} +
+ + {GetNameInitials(TServicoPedido?.apresentante)} + +
+
+
+ {GetCapitalize(TServicoPedido?.apresentante)} +
+
+ {FormatCPF(String(TServicoPedido?.cpfcnpj_apresentante))} +
+
+
+ +
+ + + {FormatDateTime(TServicoPedido?.data_pedido)} + +
+
+ + + 14:50:31 + +
+
+
+ + + + Operador + + + +
+ {/* Foto ou Iniciais */} +
+ + {GetNameInitials(TServicoPedido?.login)} + +
+
+
+ {GetCapitalize(TServicoPedido?.login)} +
+
+ {GetCapitalize(TServicoPedido?.funcao)} +
+
+
+
+
+ + + + Impressões + + + + + + + +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx new file mode 100644 index 0000000..05b8ea2 --- /dev/null +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx @@ -0,0 +1,83 @@ +'use client'; + +import * as React from 'react'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Separator } from '@/components/ui/separator'; +import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; + +import TServicoPedidoDetailsPagamentoInterface from '../../interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface'; + +export default function TServicoPedidoDetailsPagamento({ + situacao, + items, +}: TServicoPedidoDetailsPagamentoInterface) { + + // Formatação monetária (BRL) + const fmt = React.useMemo( + () => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }), + [] + ); + + // Helper para evitar NaN/undefined + const safe = (n: number | undefined | null) => + typeof n === 'number' && Number.isFinite(n) ? n : 0; + + // Somas por tipo de valor + const { emolumento, taxa_judiciaria, valor_iss, fundesp } = React.useMemo(() => { + return (items ?? []).reduce( + (acc, item) => { + acc.emolumento += safe(item.emolumento); + acc.taxa_judiciaria += safe(item.taxa_judiciaria); + acc.valor_iss += safe(item.valor_iss); + acc.fundesp += safe(item.fundesp); + return acc; + }, + { emolumento: 0, taxa_judiciaria: 0, valor_iss: 0, fundesp: 0 } + ); + }, [items]); + + // Total exibido = soma dos quatro componentes + const total = emolumento + taxa_judiciaria + valor_iss + fundesp; + + return ( + + + + Pagamento + + + + +
+
+ Emolumento + {fmt.format(emolumento)} +
+ +
+ Tx. Judiciária + {fmt.format(taxa_judiciaria)} +
+ +
+ ISS + {fmt.format(valor_iss)} +
+ +
+ Fundesp + {fmt.format(fundesp)} +
+ + + +
+ Total + {fmt.format(total)} +
+
+
+
+ ); +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx deleted file mode 100644 index ea3e993..0000000 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm copy.tsx +++ /dev/null @@ -1,110 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; - -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; -import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; - -import { useTServicoPedidoFormHook } from '../../hooks/TServicoPedido/useTServicoPedidoFormHook'; -import { TServicoPedidoFormInterface } from '../../interfaces/TServicoPedido/TServicoPedidoFormInterface'; - -/** - * Formulário de cadastro/edição de Natureza - * Baseado nos campos da tabela G_NATUREZA - */ -export default function TServicoPedidoForm({ - isOpen, - data, - onClose, - onSave, - buttonIsLoading, -}: TServicoPedidoFormInterface) { - const form = useTServicoPedidoFormHook({}); - - // Atualiza o formulário quando recebe dados para edição - useEffect(() => { - ResetFormIfData(form, data); - }, [data, form]); - - function onError(error: any) { - console.log('Erro no formulário:', error); - } - - return ( - { - if (!open) onClose(null, false); - }} - > - - - Formulário de Gramática - - Formulário de Gramática - - - {/* Formulário principal */} -
- - {/* GRID MOBILE FIRST */} -
- {/* Palavra */} -
- ( - - Palavra - - - - - - )} - /> -
-
- {/* Rodapé */} - - - - - - -
- -
-
- ); -} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 80ce376..a01bd7e 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -3,6 +3,7 @@ import { CreditCard, Package, UserSquare2 } from 'lucide-react'; import * as React from 'react'; +import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Form, @@ -15,8 +16,12 @@ import { import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; +import TServicoTipoSelect from '@/packages/administrativo/components/TServicoTipo/TServicoTipoSelect'; import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; +import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; + +import TServicoItemPedidoFormTable from '../TServicoItemPedido/TServicoItemPedidoFormTable'; type StepKey = 'pedido' | 'servicoPedidoItem' | 'payment'; @@ -96,186 +101,420 @@ export default function TServicoPedidoForm() { ResetFormIfData(form, data); }, [data, form]); + const dataItens = [ + { + 'emolumento': 4.99, + 'fundesp': 1.21, + 'taxa_judiciaria': 0, + 'valor_iss': 0.25, + 'valor': 6.45, + 'servico': 'Autenticação Original', + 'tabela': 'Autenticação por página' + }, + { + 'emolumento': 3.50, + 'fundesp': 0.85, + 'taxa_judiciaria': 0, + 'valor_iss': 0.18, + 'valor': 4.53, + 'servico': 'Autenticação de Cópia', + 'tabela': 'Autenticação por página' + }, + { + 'emolumento': 5.90, + 'fundesp': 1.20, + 'taxa_judiciaria': 0, + 'valor_iss': 0.30, + 'valor': 7.40, + 'servico': 'Reconhecimento de Firma (Semelhança)', + 'tabela': 'Reconhecimento de firma' + }, + { + 'emolumento': 8.50, + 'fundesp': 1.75, + 'taxa_judiciaria': 0, + 'valor_iss': 0.43, + 'valor': 10.68, + 'servico': 'Reconhecimento de Firma (Autenticidade)', + 'tabela': 'Reconhecimento de firma' + }, + { + 'emolumento': 12.00, + 'fundesp': 2.50, + 'taxa_judiciaria': 0, + 'valor_iss': 0.60, + 'valor': 15.10, + 'servico': 'Abertura de Firma', + 'tabela': 'Ficha de assinatura' + }, + { + 'emolumento': 18.00, + 'fundesp': 3.60, + 'taxa_judiciaria': 0, + 'valor_iss': 0.90, + 'valor': 22.50, + 'servico': 'Procuração Particular', + 'tabela': 'Procuração por folha' + }, + { + 'emolumento': 35.00, + 'fundesp': 7.00, + 'taxa_judiciaria': 0, + 'valor_iss': 1.75, + 'valor': 43.75, + 'servico': 'Procuração Pública', + 'tabela': 'Procuração (tabela geral)' + }, + { + 'emolumento': 20.00, + 'fundesp': 4.00, + 'taxa_judiciaria': 0, + 'valor_iss': 1.00, + 'valor': 25.00, + 'servico': 'Certidão Simples', + 'tabela': 'Certidões' + }, + { + 'emolumento': 45.00, + 'fundesp': 9.00, + 'taxa_judiciaria': 0, + 'valor_iss': 2.25, + 'valor': 56.25, + 'servico': 'Certidão em Inteiro Teor', + 'tabela': 'Certidões' + }, + { + 'emolumento': 80.00, + 'fundesp': 16.00, + 'taxa_judiciaria': 0, + 'valor_iss': 4.00, + 'valor': 100.00, + 'servico': 'Ata Notarial', + 'tabela': 'Ata por página' + }, + { + 'emolumento': 7.50, + 'fundesp': 1.50, + 'taxa_judiciaria': 0, + 'valor_iss': 0.38, + 'valor': 9.38, + 'servico': 'Testemunha em Documento', + 'tabela': 'Outras declarações' + }, + { + 'emolumento': 6.20, + 'fundesp': 1.24, + 'taxa_judiciaria': 0, + 'valor_iss': 0.31, + 'valor': 7.75, + 'servico': 'Autenticação de Documento Eletrônico', + 'tabela': 'Autenticação digital' + }, + { + 'emolumento': 25.00, + 'fundesp': 5.00, + 'taxa_judiciaria': 0, + 'valor_iss': 1.25, + 'valor': 31.25, + 'servico': 'Reconhecimento de Sinal Público', + 'tabela': 'Sinal público' + }, + { + 'emolumento': 10.00, + 'fundesp': 2.00, + 'taxa_judiciaria': 0, + 'valor_iss': 0.50, + 'valor': 12.50, + 'servico': 'Certificação de Cópia Digital', + 'tabela': 'Certificação digital' + }, + { + 'emolumento': 9.90, + 'fundesp': 1.98, + 'taxa_judiciaria': 0, + 'valor_iss': 0.50, + 'valor': 12.38, + 'servico': 'Arquivamento de Documento', + 'tabela': 'Arquivamento' + } + ]; + + return ( -
- -
- {/* Sidebar - Sticky */} - - {/* Conteúdo */} -
-
- {/* Seção: Pedido */} - - - Pedido - - -
-
- ( - - Escrevente/Tabelião - - - - )} +
+

+ Pedido +

+ + +
+ {/* Sidebar - Sticky */} + + {/* Conteúdo */} +
+
+ {/* Seção: Pedido */} + + + +

+ Pedido +

+
+
+ +
+
+ ( + + Escrevente/Tabelião + + + + )} + /> +
+
+ ( + + Apresentante + + + + + + )} + /> +
+
+ ( + + CPF + + + + + + )} + /> +
+
+ ( + + Pessoa presente no selo + + + + + + )} + /> +
+
+ ( + + CPF + + + + + + )} + /> +
-
- ( - - Pessoa presente no selo - - - - - - )} - /> + + + {/* Seção: Itens */} + + + +

+ Itens +

+
+
+ +
+
+ ( + + Serviços + + + + )} + /> +
+
+ ( + + Emolumentos + + + + )} + /> +
+
+ { }} + onDelete={() => { }} + /> +
-
- ( - - CPF - - - - - - )} - /> + + + {/* Seção: Pagamento */} + + + +

+ Pagamento +

+
+
+ +
+
+ ( + + Requerente + + + + + + )} + /> +
+
+ ( + + CPF/CNPJ Requerente + + + + + + )} + /> +
+
+ ( + + Forma Pagamento + + + + )} + /> +
+
+ + +
-
-
-
- - {/* Seção: Itens */} - - - Itens - - -
-
- ( - - Escrevente/Tabelião - - - - )} - /> -
-
-
-
- - {/* Seção: Pagamento */} - - - Pagamento - - ... - -
-
-
- - + + +
+
+
+ + +
); } diff --git a/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoIndexData.ts b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoIndexData.ts index db1e311..26ee8b0 100644 --- a/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoIndexData.ts +++ b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoIndexData.ts @@ -1,15 +1,16 @@ +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; import API from '@/shared/services/api/Api'; import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; -async function executeTServicoItemPedidoIndexData(): Promise { - const api = new API(); +async function executeTServicoItemPedidoIndexData(data: TServicoItemPedidoInterface): Promise { + const api = new API(); return api.send({ method: Methods.GET, - endpoint: `servicos/t_servico_itempedido/`, + endpoint: `servicos/balcao/t_servico_itempedido/pedido/${data.servico_pedido_id}`, }); } -export const TServicoItemPedidoIndexData = withClientErrorHandler(executeTServicoItemPedidoIndexData); +export const TServicoItemPedidoIndexData = withClientErrorHandler(executeTServicoItemPedidoIndexData); \ No newline at end of file diff --git a/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoShowData.ts b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoShowData.ts new file mode 100644 index 0000000..1282797 --- /dev/null +++ b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoShowData.ts @@ -0,0 +1,16 @@ +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; +import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; +import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; + + +async function executeTServicoItemPedidoShowData(data: TServicoItemPedidoInterface): Promise { + const api = new API(); + return api.send({ + method: Methods.GET, + endpoint: `servicos/balcao/t_servico_itempedido/pedido/${data.servico_pedido_id}`, + }); +} + +export const TServicoItemPedidoShowData = withClientErrorHandler(executeTServicoItemPedidoShowData); diff --git a/src/packages/servicos/data/TServicoPedido/TServicoPedidoShowData.ts b/src/packages/servicos/data/TServicoPedido/TServicoPedidoShowData.ts new file mode 100644 index 0000000..cd2e8d7 --- /dev/null +++ b/src/packages/servicos/data/TServicoPedido/TServicoPedidoShowData.ts @@ -0,0 +1,16 @@ +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; +import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; +import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; + + +async function executeTServicoPedidoShowData(data: TServicoPedidoInterface): Promise { + const api = new API(); + return api.send({ + method: Methods.GET, + endpoint: `servicos/balcao/t_servico_pedido/${data.servico_pedido_id}`, + }); +} + +export const TServicoPedidoShowData = withClientErrorHandler(executeTServicoPedidoShowData); diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts index 002c56d..c213bec 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts @@ -2,23 +2,29 @@ import { useState } from 'react'; +import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { TServicoItemPedidoIndexService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService'; import { useResponse } from '@/shared/components/response/ResponseContext'; -import TServicoItemPedidoInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; -import { TServicoItemPedidoIndexService } from '../../services/TServicoItemPedido/TServicoItemPedidoIndexService'; - export const useTServicoItemPedidoIndexHook = () => { + const { setResponse } = useResponse(); - const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + + const indexTServicoItemPedido = async (data: TServicoItemPedidoInterface) => { + + const response = await TServicoItemPedidoIndexService(data); - const indexTServicoItemPedido = async () => { - const response = await TServicoItemPedidoIndexService(); - // Armazena os dados consultados setTServicoItemPedido(response.data); - // Define a resposta (toast, modal, feedback, etc.) + setResponse(response); + + // Retorno imediato dos valores + return response + }; return { diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoShowHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoShowHook.ts new file mode 100644 index 0000000..d065be8 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoShowHook.ts @@ -0,0 +1,28 @@ +import { useState } from 'react'; + +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { TServicoItemPedidoShowService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoShowService'; +import { useResponse } from '@/shared/components/response/ResponseContext'; + + + +export const useTServicoItemPedidoShowHook = () => { + + const { setResponse } = useResponse(); + + const [TServicoItemPedido, setTServicoItemPedido] = useState(); + + const showTServicoItemPedido = async (data: TServicoItemPedidoInterface) => { + + const response = await TServicoItemPedidoShowService(data); + + setTServicoItemPedido(response.data); + + setResponse(response); + + return response.data + + }; + + return { TServicoItemPedido, showTServicoItemPedido }; +}; diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook.ts new file mode 100644 index 0000000..f5c3613 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook.ts @@ -0,0 +1,29 @@ +import { useState } from 'react'; + + +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { TServicoPedidoShowService } from '@/packages/servicos/services/TServicoPedido/TServicoPedidoShowService'; +import { useResponse } from '@/shared/components/response/ResponseContext'; + + +export const useTServicoPedidoShowHook = () => { + + const { setResponse } = useResponse(); + + const [TServicoPedido, setTServicoPedido] = useState(null); + + const showTServicoPedido = async (data: TServicoPedidoInterface) => { + + const response = await TServicoPedidoShowService(data); + + setTServicoPedido(response.data); + + setResponse(response); + + return response.data + + }; + + return { TServicoPedido, showTServicoPedido }; + +}; diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts new file mode 100644 index 0000000..1ef8d72 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts @@ -0,0 +1,11 @@ +export default interface TServicoItemPedidoIndexResponseInterface { + servico_itempedido_id: number, + emolumento: number, + taxa_judiciaria: number, + valor_iss: number, + fundesp: number, + valor: number, + descricao: string, + emolumento_descricao: string, + situacao: string +} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts new file mode 100644 index 0000000..cc28f01 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts @@ -0,0 +1,5 @@ +import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoIndexResponseInterface"; + +export default interface TServicoItemPedidoListInterface { + items: TServicoItemPedidoIndexResponseInterface[]; +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsInterface.ts new file mode 100644 index 0000000..f9753b2 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsInterface.ts @@ -0,0 +1,3 @@ +export default interface TServicoPedidoDetailsInterface { + servico_pedido_id: number +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts new file mode 100644 index 0000000..19bcde6 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts @@ -0,0 +1,8 @@ +import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoIndexResponseInterface"; + +export default interface TServicoPedidoDetailsPagamentoInterface { + + situacao: string, + items: TServicoItemPedidoIndexResponseInterface[] + +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index b0221f0..1ec907b 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -14,4 +14,6 @@ export default interface TServicoPedidoInterface { cpfcnpj_apresentante?: string; selo_pessoa_nome?: string; selo_pessoa_cpfcnpj?: string; + login?: string; + funcao?: string; } \ No newline at end of file diff --git a/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService.ts b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService.ts index 37403fd..9c1c69d 100644 --- a/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService.ts +++ b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService.ts @@ -1,9 +1,10 @@ + +import { TServicoItemPedidoIndexData } from '@/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoIndexData'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; -import { TServicoItemPedidoIndexData } from '../../data/TServicoItemPedido/TServicoItemPedidoIndexData'; - -export default async function executeTServicoItemPedidoIndexService() { - const response = await TServicoItemPedidoIndexData(); +export default async function executeTServicoItemPedidoIndexService(data: TServicoItemPedidoInterface) { + const response = await TServicoItemPedidoIndexData(data); return response; } diff --git a/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoShowService.ts b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoShowService.ts new file mode 100644 index 0000000..878a690 --- /dev/null +++ b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoShowService.ts @@ -0,0 +1,17 @@ + +import { TServicoItemPedidoShowData } from '@/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoShowData'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + + + + +async function executeTServicoItemPedidoShowService(data: TServicoItemPedidoInterface) { + + const response = await TServicoItemPedidoShowData(data); + + return response; + +} + +export const TServicoItemPedidoShowService = withClientErrorHandler(executeTServicoItemPedidoShowService); diff --git a/src/packages/servicos/services/TServicoPedido/TServicoPedidoShowService.ts b/src/packages/servicos/services/TServicoPedido/TServicoPedidoShowService.ts new file mode 100644 index 0000000..df4d660 --- /dev/null +++ b/src/packages/servicos/services/TServicoPedido/TServicoPedidoShowService.ts @@ -0,0 +1,12 @@ + + +import { TServicoPedidoShowData } from '@/packages/servicos/data/TServicoPedido/TServicoPedidoShowData'; +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +async function executeTServicoPedidoShowService(data: TServicoPedidoInterface) { + const response = await TServicoPedidoShowData(data); + return response; +} + +export const TServicoPedidoShowService = withClientErrorHandler(executeTServicoPedidoShowService); diff --git a/src/shared/components/tipoPagamento/TipoPagamentoSelect.tsx b/src/shared/components/tipoPagamento/TipoPagamentoSelect.tsx new file mode 100644 index 0000000..a5757f4 --- /dev/null +++ b/src/shared/components/tipoPagamento/TipoPagamentoSelect.tsx @@ -0,0 +1,85 @@ +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import React from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import { TipoPagamentoEnum } from '@/shared/enums/TipoPagamentoEnum'; + +type TipoPagamentoSelectProps = { + field: { + value?: number | null; + onChange: (value: number) => void; + }; +}; + +export default function TipoPagamentoSelect({ field }: TipoPagamentoSelectProps) { + const [open, setOpen] = React.useState(false); + + // Cria as opções a partir do enum + const options = Object.entries(TipoPagamentoEnum).map(([key, label]) => ({ + value: Number(key), + label, + })); + + // Label exibida atualmente + const selectedLabel = + field.value !== undefined && field.value !== null + ? options.find((item) => item.value === Number(field.value))?.label + : 'Selecione...'; + + return ( + + + + + + + + + + + Nenhum resultado encontrado. + + {options.map((item) => ( + { + field.onChange(item.value); // envia número + setOpen(false); + }} + > + + {item.label} + + ))} + + + + + + ); +} diff --git a/src/shared/enums/TipoPagamentoEnum.ts b/src/shared/enums/TipoPagamentoEnum.ts new file mode 100644 index 0000000..a6f3a8e --- /dev/null +++ b/src/shared/enums/TipoPagamentoEnum.ts @@ -0,0 +1,6 @@ +export const TipoPagamentoEnum = { + 1: 'Dinheiro', + 2: 'Cartão de Crédito', + 3: 'Cartão de Débito', + 4: 'Pix', +} as const; diff --git a/src/shared/hooks/useRunOnceByKey.ts b/src/shared/hooks/useRunOnceByKey.ts new file mode 100644 index 0000000..d70665f --- /dev/null +++ b/src/shared/hooks/useRunOnceByKey.ts @@ -0,0 +1,18 @@ +import { useCallback, useRef } from "react"; + +export default function useRunOnceByKey() { + const done = useRef(new Set()); + + const run = useCallback(async (key: string, tasks: Array<() => Promise>, opts?: { signal?: AbortSignal }) => { + if (done.current.has(key)) return; + done.current.add(key); + + await Promise.allSettled(tasks.map(fn => fn())); + }, []); + + const resetKey = useCallback((key: string) => { + done.current.delete(key); + }, []); + + return { run, resetKey }; +} \ No newline at end of file From 06d55ec1257a0db0fc8fe302fffb0ee1a3c09ee9 Mon Sep 17 00:00:00 2001 From: Keven Date: Thu, 6 Nov 2025 17:32:40 -0300 Subject: [PATCH 03/14] =?UTF-8?q?[MVPTN-37]=20feat(Pedido):=20Ajusta=20o?= =?UTF-8?q?=20foruml=C3=A1rio=20para=20utilizar=20o=20endpoint=20para=20sa?= =?UTF-8?q?lvar=20o=20pedido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 29 +- .../components/GGramatica/GGramaticaForm.tsx | 2 +- .../components/GGramatica/GGramaticaIndex.tsx | 2 +- .../components/GGramatica/GGramaticaTable.tsx | 2 +- .../TServicoItemPedidoList.tsx | 2 +- .../TServicoPedido/TServicoPedidoDetails.tsx | 8 +- .../TServicoPedido/TServicoPedidoForm.tsx | 444 ++++-------------- .../useTServicoPedidoFormHook.ts | 13 + .../interfaces/TServico/TServicoInterface.ts | 5 + .../TServicoPedido/TServicoPedidoInterface.ts | 3 +- .../TServicoPedido/TServicoPedidoSchema.ts | 6 +- .../components/response/ResponseContext.tsx | 2 +- src/shared/components/step/stepNavigator.tsx | 256 ++++++++++ 13 files changed, 419 insertions(+), 355 deletions(-) create mode 100644 src/packages/servicos/interfaces/TServico/TServicoInterface.ts create mode 100644 src/shared/components/step/stepNavigator.tsx diff --git a/package-lock.json b/package-lock.json index d61e6db..4d02c62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -124,6 +124,7 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2972,6 +2973,7 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2982,6 +2984,7 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2998,6 +3001,7 @@ "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.1", @@ -3028,6 +3032,7 @@ "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/types": "8.46.1", @@ -3502,6 +3507,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3854,6 +3860,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -4658,6 +4665,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4718,6 +4726,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4844,6 +4853,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6998,6 +7008,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.5.5.tgz", "integrity": "sha512-OQVdBPtpBfq7HxFN0kOVb7rXXOSIkt5lTzDJDGRBcOyVvNRIWFauMqi1gIHd1pszq1542vMOGY0HP4CaiALfkA==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "15.5.5", "@swc/helpers": "0.5.15", @@ -7394,6 +7405,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7551,6 +7563,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7560,6 +7573,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -7572,6 +7586,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.65.0.tgz", "integrity": "sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -7587,7 +7602,8 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-masked-text": { "version": "1.0.5", @@ -7600,6 +7616,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -7718,7 +7735,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -8478,6 +8496,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8489,7 +8508,8 @@ "version": "8.1.2", "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-8.1.2.tgz", "integrity": "sha512-KITxHEEHRlxC5xOnxA123eAJ67NgsWxNphtItWt9TRu07DiTZrWIqJeIKRX9euE51/l3kJO4WQiqoBXKTJJGsA==", - "license": "GPL-2.0-or-later" + "license": "GPL-2.0-or-later", + "peer": true }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -8643,6 +8663,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8708,6 +8729,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -8994,6 +9016,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/packages/administrativo/components/GGramatica/GGramaticaForm.tsx b/src/packages/administrativo/components/GGramatica/GGramaticaForm.tsx index 25bfa17..b9710c3 100644 --- a/src/packages/administrativo/components/GGramatica/GGramaticaForm.tsx +++ b/src/packages/administrativo/components/GGramatica/GGramaticaForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { diff --git a/src/packages/administrativo/components/GGramatica/GGramaticaIndex.tsx b/src/packages/administrativo/components/GGramatica/GGramaticaIndex.tsx index be6e005..e4be82b 100644 --- a/src/packages/administrativo/components/GGramatica/GGramaticaIndex.tsx +++ b/src/packages/administrativo/components/GGramatica/GGramaticaIndex.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useGGramaticaDeleteHook } from '@/packages/administrativo/hooks/GGramatica/useGGramaticaDeleteHook'; diff --git a/src/packages/administrativo/components/GGramatica/GGramaticaTable.tsx b/src/packages/administrativo/components/GGramatica/GGramaticaTable.tsx index 648ccaf..58295e9 100644 --- a/src/packages/administrativo/components/GGramatica/GGramaticaTable.tsx +++ b/src/packages/administrativo/components/GGramatica/GGramaticaTable.tsx @@ -2,8 +2,8 @@ import { DataTable } from '@/shared/components/dataTable/DataTable'; -import GGramaticaColumns from './GGramaticaColumns'; import GGramaticaTableInterface from '../../interfaces/GGramatica/GGramaticaTableInterface'; +import GGramaticaColumns from './GGramaticaColumns'; /** * Componente principal da tabela de Naturezas diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx index 3490cfa..3bd40f8 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx @@ -15,7 +15,7 @@ export default function TServicoItemPedidoList({ items }: TServicoItemPedidoList {/* Altura máxima + scroll vertical */}
- {items.map((item) => ( + {items?.map((item) => (
('pedido'); + const sections: StepSection[] = [ + { key: 'pedido', id: 'selectPedido', icon: , title: 'Pedido', description: 'Dados gerais do pedido.' }, + { key: 'servicoPedidoItem', id: 'selectServicoPedidoItem', icon: , title: 'Itens', description: 'Itens/serviços do pedido.' }, + { key: 'payment', id: 'selectPayment', icon: , title: 'Pagamento', description: 'Forma e dados de pagamento.' }, + ]; + + const ref = React.useRef(null); // submit e error do RHF - function onSubmit(values: any) { - console.log('Submit:', values); - } + const onSave = useCallback( + + async (formData: TServicoPedidoInterface) => { + + console.log(formData); + + // Coloca o botão em estado de loading + setButtonIsLoading(true); + + // Aguarda salvar o registro + const response = await saveTServicoPedido(formData); + + // Verifica se pode redirecionar o usuário + if (response.servico_pedido_id > 0) { + + // Redirecionamento de página + router.replace('/servicos/balcao/detalhes/' + response.servico_pedido_id) + } + + // Remove o botão em estado de loading + setButtonIsLoading(false); + + }, + [saveTServicoPedido, TServicoPedido], + ); + function onError(error: any) { console.log('Erro no formulário:', error); } - // === Bloqueio do scroll-spy durante rolagem programática === - const spyLockedRef = React.useRef(false); - const spyTimerRef = React.useRef(null); - - const lockSpy = React.useCallback(() => { - spyLockedRef.current = true; - if (spyTimerRef.current) window.clearTimeout(spyTimerRef.current); - spyTimerRef.current = window.setTimeout(() => { - spyLockedRef.current = false; - }, SPY_LOCK_MS) as unknown as number; - }, []); - - const scrollToSection = React.useCallback( - (id: string) => { - const el = document.getElementById(id); - if (!el) return; - lockSpy(); - const y = el.getBoundingClientRect().top + window.scrollY - SCROLL_OFFSET; - window.scrollTo({ top: y, behavior: 'smooth' }); - }, - [lockSpy] - ); - - // Scroll spy - React.useEffect(() => { - const handler = () => { - if (spyLockedRef.current) return; - - let current: StepKey = 'pedido'; - let best = Number.POSITIVE_INFINITY; - - for (const { key, id } of SECTION_ORDER) { - const el = document.getElementById(id); - if (!el) continue; - const dist = Math.abs(el.getBoundingClientRect().top - SCROLL_OFFSET - 8); - if (dist < best) { - best = dist; - current = key; - } - } - if (current !== active) setActive(current); - }; - - handler(); - window.addEventListener('scroll', handler, { passive: true }); - return () => window.removeEventListener('scroll', handler); - }, [active]); - - // Atualiza o formulário quando recebe dados para edição - React.useEffect(() => { - ResetFormIfData(form, data); - }, [data, form]); - - const dataItens = [ - { - 'emolumento': 4.99, - 'fundesp': 1.21, - 'taxa_judiciaria': 0, - 'valor_iss': 0.25, - 'valor': 6.45, - 'servico': 'Autenticação Original', - 'tabela': 'Autenticação por página' - }, - { - 'emolumento': 3.50, - 'fundesp': 0.85, - 'taxa_judiciaria': 0, - 'valor_iss': 0.18, - 'valor': 4.53, - 'servico': 'Autenticação de Cópia', - 'tabela': 'Autenticação por página' - }, - { - 'emolumento': 5.90, - 'fundesp': 1.20, - 'taxa_judiciaria': 0, - 'valor_iss': 0.30, - 'valor': 7.40, - 'servico': 'Reconhecimento de Firma (Semelhança)', - 'tabela': 'Reconhecimento de firma' - }, - { - 'emolumento': 8.50, - 'fundesp': 1.75, - 'taxa_judiciaria': 0, - 'valor_iss': 0.43, - 'valor': 10.68, - 'servico': 'Reconhecimento de Firma (Autenticidade)', - 'tabela': 'Reconhecimento de firma' - }, - { - 'emolumento': 12.00, - 'fundesp': 2.50, - 'taxa_judiciaria': 0, - 'valor_iss': 0.60, - 'valor': 15.10, - 'servico': 'Abertura de Firma', - 'tabela': 'Ficha de assinatura' - }, - { - 'emolumento': 18.00, - 'fundesp': 3.60, - 'taxa_judiciaria': 0, - 'valor_iss': 0.90, - 'valor': 22.50, - 'servico': 'Procuração Particular', - 'tabela': 'Procuração por folha' - }, - { - 'emolumento': 35.00, - 'fundesp': 7.00, - 'taxa_judiciaria': 0, - 'valor_iss': 1.75, - 'valor': 43.75, - 'servico': 'Procuração Pública', - 'tabela': 'Procuração (tabela geral)' - }, - { - 'emolumento': 20.00, - 'fundesp': 4.00, - 'taxa_judiciaria': 0, - 'valor_iss': 1.00, - 'valor': 25.00, - 'servico': 'Certidão Simples', - 'tabela': 'Certidões' - }, - { - 'emolumento': 45.00, - 'fundesp': 9.00, - 'taxa_judiciaria': 0, - 'valor_iss': 2.25, - 'valor': 56.25, - 'servico': 'Certidão em Inteiro Teor', - 'tabela': 'Certidões' - }, - { - 'emolumento': 80.00, - 'fundesp': 16.00, - 'taxa_judiciaria': 0, - 'valor_iss': 4.00, - 'valor': 100.00, - 'servico': 'Ata Notarial', - 'tabela': 'Ata por página' - }, - { - 'emolumento': 7.50, - 'fundesp': 1.50, - 'taxa_judiciaria': 0, - 'valor_iss': 0.38, - 'valor': 9.38, - 'servico': 'Testemunha em Documento', - 'tabela': 'Outras declarações' - }, - { - 'emolumento': 6.20, - 'fundesp': 1.24, - 'taxa_judiciaria': 0, - 'valor_iss': 0.31, - 'valor': 7.75, - 'servico': 'Autenticação de Documento Eletrônico', - 'tabela': 'Autenticação digital' - }, - { - 'emolumento': 25.00, - 'fundesp': 5.00, - 'taxa_judiciaria': 0, - 'valor_iss': 1.25, - 'valor': 31.25, - 'servico': 'Reconhecimento de Sinal Público', - 'tabela': 'Sinal público' - }, - { - 'emolumento': 10.00, - 'fundesp': 2.00, - 'taxa_judiciaria': 0, - 'valor_iss': 0.50, - 'valor': 12.50, - 'servico': 'Certificação de Cópia Digital', - 'tabela': 'Certificação digital' - }, - { - 'emolumento': 9.90, - 'fundesp': 1.98, - 'taxa_judiciaria': 0, - 'valor_iss': 0.50, - 'valor': 12.38, - 'servico': 'Arquivamento de Documento', - 'tabela': 'Arquivamento' - } - ]; - - return (

Pedido

- +
{/* Sidebar - Sticky */}
@@ -516,48 +324,4 @@ export default function TServicoPedidoForm() {
); -} - -/* ---------- Components auxiliares ---------- */ - -function StepLink({ - active, - onClick, - icon, - title, - description, -}: { - active?: boolean; - onClick?: () => void; - icon: React.ReactNode; - title: string; - description: string; -}) { - return ( - - ); -} +} \ No newline at end of file diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index 697c882..fc60cba 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -8,6 +8,19 @@ export function useTServicoPedidoFormHook(defaults?: Partial; \ No newline at end of file diff --git a/src/shared/components/response/ResponseContext.tsx b/src/shared/components/response/ResponseContext.tsx index e11a004..81d404d 100644 --- a/src/shared/components/response/ResponseContext.tsx +++ b/src/shared/components/response/ResponseContext.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { createContext, useContext, useState, ReactNode } from 'react'; +import React, { createContext, ReactNode, useContext, useState } from 'react'; interface ResponseState { message?: string; diff --git a/src/shared/components/step/stepNavigator.tsx b/src/shared/components/step/stepNavigator.tsx new file mode 100644 index 0000000..a6f55d1 --- /dev/null +++ b/src/shared/components/step/stepNavigator.tsx @@ -0,0 +1,256 @@ +'use client'; + +import * as React from 'react'; + +import { cn } from '@/lib/utils'; // ajuste o caminho conforme seu projeto + +/** ========================= + * Constantes (com defaults) + * ========================= */ +const DEFAULT_SCROLL_OFFSET = 16; +const DEFAULT_SPY_LOCK_MS = 600; + +/** =========== + * Tipos + * =========== */ +export type StepKey = string; + +export type StepSection = { + /** chave lógica do step (ex.: 'pedido') */ + key: StepKey; + /** id do elemento alvo no DOM (ex.: 'selectPedido') */ + id: string; + /** ícone (lucide ou outro) */ + icon: React.ReactNode; + /** título do step */ + title: string; + /** descrição curta do step */ + description?: string; +}; + +export type StepNavigatorProps = { + /** Lista de seções em ordem */ + sections: StepSection[]; + /** Ativo controlado externamente */ + active?: StepKey; + /** Ativo padrão (modo não-controlado) */ + defaultActive?: StepKey; + /** Callback ao mudar de seção (por scroll ou clique) */ + onChange?: (key: StepKey) => void; + + /** Offset do topo ao calcular posicionamento (ex.: header fixo) */ + scrollOffset?: number; + /** Tempo de bloqueio do spy após scroll programático */ + spyLockMs?: number; + + /** Classe extra no container */ + className?: string; + /** Se `false`, desabilita o scroll-spy */ + enableScrollSpy?: boolean; +}; + +/** ================= + * Hook: useScrollSpy + * ================= */ +function useScrollSpy(opts: { + sections: StepSection[]; + active: StepKey | undefined; + setActive: (key: StepKey) => void; + scrollOffset: number; + enable: boolean; + lockMs: number; +}) { + const { sections, active, setActive, scrollOffset, enable, lockMs } = opts; + + // bloqueio do spy durante rolagem programática + const spyLockedRef = React.useRef(false); + const spyTimerRef = React.useRef(null); + + const lockSpy = React.useCallback(() => { + spyLockedRef.current = true; + if (spyTimerRef.current) window.clearTimeout(spyTimerRef.current); + spyTimerRef.current = window.setTimeout(() => { + spyLockedRef.current = false; + }, lockMs) as unknown as number; + }, [lockMs]); + + const scrollToId = React.useCallback( + (id: string) => { + const el = document.getElementById(id); + if (!el) return; + lockSpy(); + const y = el.getBoundingClientRect().top + window.scrollY - scrollOffset; + window.scrollTo({ top: y, behavior: 'smooth' }); + }, + [lockSpy, scrollOffset] + ); + + React.useEffect(() => { + if (!enable) return; + + const handler = () => { + if (spyLockedRef.current) return; + + let current: StepKey | undefined = undefined; + let best = Number.POSITIVE_INFINITY; + + for (const { key, id } of sections) { + const el = document.getElementById(id); + if (!el) continue; + const dist = Math.abs(el.getBoundingClientRect().top - scrollOffset - 8); + if (dist < best) { + best = dist; + current = key; + } + } + + if (current && current !== active) { + setActive(current); + } + }; + + handler(); // avalia logo ao montar + window.addEventListener('scroll', handler, { passive: true }); + return () => window.removeEventListener('scroll', handler); + }, [enable, sections, active, scrollOffset, setActive]); + + return { scrollToId, lockSpy }; +} + +/** ================== + * Subcomponente: StepLink + * ================== */ +function StepLink({ + active, + onClick, + icon, + title, + description, +}: { + active?: boolean; + onClick?: () => void; + icon: React.ReactNode; + title: string; + description?: string; +}) { + return ( + + ); +} + +/** =================== + * Componente principal + * =================== */ +export type StepNavigatorRef = { + /** Rola até a seção pela key ou id */ + scrollTo: (target: StepKey | { id: string }) => void; +}; + +export const StepNavigator = React.forwardRef( + ( + { + sections, + active: activeProp, + defaultActive, + onChange, + scrollOffset = DEFAULT_SCROLL_OFFSET, + spyLockMs = DEFAULT_SPY_LOCK_MS, + className, + enableScrollSpy = true, + }, + ref + ) => { + // estado controlado/ não-controlado + const [activeState, setActiveState] = React.useState( + activeProp ?? defaultActive ?? sections[0]?.key + ); + + // sincroniza quando for controlado + React.useEffect(() => { + if (activeProp !== undefined) setActiveState(activeProp); + }, [activeProp]); + + const setActive = React.useCallback( + (key: StepKey) => { + if (activeProp === undefined) { + setActiveState(key); + } + onChange?.(key); + }, + [activeProp, onChange] + ); + + const { scrollToId, lockSpy } = useScrollSpy({ + sections, + active: activeState, + setActive, + scrollOffset, + enable: enableScrollSpy, + lockMs: spyLockMs, + }); + + const scrollTo = React.useCallback( + (target: StepKey | { id: string }) => { + const id = + typeof target === 'string' + ? sections.find((s) => s.key === target)?.id + : target.id; + if (!id) return; + scrollToId(id); + }, + [sections, scrollToId] + ); + + React.useImperativeHandle(ref, () => ({ scrollTo }), [scrollTo]); + + return ( + + ); + } +); +StepNavigator.displayName = 'StepNavigator'; \ No newline at end of file From 790c79ede6ba9b4b1017227871b06390ca5e071d Mon Sep 17 00:00:00 2001 From: Keven Date: Sat, 8 Nov 2025 20:35:25 -0300 Subject: [PATCH 04/14] =?UTF-8?q?[MVPTN-37]=20feat(Itens):=20Implementa=20?= =?UTF-8?q?a=20inser=C3=A7=C3=A3o=20de=20itens=20ao=20pedido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_data/GEmolumento/GEmolumentoIndexData.ts | 3 +- .../g_emolumento/useGEmolumentoReadHook.ts | 5 +- .../useGEmolumentoItemReadHook.ts | 5 +- .../GEmolumento/GEmolumentoServicoSelect.tsx | 149 ++++++++++++++++++ .../TServicoTipo/TServicoTipoSelect.tsx | 33 ++-- .../data/GCalculo/GCalculoServicoData.ts | 17 ++ .../GCalculo/GCalculoServicoInterface.ts | 4 + .../GCalculo/GCalculoServicoService.ts | 11 ++ .../TServicoItemPedidoFormColumns.tsx | 6 +- .../TServicoPedido/TServicoPedidoForm.tsx | 95 ++++++++++- .../useTServicoItemPedidoAddHook.ts | 54 +++++++ .../useTServicoPedidoFormHook.ts | 3 +- .../TServicoItemPedidoAddInterface.ts | 7 + .../TServicoItemPedidoIntefarce.ts | 3 +- .../TServicoPedido/TServicoPedidoInterface.ts | 3 + .../TServicoPedido/TServicoPedidoSchema.ts | 9 +- src/shared/actions/data/ToMoney.ts | 6 + 17 files changed, 383 insertions(+), 30 deletions(-) create mode 100644 src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx create mode 100644 src/packages/administrativo/data/GCalculo/GCalculoServicoData.ts create mode 100644 src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts create mode 100644 src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts create mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts create mode 100644 src/shared/actions/data/ToMoney.ts diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts index 422fb54..e8e80b8 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts @@ -6,6 +6,7 @@ import API from '@/shared/services/api/Api'; // Importa o enum que define os métodos HTTP disponíveis (GET, POST, PUT, DELETE, etc.) import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; + import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface'; // Função assíncrona responsável por executar a requisição para listar os tipos de marcação @@ -14,7 +15,7 @@ async function executeGEmolumentoIndexData(data: GEmolumentoReadInterface) { const api = new API(); // Concatena o endpoint com a query string (caso existam parâmetros) - const endpoint = `administrativo/g_emolumento/${data.sistema_id}`; + const endpoint = `administrativo/g_emolumento/sistema/${data.sistema_id}`; // Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/' return await api.send({ diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts index b18cc96..924bf9c 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook.ts @@ -1,15 +1,16 @@ // Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.) +import { useMemo, useState } from 'react'; + import { useResponse } from '@/shared/components/response/ResponseContext'; // Importa hooks do React para gerenciamento de estado e memorização de valores -import { useMemo, useState } from 'react'; // Importa a interface que define a estrutura dos dados de "GEmolumento" +import { GEmolumentoInterface } from '../../_interfaces/GEmolumentoInterface'; import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInterface'; // Importa o serviço responsável por buscar os dados de "GEmolumento" na API import { GEmolumentoIndexService } from '../../_services/g_emolumento/GEmolumentoIndexService'; -import { GEmolumentoInterface } from '../../_interfaces/GEmolumentoInterface'; // Hook personalizado para leitura (consulta) dos emolumentos export const useGEmolumentoReadHook = () => { diff --git a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts index 000908a..1fa5dd1 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento_item/useGEmolumentoItemReadHook.ts @@ -1,15 +1,16 @@ // Importa o hook responsável por gerenciar e exibir respostas globais (sucesso, erro, etc.) +import { useMemo, useState } from 'react'; + import { useResponse } from '@/shared/components/response/ResponseContext'; // Importa hooks do React para gerenciamento de estado e memorização de valores -import { useMemo, useState } from 'react'; // Importa a interface que define a estrutura dos dados de "gEmolumentoItem" +import { GEmolumentoItemInterface } from '../../_interfaces/GEmolumentoItemInterface'; import { GEmolumentoItemReadInterface } from '../../_interfaces/GEmolumentoItemReadInterface'; // Importa o serviço responsável por buscar os dados de "GEmolumentoItem" na API import { GEmolumentoItemValorService } from '../../_services/g_emolumento_item/GEmolumentoItemValorService'; -import { GEmolumentoItemInterface } from '../../_interfaces/GEmolumentoItemInterface'; // Hook personalizado para leitura (consulta) dos emolumentos export const useGEmolumentoItemReadHook = () => { diff --git a/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx new file mode 100644 index 0000000..d35c6a4 --- /dev/null +++ b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx @@ -0,0 +1,149 @@ +'use client'; + +import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { useGEmolumentoReadHook } from '@/app/(protected)/(cadastros)/cadastros/_hooks/g_emolumento/useGEmolumentoReadHook'; +import { GEmolumentoReadInterface } from '@/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface'; +import { Button } from '@/components/ui/button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { FormControl } from '@/components/ui/form'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; + +// Tipagem das props do componente +interface GEmolumentoSelectProps { + sistema_id: number; + field: any; + onSelectChange?: (emolumento: { emolumento_id: number; descricao: string }) => void; + className?: string; +} + +// Componente principal do select de emolumentos +export default function GEmolumentoServicoSelect({ + sistema_id, + field, + onSelectChange, + className, +}: GEmolumentoSelectProps) { + const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const gEmolumentoReadParams: GEmolumentoReadInterface = { sistema_id }; + const { gEmolumento = [], fetchGEmolumento } = useGEmolumentoReadHook(); + + /** + * Efeito para buscar os dados apenas uma vez. + */ + const loadData = useCallback(async () => { + if (gEmolumento.length) return; + setIsLoading(true); + await fetchGEmolumento(gEmolumentoReadParams); + setIsLoading(false); + }, [gEmolumento.length, fetchGEmolumento, gEmolumentoReadParams]); + + useEffect(() => { + loadData(); + }, [loadData]); + + /** + * Memoriza o item selecionado para evitar reprocessamentos. + */ + const selected = useMemo( + () => + gEmolumento.find( + (item) => String(item.emolumento_id) === String(field?.value?.id ?? ''), + ), + [gEmolumento, field?.value], + ); + + /** + * Manipulador de seleção com verificação segura. + */ + const handleSelect = useCallback( + (item: any) => { + if (!field?.onChange) return; + + const selectedValue = { + emolumento_id: Number(item.emolumento_id), + descricao: item.descricao, + }; + + field.onChange(selectedValue); + + if (onSelectChange) onSelectChange(selectedValue); + + setOpen(false); + }, + [field, onSelectChange], + ); + + return ( + + + + + + + + + + + + + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} + + + {gEmolumento.map((item) => ( + handleSelect(item)} + > + + {GetCapitalize(item.descricao ?? '')} + + ))} + + + + + + ); +} diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index 9aa3642..fc665d1 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -19,7 +19,6 @@ import { useTServicoTipoReadHook } from '@/packages/administrativo/hooks/TServic import TServicoTipoSelectInterface from '@/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; - export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterface) { const [open, setOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -27,7 +26,6 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac /** * Efeito para buscar os dados apenas uma vez. - * useCallback evita recriação desnecessária da função. */ const loadData = useCallback(async () => { if (tServicoTipo.length) return; @@ -41,20 +39,29 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac }, [loadData]); /** - * Memoriza o bairro selecionado para evitar reprocessamentos. + * Item selecionado (comparando por ID) */ const selected = useMemo( - () => tServicoTipo.find((b) => String(b.servico_tipo_id) === String(field?.value ?? '')), + () => + tServicoTipo.find( + (item) => String(item.servico_tipo_id) === String(field?.value?.servico_tipo_id ?? ''), + ), [tServicoTipo, field?.value], ); /** - * Manipulador de seleção com verificação segura. + * Manipulador de seleção */ const handleSelect = useCallback( - (bairroId: string | number) => { + (item: any) => { if (!field?.onChange) return; - field.onChange(bairroId); + + const selectedValue = { + servico_tipo_id: Number(item.servico_tipo_id), + descricao: item.descricao, + }; + + field.onChange(selectedValue); setOpen(false); }, [field], @@ -73,16 +80,17 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac > {isLoading ? 'Carregando...' - : selected - ? GetCapitalize(selected.descricao) + : field.value?.descricao + ? GetCapitalize(field.value.descricao) : 'Selecione...'} + - + {isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'} @@ -92,12 +100,13 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac handleSelect(Number(item.servico_tipo_id))} + onSelect={() => handleSelect(item)} > { + const api = new API(); + return api.send({ + method: Methods.POST, + endpoint: `administrativo/g_calculo/servico/`, + body: data, + }); +} + +export const GCalculoServico = withClientErrorHandler(executeGCalculoCalcularData); diff --git a/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts b/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts new file mode 100644 index 0000000..19ecb7b --- /dev/null +++ b/src/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface.ts @@ -0,0 +1,4 @@ +export default interface GCalculoServicoInterface { + valor_documento?: number; + emolumento_id?: number; +} diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts new file mode 100644 index 0000000..6b90bcc --- /dev/null +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -0,0 +1,11 @@ +import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler"; + +import { GCalculoServico } from "../../data/GCalculo/GCalculoServicoData"; +import GCalculoServicoInterface from "../../interfaces/GCalculo/GCalculoServicoInterface"; + +async function executeGCalculoServicoService(data: GCalculoServicoInterface) { + const response = await GCalculoServico(data); + return response; +} + +export const GCalculoServicoService = withClientErrorHandler(executeGCalculoServicoService); diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx index 558ef73..c31a92f 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -26,7 +26,7 @@ export default function TServicoItemPedidoFormColumns(
- {GetCapitalize(data.servico)} + {GetCapitalize(data.descricao)}
{GetCapitalize(data.tabela)} @@ -36,8 +36,8 @@ export default function TServicoItemPedidoFormColumns( ); }, sortingFn: (a, b) => - (a.original.servico?.toLowerCase() || '').localeCompare( - b.original.servico?.toLowerCase() || '', + (a.original.descricao?.toLowerCase() || '').localeCompare( + b.original.descricao?.toLowerCase() || '', ), }, diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 48c6539..fe964d0 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -16,7 +16,11 @@ import { FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect'; import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; +import TServicoTipoSelect from '@/packages/administrativo/components/TServicoTipo/TServicoTipoSelect'; +import TServicoItemPedidoFormTable from '@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable'; +import { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; @@ -26,16 +30,40 @@ import { StepNavigator, StepNavigatorRef, StepSection } from '@/shared/component import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; + + export default function TServicoPedidoForm() { // Controle de rotas const router = useRouter(); + const form = useTServicoPedidoFormHook({}); + + const { setValue, handleSubmit, watch } = form; + // Controle de estado do botão const [buttonIsLoading, setButtonIsLoading] = useState(false); const { TServicoPedido, saveTServicoPedido } = useTServicoPedidoSaveHook() + const { TServicoItemPedido, addTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue) - const form = useTServicoPedidoFormHook({}); + const servicoAtual = watch('servico_tipo_id'); + const emolumentoAtual = watch('emolumento_id'); + + const handleAddItem = () => { + + if (servicoAtual && emolumentoAtual) { + + // Cria um novo objeto + const servicoEEmolumento = { + servico_tipo: servicoAtual, + emolumento: emolumentoAtual + } + + // Adiciona o item calculado na tabel + addTServicoItemPedido(servicoEEmolumento) + + } + }; const sections: StepSection[] = [ { key: 'pedido', id: 'selectPedido', icon: , title: 'Pedido', description: 'Dados gerais do pedido.' }, @@ -50,8 +78,6 @@ export default function TServicoPedidoForm() { async (formData: TServicoPedidoInterface) => { - console.log(formData); - // Coloca o botão em estado de loading setButtonIsLoading(true); @@ -76,6 +102,11 @@ export default function TServicoPedidoForm() { console.log('Erro no formulário:', error); } + React.useEffect(() => { + console.log(JSON.stringify(TServicoItemPedido)) + }, [TServicoItemPedido]) + + return (

@@ -205,6 +236,58 @@ export default function TServicoPedidoForm() {

+ + + +

+ Itens +

+
+
+ +
+
+ ( + + Serviços + + + + )} + /> +
+
+ ( + + Emolumentos + + + + )} + /> +
+
+ +
+
+ { }} + onDelete={() => { }} + /> +
+
+
+
{/* Seção: Pagamento */} @@ -252,7 +335,7 @@ export default function TServicoPedidoForm() { )} />
-
+
-
+
-
+
) => { + + const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + + const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { + + console.log(data.emolumento) + + data.sistema_id = 2 + data.valor_documento = 0 + data.quantidade = 1 + data.emolumento_id = data.emolumento.emolumento_id + + const response = await GCalculoServicoService(data) + + const item = { + emolumento_id: data.emolumento.emolumento_id, + emolumento_item_id: data.emolumento.emolumento_item_id, + servico_tipo_id: data.servico_tipo.servico_tipo_id, + descricao: data.servico_tipo.descricao, + tabela: data.emolumento.descricao, + situacao: 'F', + qtd: 1, + valor: response.data.valor_total, + emolumento: response.data.valor_emolumento, + fundesp: response.data.valor_fundos, + taxa_judiciaria: response.data.taxa_judiciaria, + valor_iss: response.data.valor_iss, + } + + setTServicoItemPedido((prev) => { + const novoItem = [...prev, item] + if (setValue) setValue('itens', novoItem) + return novoItem + }); + + }; + + return { + TServicoItemPedido, + addTServicoItemPedido, + }; +}; diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index fc60cba..b8fef59 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -13,7 +13,7 @@ export function useTServicoPedidoFormHook(defaults?: Partial texto longo ou base64 + certidao_texto?: string | null ato_antigo_tipo?: string valor_iss?: number id_ato_isentado?: number @@ -63,4 +63,5 @@ export default interface TServicoItemPedidoInterface { vrcext?: number valor_fundo_selo?: number averbacao?: string + descricao?: string } diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index 05342c5..ca0df03 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -17,4 +17,7 @@ export default interface TServicoPedidoInterface { selo_pessoa_cpfcnpj?: string; login?: string; funcao?: string; + itens?: []; + servico_tipo_id: object; + emolumento_id: object; } \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 85c2d92..499aedb 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -1,5 +1,7 @@ import z from "zod"; +import { TServicoItemPedidoSchema } from "../TServicoItemPedido/TServicoItemPedidoSchema"; + export const TServicoPedidoSchema = z.object({ servico_pedido_id: z.number().optional(), @@ -9,7 +11,7 @@ export const TServicoPedidoSchema = z.object({ data_pedido: z.string().optional(), mensalista_livrocaixa_id: z.number().optional(), observacao: z.string().optional(), - escrevente_id: z.number().optional(), + escrevente_id: z.number(), situacao: z.string().optional(), estornado: z.string().optional(), nfse_id: z.number().optional(), @@ -18,7 +20,10 @@ export const TServicoPedidoSchema = z.object({ selo_pessoa_nome: z.string().optional(), selo_pessoa_cpfcnpj: z.string().optional(), login: z.string().optional(), - funcao: z.string().optional() + funcao: z.string().optional(), + itens: z.array(TServicoItemPedidoSchema).default([]), + servico_tipo_id: z.object(), + emolumento_id: z.object(), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/shared/actions/data/ToMoney.ts b/src/shared/actions/data/ToMoney.ts new file mode 100644 index 0000000..95a5c24 --- /dev/null +++ b/src/shared/actions/data/ToMoney.ts @@ -0,0 +1,6 @@ +// Helper: garante número com 2 casas decimais +export const toMoney = (v: unknown): number => { + const n = Number(v ?? 0); + // evita problemas de ponto flutuante + return Math.round((n + Number.EPSILON) * 100) / 100; +}; From 88cb6f67c80a6d178f37832cd98652dd04503b25 Mon Sep 17 00:00:00 2001 From: Keven Date: Sun, 9 Nov 2025 19:03:15 -0300 Subject: [PATCH 05/14] =?UTF-8?q?[MVPTN-37]=20feat(Balc=C3=A3o):=20Ajusta?= =?UTF-8?q?=20a=20tela=20de=20balc=C3=A3o=20para=20trabalhar=20com=20itens?= =?UTF-8?q?=20de=20pedido?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../balcao/pedido/[servicoPedidoId]/page.tsx | 16 ++ src/app/globals.css | 261 +++++++----------- .../TServicoTipo/TServicoTipoSelect.tsx | 1 + .../TServicoItemPedidoFormColumns.tsx | 5 +- .../TServicoItemPedidoFormTable.tsx | 4 +- .../TServicoPedido/TServicoPedidoColumns.tsx | 8 +- .../TServicoPedido/TServicoPedidoForm.tsx | 89 +++++- .../TServicoPedido/TServicoPedidoIndex.tsx | 5 +- .../useTServicoItemPedidoAddHook.ts | 11 +- .../useTServicoItemPedidoLocalAddHook.ts | 44 +++ .../useTServicoPedidoFormHook.ts | 4 +- .../interfaces/TServico/TServicoInterface.ts | 1 - .../TServicoItemPedidoTableInterface.ts | 2 - .../TServicoPedidoFormInterface.ts | 5 +- .../TServicoPedido/TServicoPedidoSchema.ts | 10 +- 15 files changed, 262 insertions(+), 204 deletions(-) create mode 100644 src/app/(protected)/servicos/balcao/pedido/[servicoPedidoId]/page.tsx create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts diff --git a/src/app/(protected)/servicos/balcao/pedido/[servicoPedidoId]/page.tsx b/src/app/(protected)/servicos/balcao/pedido/[servicoPedidoId]/page.tsx new file mode 100644 index 0000000..d830198 --- /dev/null +++ b/src/app/(protected)/servicos/balcao/pedido/[servicoPedidoId]/page.tsx @@ -0,0 +1,16 @@ +'use client' + +import { useParams } from "next/navigation"; + +import TServicoPedidoForm from "@/packages/servicos/components/TServicoPedido/TServicoPedidoForm"; + +export default function TServicoPedidoPage() { + + const params = useParams(); + + return ( + + ) +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 3b3b53c..cb3b2e8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3,177 +3,112 @@ @custom-variant dark (&:is(.dark *)); -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: Inter, sans-serif; - --font-mono: JetBrains Mono, monospace; - --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); - --font-serif: Source Serif 4, serif; - --radius: 0.375rem; - --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); - --shadow-2xl: var(--shadow-2xl); - --shadow-xl: var(--shadow-xl); - --shadow-lg: var(--shadow-lg); - --shadow-md: var(--shadow-md); - --shadow: var(--shadow); - --shadow-sm: var(--shadow-sm); - --shadow-xs: var(--shadow-xs); - --shadow-2xs: var(--shadow-2xs); - --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); - --color-destructive-foreground: var(--destructive-foreground); -} - :root { - --background: #f8fafc; - --foreground: #1e293b; - --card: #ffffff; - --card-foreground: #1e293b; - --popover: #ffffff; - --popover-foreground: #1e293b; - --primary: #1a292f; - --primary-foreground: #ffffff; - --secondary: #e5e7eb; - --secondary-foreground: #162227; - --muted: #f3f4f6; - --muted-foreground: #6b7280; - --accent: #e0e7ff; - --accent-foreground: #2b454f; - --destructive: #ef4444; - --destructive-foreground: #ffffff; - --border: #d1d5db; - --input: #d1d5db; - --ring: #2b454f; - --chart-1: #f26f28; - --chart-2: #f26418; - --chart-3: #e75a0d; - --chart-4: #c14b0b; - --chart-5: #9a3c09; - --sidebar: #f3f4f6; - --sidebar-foreground: #1e293b; - --sidebar-primary: #2b454f; - --sidebar-primary-foreground: #ffffff; - --sidebar-accent: #1a292f; - --sidebar-accent-foreground: #374151; - --sidebar-border: #ffffff; - --sidebar-ring: #2b454f; - --font-sans: Inter, sans-serif; - --font-serif: Merriweather, serif; - --font-mono: JetBrains Mono, monospace; - --radius: 0.5rem; - --shadow-x: 0px; - --shadow-y: 4px; - --shadow-blur: 8px; - --shadow-spread: -1px; + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 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: hsl(0 0% 0%); - --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); - --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); - --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10); - --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10); - --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10); - --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25); + --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.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); --tracking-normal: 0em; --spacing: 0.25rem; } .dark { - --background: #0f172a; - --foreground: #e2e8f0; - --card: #1e293b; - --card-foreground: #e2e8f0; - --popover: #1e293b; - --popover-foreground: #e2e8f0; - --primary: #818cf8; - --primary-foreground: #0f172a; - --secondary: #2d3748; - --secondary-foreground: #d1d5db; - --muted: #152032; - --muted-foreground: #9ca3af; - --accent: #374151; - --accent-foreground: #d1d5db; - --destructive: #ef4444; - --destructive-foreground: #0f172a; - --border: #4b5563; - --input: #4b5563; - --ring: #818cf8; - --chart-1: #818cf8; - --chart-2: #6366f1; - --chart-3: #4f46e5; - --chart-4: #4338ca; - --chart-5: #3730a3; - --sidebar: #1e293b; - --sidebar-foreground: #e2e8f0; - --sidebar-primary: #818cf8; - --sidebar-primary-foreground: #0f172a; - --sidebar-accent: #374151; - --sidebar-accent-foreground: #d1d5db; - --sidebar-border: #4b5563; - --sidebar-ring: #818cf8; - --font-sans: Inter, sans-serif; - --font-serif: Merriweather, serif; - --font-mono: JetBrains Mono, monospace; - --radius: 0.5rem; - --shadow-x: 0px; - --shadow-y: 4px; - --shadow-blur: 8px; - --shadow-spread: -1px; + --background: oklch(0.1450 0 0); + --foreground: oklch(0.9850 0 0); + --card: oklch(0.2050 0 0); + --card-foreground: oklch(0.9850 0 0); + --popover: oklch(0.2690 0 0); + --popover-foreground: oklch(0.9850 0 0); + --primary: oklch(0.9220 0 0); + --primary-foreground: oklch(0.2050 0 0); + --secondary: oklch(0.2690 0 0); + --secondary-foreground: oklch(0.9850 0 0); + --muted: oklch(0.2690 0 0); + --muted-foreground: oklch(0.7080 0 0); + --accent: oklch(0.3710 0 0); + --accent-foreground: oklch(0.9850 0 0); + --destructive: oklch(0.7040 0.1910 22.2160); + --destructive-foreground: oklch(0.9850 0 0); + --border: oklch(0.2750 0 0); + --input: oklch(0.3250 0 0); + --ring: oklch(0.5560 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.2050 0 0); + --sidebar-foreground: oklch(0.9850 0 0); + --sidebar-primary: oklch(0.4880 0.2430 264.3760); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.2690 0 0); + --sidebar-accent-foreground: oklch(0.9850 0 0); + --sidebar-border: oklch(0.2750 0 0); + --sidebar-ring: oklch(0.4390 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: hsl(0 0% 0%); - --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05); - --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); - --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10); - --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10); - --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10); - --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10); - --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25); + --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.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); } @theme inline { diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index fc665d1..2648da6 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -59,6 +59,7 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac const selectedValue = { servico_tipo_id: Number(item.servico_tipo_id), descricao: item.descricao, + tipo_item: item.tipo_item, }; field.onChange(selectedValue); diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx index c31a92f..5064271 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -11,10 +11,7 @@ type TableMeta = { updateData?: (rowIndex: number, columnId: string, value: unknown) => void; }; -export default function TServicoItemPedidoFormColumns( - onEdit: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, - onDelete: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, -): ColumnDef[] { +export default function TServicoItemPedidoFormColumns(): ColumnDef[] { return [ // servico { diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx index 55d1c74..9e23bbf 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx @@ -9,8 +9,8 @@ import TServicoItemPedidoFormColumns from './TServicoItemPedidoFormColumns'; /** * Componente principal da tabela de Naturezas */ -export default function TServicoItemPedidoFormTable({ data, onEdit, onDelete }: TServicoItemPedidoTableInterface) { - const columns = TServicoItemPedidoFormColumns(onEdit, onDelete); +export default function TServicoItemPedidoFormTable({ data }: TServicoItemPedidoTableInterface) { + const columns = TServicoItemPedidoFormColumns(); return (
- onEdit(servicoPedido, true)}> - - Editar + + + + Editar + onDelete(servicoPedido, true)}> diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index fe964d0..792f88b 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -21,35 +21,90 @@ import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuar import TServicoTipoSelect from '@/packages/administrativo/components/TServicoTipo/TServicoTipoSelect'; import TServicoItemPedidoFormTable from '@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable'; import { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; +import { useTServicoItemPedidoIndexHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook'; import { useTServicoPedidoFormHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook'; import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; +import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook'; +import { TServicoPedidoFormInterface } from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface'; import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; import { parseNumberInput } from '@/shared/actions/form/parseNumberInput'; import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; import { StepNavigator, StepNavigatorRef, StepSection } from '@/shared/components/step/stepNavigator'; import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; +import { useTServicoItemPedidoLocalAddHook } from '../../hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook'; - - -export default function TServicoPedidoForm() { +export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedidoFormInterface) { // Controle de rotas const router = useRouter(); const form = useTServicoPedidoFormHook({}); - const { setValue, handleSubmit, watch } = form; + const { setValue, handleSubmit, reset, watch } = form; // Controle de estado do botão const [buttonIsLoading, setButtonIsLoading] = useState(false); - const { TServicoPedido, saveTServicoPedido } = useTServicoPedidoSaveHook() - const { TServicoItemPedido, addTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue) + const { TServicoPedido, saveTServicoPedido } = useTServicoPedidoSaveHook(); + const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue); + const { TServicoItemPedido, addTServicoItemPedido, setTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue); + const { TServicoItemPedido: itemPedidoShow, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() + const { TServicoPedido: pedidoShow, showTServicoPedido } = useTServicoPedidoShowHook(); + // Campos para serem monitorados const servicoAtual = watch('servico_tipo_id'); const emolumentoAtual = watch('emolumento_id'); - const handleAddItem = () => { + // BUsca o o pedido desejado + const TServicoPedidoShowData = async () => { + + // Criar um objeto de envio + const servicoPedido: TServicoPedidoInterface = { + + servico_pedido_id: servico_pedido_id + + } + + // Busca os dados desejado + const response = await showTServicoPedido(servicoPedido) + + // Verifica se foi localizado os dados + if (response.servico_pedido_id) { + + // Atualiza os valores do pedido + reset(response) + + // Busca os itens do pedido + TServicoPedidoItemIndexData(response.servico_pedido_id) + + } + + } + + // Busca os itens do pedido + const TServicoPedidoItemIndexData = async (servico_pedido_id: number) => { + + // Cria objeto de envio + const servicoPedido: TServicoPedidoInterface = { + + servico_pedido_id: servico_pedido_id + + } + + // Busca os itens + const response = await indexTServicoItemPedido(servicoPedido) + + // Percorre todos os itens localizados + for (const item of response.data) { + + // Adiciona os itens locais + localAddTServicoItemPedido(item) + + } + + } + + const handleAddItem = async () => { if (servicoAtual && emolumentoAtual) { @@ -59,8 +114,7 @@ export default function TServicoPedidoForm() { emolumento: emolumentoAtual } - // Adiciona o item calculado na tabel - addTServicoItemPedido(servicoEEmolumento) + await addTServicoItemPedido(servicoEEmolumento); } }; @@ -103,8 +157,16 @@ export default function TServicoPedidoForm() { } React.useEffect(() => { - console.log(JSON.stringify(TServicoItemPedido)) - }, [TServicoItemPedido]) + + // Verifica se existe pedido para buscar + if (servico_pedido_id) { + + // Busca o pedido desejado + TServicoPedidoShowData() + + } + + }, []) return ( @@ -279,10 +341,7 @@ export default function TServicoPedidoForm() {
{ }} - onDelete={() => { }} + data={TServicoItemPedidoLocal} />
diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx index 0d54fdf..3615c71 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoIndex.tsx @@ -122,7 +122,10 @@ export default function TServicoPedidoIndex() { href='/servicos/balcao/pedido' /> {/* Tabela de andamentos */} - + {/* Modal de confirmação */} {isConfirmOpen && ( ) => { const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + const { TServicoItemPedidoLocal, localAddTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue) const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { - console.log(data.emolumento) - data.sistema_id = 2 data.valor_documento = 0 data.quantidade = 1 @@ -26,8 +27,9 @@ export const useTServicoItemPedidoAddHook = (setValue: UseFormSetValue) => { + + const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState([]); + + const localAddTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { + + const item = { + emolumento_id: data.emolumento_id, + emolumento_item_id: data.emolumento_item_id, + servico_tipo_id: data.servico_tipo_id, + tipo_item: data.tipo_item, + descricao: data.descricao, + tabela: data.descricao, + situacao: data.situacao, + qtd: data.qtd, + valor: data.valor, + emolumento: data.emolumento, + fundesp: data.fundesp, + taxa_judiciaria: data.taxa_judiciaria, + valor_iss: data.valor_iss, + } + + setLocalTServicoItemPedido((prev) => { + const novoItem = [...prev, item] + if (setValue) setValue('itens', novoItem) + return novoItem + }); + + }; + + return { + TServicoItemPedidoLocal, + setLocalTServicoItemPedido, + localAddTServicoItemPedido, + }; +}; diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index b8fef59..9045b5a 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -1,7 +1,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; -import { TServicoPedidoFormValues, TServicoPedidoSchema } from '../../schemas/TServicoPedido/TServicoPedidoSchema'; +import { TServicoPedidoFormValues, TServicoPedidoSchema } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema'; export function useTServicoPedidoFormHook(defaults?: Partial) { return useForm({ @@ -21,6 +21,8 @@ export function useTServicoPedidoFormHook(defaults?: Partial void; - onDelete: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void; } diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts index 827f2f1..97bdab4 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts @@ -1,7 +1,4 @@ -import { TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema'; export interface TServicoPedidoFormInterface { - data: TServicoPedidoFormValues | null; - onSave: (data: TServicoPedidoFormValues) => void; - buttonIsLoading: boolean; + servico_pedido_id?: number } diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 499aedb..37f1d79 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -9,12 +9,12 @@ export const TServicoPedidoSchema = z.object({ valor_pago: z.number().optional(), usuario_id: z.number().optional(), data_pedido: z.string().optional(), - mensalista_livrocaixa_id: z.number().optional(), - observacao: z.string().optional(), + mensalista_livrocaixa_id: z.union([z.number(), z.null()]), + observacao: z.union([z.string(), z.null()]), escrevente_id: z.number(), situacao: z.string().optional(), estornado: z.string().optional(), - nfse_id: z.number().optional(), + nfse_id: z.union([z.number(), z.null()]), apresentante: z.string().optional(), cpfcnpj_apresentante: z.string().optional(), selo_pessoa_nome: z.string().optional(), @@ -22,8 +22,8 @@ export const TServicoPedidoSchema = z.object({ login: z.string().optional(), funcao: z.string().optional(), itens: z.array(TServicoItemPedidoSchema).default([]), - servico_tipo_id: z.object(), - emolumento_id: z.object(), + servico_tipo_id: z.object().optional(), + emolumento_id: z.object().optional(), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file From b85cd6aeb9136ea40844590b6c73d5b6b71c10fd Mon Sep 17 00:00:00 2001 From: Keven Date: Mon, 10 Nov 2025 18:21:14 -0300 Subject: [PATCH 06/14] [MVPTN-37] feat(ItensPedido): Implementa parcialmente os pessoas vinculadas aos itens dos pedidos --- .../TPessoaJuridica/TPessoaJuridicaForm.tsx | 2 +- .../TPessoa/TPessoaTableFormColumnsDialog.tsx | 142 ++++++++++++++++++ .../TPessoa/TPessoaTableFormDialog.tsx | 128 ++++++++++++++++ .../TPessoaRepresentanteForm.tsx | 4 +- .../TServicoTipo/TServicoTipoSelect.tsx | 1 + .../useTPessoaFisicaIndexHook.ts | 2 + .../TPessoa/TPessoaTableFormInterface.ts | 9 ++ .../TServicoPedido/TServicoPedidoForm.tsx | 87 ++++++++--- .../useTServicoItemPedidoAddHook.ts | 79 ++++++---- .../useTServicoItemPedidoLocalAddHook.ts | 41 +++-- src/shared/components/dataTable/DataTable.tsx | 46 +++--- 11 files changed, 440 insertions(+), 101 deletions(-) create mode 100644 src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx create mode 100644 src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx create mode 100644 src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts diff --git a/src/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaForm.tsx b/src/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaForm.tsx index 93d9eb8..487f696 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaForm.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaJuridica/TPessoaJuridicaForm.tsx @@ -1,7 +1,7 @@ 'use client'; import { HouseIcon, IdCardIcon, UserIcon } from 'lucide-react'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx new file mode 100644 index 0000000..8b0ace3 --- /dev/null +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx @@ -0,0 +1,142 @@ +import { ColumnDef } from '@tanstack/react-table'; +import { ArrowUpDownIcon } from 'lucide-react'; + +import { Button } from '@/components/ui/button'; +import { FormatCPF } from '@/shared/actions/CPF/FormatCPF'; +import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; +import { FormatPhone } from '@/shared/actions/phone/FormatPhone'; +import GetNameInitials from '@/shared/actions/text/GetNameInitials'; +import empty from '@/shared/actions/validations/empty'; + +import TPessoaInterface from '../../interfaces/TPessoa/TPessoaInterface'; + +/** + * Função para criar a definição das colunas da tabela + */ +export function TPessoaTableFormColumnsDialog(): ColumnDef[] { + return [ + // ID + { + accessorKey: 'pessoa_id', + header: ({ column }) => ( + + ), + cell: ({ row }) => Number(row.getValue('pessoa_id')), + enableSorting: false, + }, + + // Nome / Email / Foto + { + id: 'nome_completo', + accessorFn: (row) => row, + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const pessoa = row.original; + + return ( +
+ {/* Foto ou Iniciais */} +
+ {pessoa.foto ? ( + {pessoa.nome + ) : ( + + {GetNameInitials(pessoa.nome)} + + )} +
+ + {/* Nome e Email */} +
+
{pessoa.nome || '-'}
+
+ {empty(pessoa.email) ? 'Email não informado' : pessoa.email} +
+
+
+ ); + }, + sortingFn: (a, b) => + (a.original.nome?.toLowerCase() || '').localeCompare(b.original.nome?.toLowerCase() || ''), + }, + + // CPF + { + accessorKey: 'cpf_cnpj', + header: ({ column }) => ( + + ), + cell: ({ row }) => FormatCPF(row.getValue('cpf_cnpj')), + }, + + // Telefone + { + accessorKey: 'telefone', + header: ({ column }) => ( + + ), + cell: ({ row }) => FormatPhone(row.getValue('telefone')), + }, + + // Cidade / UF + { + id: 'cidade_uf', + accessorFn: (row) => `${row.cidade}/${row.uf}`, + header: ({ column }) => ( + + ), + cell: ({ row }) => {row.getValue('cidade_uf') || '-'}, + sortingFn: (a, b) => + `${a.original.cidade}/${a.original.uf}` + .toLowerCase() + .localeCompare(`${b.original.cidade}/${b.original.uf}`.toLowerCase()), + }, + + // Data de cadastro + { + accessorKey: 'data_cadastro', + header: ({ column }) => ( + + ), + cell: ({ row }) => FormatDateTime(row.getValue('data_cadastro')), + sortingFn: 'datetime', + }, + + ]; +} diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx new file mode 100644 index 0000000..1b83d8d --- /dev/null +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx @@ -0,0 +1,128 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { Form } from '@/components/ui/form'; +import { useTPessoaFisicaIndexHook } from '@/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook'; +import { useTPessoaRepresentanteFormHook } from '@/packages/administrativo/hooks/TPessoaRepresentante/useTPessoaRepresentanteFormHook'; +import TPessoaTableFormInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface'; +import { DataTable } from '@/shared/components/dataTable/DataTable'; +import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; + +import { useTPessoaJuridicaIndexHook } from '../../hooks/TPessoa/TPessoaJuridica/useTPessoaJuridicaIndexHook'; +import { TPessoaTableFormColumnsDialog } from './TPessoaTableFormColumnsDialog'; + + +export default function TPessoaTableFormDialog({ + isOpen, + tipoPessoa, + onClose, + onSave, + buttonIsLoading, +}: TPessoaTableFormInterface) { + + const { tPessoaFisica, fetchTPessoaFisica } = useTPessoaFisicaIndexHook(); + const { tPessoaJuridica, fetchTPessoaJuridica } = useTPessoaJuridicaIndexHook(); + const [pessoas, setPessoas] = useState() + + const form = useTPessoaRepresentanteFormHook(); + + const loadData = async (tipoPessoa: string) => { + + switch (tipoPessoa) { + + case "F": + + await fetchTPessoaFisica(); + break; + + case "J": + + await fetchTPessoaJuridica(); + break; + + } + + }; + + useEffect(() => { + + setPessoas(tPessoaFisica) + + }, [tPessoaFisica]) + + useEffect(() => { + + setPessoas(tPessoaJuridica) + + }, [tPessoaJuridica]) + + useEffect(() => { + + if (tipoPessoa) { + + loadData(tipoPessoa); + + } + + }, [tipoPessoa]); + + const columns = TPessoaTableFormColumnsDialog(); + + return ( + { + if (!open) onClose(null, false); + }} + > + + + Pessoa + Busque a pessoa desejada + +
+ +
+ +
+ {/* Rodapé do Dialog */} + + + + + + +
+ +
+
+ ); +} diff --git a/src/packages/administrativo/components/TPessoaRepresentante/TPessoaRepresentanteForm.tsx b/src/packages/administrativo/components/TPessoaRepresentante/TPessoaRepresentanteForm.tsx index a8ddf69..9bb39c6 100644 --- a/src/packages/administrativo/components/TPessoaRepresentante/TPessoaRepresentanteForm.tsx +++ b/src/packages/administrativo/components/TPessoaRepresentante/TPessoaRepresentanteForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; import { @@ -17,11 +17,11 @@ import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; import { DataTable } from '@/shared/components/dataTable/DataTable'; import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; -import TPessoasRepresentanteFormColumns from './TPessoasRepresentanteFormColumns'; import { useTPessoaFisicaIndexHook } from '../../hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook'; import { useTPessoaRepresentanteFormHook } from '../../hooks/TPessoaRepresentante/useTPessoaRepresentanteFormHook'; import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface'; import TPessoaRepresentanteFormInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentnateFormInterface'; +import TPessoasRepresentanteFormColumns from './TPessoasRepresentanteFormColumns'; export default function TPessoaRepresentanteForm({ isOpen, diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index 2648da6..9fb3486 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -60,6 +60,7 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac servico_tipo_id: Number(item.servico_tipo_id), descricao: item.descricao, tipo_item: item.tipo_item, + tipo_pessoa: item.tipo_pessoa, }; field.onChange(selectedValue); diff --git a/src/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook.ts b/src/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook.ts index feb748a..43d6c9b 100644 --- a/src/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook.ts +++ b/src/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook.ts @@ -15,6 +15,8 @@ export const useTPessoaFisicaIndexHook = () => { setTPessoa(response.data); setResponse(response); + + return response.data }; return { tPessoaFisica, fetchTPessoaFisica }; diff --git a/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts new file mode 100644 index 0000000..6df3555 --- /dev/null +++ b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts @@ -0,0 +1,9 @@ +import { TPessoaRepresentanteFormValues } from '../../schemas/TPessoaRepresentante/TPessoaRepresentanteSchema'; + +export default interface TPessoaTableFormInterface { + isOpen: boolean; + tipoPessoa: string; + onClose: (item: null, isFormStatus: boolean) => void; + onSave: (data: TPessoaRepresentanteFormValues) => void; + buttonIsLoading: boolean; +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 792f88b..67eba9b 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -18,10 +18,12 @@ import { import { Input } from '@/components/ui/input'; import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect'; 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 TServicoItemPedidoFormTable from '@/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable'; import { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; 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 { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook'; import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook'; @@ -32,7 +34,6 @@ import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; import { StepNavigator, StepNavigatorRef, StepSection } from '@/shared/components/step/stepNavigator'; import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; -import { useTServicoItemPedidoLocalAddHook } from '../../hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook'; export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedidoFormInterface) { @@ -50,11 +51,31 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const { TServicoItemPedido, addTServicoItemPedido, setTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue); const { TServicoItemPedido: itemPedidoShow, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() const { TServicoPedido: pedidoShow, showTServicoPedido } = useTServicoPedidoShowHook(); + const [isFormOpen, setIsFormOpen] = useState(false); + const [servicoTipoPessoa, setServicoTipoPessoa] = useState(''); // Campos para serem monitorados const servicoAtual = watch('servico_tipo_id'); const emolumentoAtual = watch('emolumento_id'); + /** + * Fecha o formulário e limpa o andamento selecionado + */ + const handleCloseForm = useCallback(() => { + console.log('handleCloseForm'); + setIsFormOpen(false); + }, []); + + /** + * Salva os dados do formulário + */ + const handleSave = useCallback( + async (formData: any) => { + console.log('handleSave'); + }, + [handleCloseForm], + ); + // BUsca o o pedido desejado const TServicoPedidoShowData = async () => { @@ -84,39 +105,45 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Busca os itens do pedido const TServicoPedidoItemIndexData = async (servico_pedido_id: number) => { - // Cria objeto de envio - const servicoPedido: TServicoPedidoInterface = { + const servicoPedido: TServicoPedidoInterface = { servico_pedido_id }; - servico_pedido_id: servico_pedido_id + const response = await indexTServicoItemPedido(servicoPedido); + + if (response?.data?.length > 0) { + + // Adiciona todos de uma vez, criando uma nova referência de estado + setLocalTServicoItemPedido(response.data); + + // (opcional) sincroniza com o react-hook-form + setValue('itens', response.data); } - // Busca os itens - const response = await indexTServicoItemPedido(servicoPedido) - - // Percorre todos os itens localizados - for (const item of response.data) { - - // Adiciona os itens locais - localAddTServicoItemPedido(item) - - } - - } + }; const handleAddItem = async () => { if (servicoAtual && emolumentoAtual) { - // Cria um novo objeto - const servicoEEmolumento = { - servico_tipo: servicoAtual, - emolumento: emolumentoAtual - } + // const servicoEEmolumento = { - await addTServicoItemPedido(servicoEEmolumento); + // servico_tipo: servicoAtual, + // emolumento: emolumentoAtual, + + // }; + + // // Adiciona o item remotamente (se for necessário) + // const newItem = await addTServicoItemPedido(servicoEEmolumento); + + // // Garante que o item seja adicionado no estado local (para a tabela renderizar) + // if (newItem) localAddTServicoItemPedido(newItem); + + setServicoTipoPessoa(servicoAtual.tipo_pessoa) + + setIsFormOpen(true); } + }; const sections: StepSection[] = [ @@ -168,6 +195,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido }, []) + React.useEffect(() => { + + setValue('itens', TServicoItemPedidoLocal, { shouldDirty: true }); + + }, [TServicoItemPedidoLocal]); + return (
@@ -464,6 +497,16 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
+ {/* Formulário de criação/edição */} + {isFormOpen && ( + + )}
); } \ No newline at end of file diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts index eb55ecd..71b3dc2 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts @@ -7,47 +7,64 @@ import { GCalculoServicoService } from '@/packages/administrativo/services/GCalc import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; -import { useTServicoItemPedidoLocalAddHook } from './useTServicoItemPedidoLocalAddHook'; - - - -export const useTServicoItemPedidoAddHook = (setValue: UseFormSetValue) => { +export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue) => { const [TServicoItemPedido, setTServicoItemPedido] = useState([]); - const { TServicoItemPedidoLocal, localAddTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue) const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { - data.sistema_id = 2 - data.valor_documento = 0 - data.quantidade = 1 - data.emolumento_id = data.emolumento.emolumento_id - - const response = await GCalculoServicoService(data) - - const item = { - emolumento_id: data.emolumento.emolumento_id, - emolumento_item_id: response.data.emolumento_item_id, - servico_tipo_id: data.servico_tipo.servico_tipo_id, - tipo_item: data.servico_tipo.tipo_item, - descricao: data.servico_tipo.descricao, - tabela: data.emolumento.descricao, - situacao: 'F', - qtd: 1, - valor: response.data.valor_total, - emolumento: response.data.valor_emolumento, - fundesp: response.data.valor_fundos, - taxa_judiciaria: response.data.taxa_judiciaria, - valor_iss: response.data.valor_iss, + // Verifica dados obrigatórios + if (!data?.emolumento?.emolumento_id || !data?.servico_tipo?.servico_tipo_id) { + console.warn('⚠️ Dados inválidos em addTServicoItemPedido', data); + return null; } + // Monta o payload normalizado + const payload = { + ...data, + sistema_id: data.sistema_id ?? 2, + valor_documento: data.valor_documento ?? 0, + quantidade: data.quantidade ?? 1, + emolumento_id: data.emolumento.emolumento_id, + }; + + const response = await GCalculoServicoService(payload); + const result = response?.data ?? {}; + + const item: TServicoItemPedidoIndexResponseInterface = { + emolumento_id: result.emolumento_id ?? payload.emolumento_id ?? 0, + emolumento_item_id: result.emolumento_item_id ?? null, + servico_tipo_id: data.servico_tipo.servico_tipo_id, + tipo_item: data.servico_tipo.tipo_item ?? '', + descricao: data.servico_tipo.descricao ?? '', + tabela: data.emolumento?.descricao ?? '', + situacao: 'F', + qtd: 1, + valor: result.valor_total ?? 0, + emolumento: result.valor_emolumento ?? 0, + fundesp: result.valor_fundos ?? 0, + taxa_judiciaria: result.taxa_judiciaria ?? 0, + valor_iss: result.valor_iss ?? 0, + }; + + // Atualiza o estado com fallback seguro setTServicoItemPedido((prev) => { - const novoItem = [...prev, item] - if (setValue) setValue('itens', novoItem) - return novoItem + + const safePrev = Array.isArray(prev) ? prev : []; + const novoArray = [...safePrev, item]; + + if (setValue) { + + // Protege contra sobrescrita incorreta do formulário + setValue('itens', novoArray, { shouldDirty: true }); + + } + + return novoArray; + }); - localAddTServicoItemPedido(item) + return item; }; diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts index 3ee1331..0f83f40 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts @@ -6,39 +6,36 @@ import { UseFormSetValue } from 'react-hook-form'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; -export const useTServicoItemPedidoLocalAddHook = (setValue: UseFormSetValue) => { +export const useTServicoItemPedidoLocalAddHook = (setValue?: UseFormSetValue) => { const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState([]); - const localAddTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { - - const item = { - emolumento_id: data.emolumento_id, - emolumento_item_id: data.emolumento_item_id, - servico_tipo_id: data.servico_tipo_id, - tipo_item: data.tipo_item, - descricao: data.descricao, - tabela: data.descricao, - situacao: data.situacao, - qtd: data.qtd, - valor: data.valor, - emolumento: data.emolumento, - fundesp: data.fundesp, - taxa_judiciaria: data.taxa_judiciaria, - valor_iss: data.valor_iss, - } + const localAddTServicoItemPedido = (item: any) => { setLocalTServicoItemPedido((prev) => { - const novoItem = [...prev, item] - if (setValue) setValue('itens', novoItem) - return novoItem + + const updated = [...prev, item]; + + if (setValue) setValue("itens", updated); + + return updated; + }); }; + const localClearTServicoItemPedido = () => { + + setLocalTServicoItemPedido([]); + + if (setValue) setValue("itens", []); + + }; + return { TServicoItemPedidoLocal, - setLocalTServicoItemPedido, localAddTServicoItemPedido, + setLocalTServicoItemPedido, + localClearTServicoItemPedido, }; }; diff --git a/src/shared/components/dataTable/DataTable.tsx b/src/shared/components/dataTable/DataTable.tsx index f99c703..55d5ce8 100644 --- a/src/shared/components/dataTable/DataTable.tsx +++ b/src/shared/components/dataTable/DataTable.tsx @@ -2,16 +2,16 @@ import { ColumnDef, + ColumnFiltersState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, - useReactTable, - SortingState, - ColumnFiltersState, - VisibilityState, RowSelectionState, + SortingState, + useReactTable, + VisibilityState, } from '@tanstack/react-table'; import { ChevronLeftIcon, ChevronRightIcon, EyeIcon } from 'lucide-react'; import React from 'react'; @@ -60,25 +60,25 @@ export function DataTable({ ...columns, ...(onEdit || onDelete ? [ - { - id: 'actions', - header: 'Ações', - cell: ({ row }: any) => ( -
- {onEdit && ( - - )} - {onDelete && ( - - )} -
- ), - } as ColumnDef, - ] + { + id: 'actions', + header: 'Ações', + cell: ({ row }: any) => ( +
+ {onEdit && ( + + )} + {onDelete && ( + + )} +
+ ), + } as ColumnDef, + ] : []), ], state: { From 6fe6c86b5d939f5a76ed9de2ee7c8bd8e673ef1f Mon Sep 17 00:00:00 2001 From: Keven Date: Tue, 11 Nov 2025 13:58:27 -0300 Subject: [PATCH 07/14] [MVPTN-37] feat(Item): Finaliza o vinculo de pessoa ao item de pedido --- package-lock.json | 51 ++++- package.json | 2 +- src/components/app-sidebar.tsx | 3 +- src/components/ui/item.tsx | 193 ++++++++++++++++++ src/components/ui/separator.tsx | 18 +- .../TPessoa/TPessoaTableFormColumnsDialog.tsx | 21 +- .../TPessoa/TPessoaTableFormDialog.tsx | 78 +++---- .../TPessoa/TPessoaTableFormInterface.ts | 3 +- .../TServicoItemPedidoFormColumns.tsx | 3 - .../TServicoPedido/TServicoPedidoForm.tsx | 123 ++++++++--- .../TServicoPedido/TServicoPedidoSchema.ts | 6 +- src/shared/components/dataTable/DataTable.tsx | 134 ++++++++---- .../loadingButton/LoadingButton.tsx | 2 +- 13 files changed, 506 insertions(+), 131 deletions(-) create mode 100644 src/components/ui/item.tsx diff --git a/package-lock.json b/package-lock.json index 4d02c62..99e913f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,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.7", + "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", @@ -2132,12 +2132,12 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", - "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", + "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2154,6 +2154,47 @@ } } }, + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", diff --git a/package.json b/package.json index 534cfa0..f25f061 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,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.7", + "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index dec69ef..4616710 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -5,6 +5,7 @@ import { Frame, GalleryVerticalEnd, HouseIcon, + SquareMousePointer, SquareTerminal, UsersIcon } from 'lucide-react'; @@ -46,7 +47,7 @@ const data = { { title: 'Servicos', url: '#', - icon: UsersIcon, + icon: SquareMousePointer, isActive: false, items: [ { diff --git a/src/components/ui/item.tsx b/src/components/ui/item.tsx new file mode 100644 index 0000000..d97de21 --- /dev/null +++ b/src/components/ui/item.tsx @@ -0,0 +1,193 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Separator } from "@/components/ui/separator" + +function ItemGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const itemVariants = cva( + "group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + { + variants: { + variant: { + default: "bg-transparent", + outline: "border-border", + muted: "bg-muted/50", + }, + size: { + default: "p-4 gap-4 ", + sm: "py-3 px-4 gap-2.5", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Item({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"div"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "div" + return ( + + ) +} + +const itemMediaVariants = cva( + "flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5", + { + variants: { + variant: { + default: "bg-transparent", + icon: "size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4", + image: + "size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function ItemMedia({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function ItemContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemDescription({ className, ...props }: React.ComponentProps<"p">) { + return ( +

a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...props} + /> + ) +} + +function ItemActions({ className, ...props }: React.ComponentProps<"div">) { + return ( +

+ ) +} + +function ItemHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function ItemFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Item, + ItemMedia, + ItemContent, + ItemActions, + ItemGroup, + ItemSeparator, + ItemTitle, + ItemDescription, + ItemHeader, + ItemFooter, +} diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx index 4e528b9..275381c 100644 --- a/src/components/ui/separator.tsx +++ b/src/components/ui/separator.tsx @@ -1,13 +1,13 @@ -'use client'; +"use client" -import * as React from 'react'; -import * as SeparatorPrimitive from '@radix-ui/react-separator'; +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils" function Separator({ className, - orientation = 'horizontal', + orientation = "horizontal", decorative = true, ...props }: React.ComponentProps) { @@ -17,12 +17,12 @@ function Separator({ decorative={decorative} orientation={orientation} className={cn( - 'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px', - className, + "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", + className )} {...props} /> - ); + ) } -export { Separator }; +export { Separator } diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx index 8b0ace3..0c13af2 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormColumnsDialog.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from '@tanstack/react-table'; import { ArrowUpDownIcon } from 'lucide-react'; import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { FormatCPF } from '@/shared/actions/CPF/FormatCPF'; import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; import { FormatPhone } from '@/shared/actions/phone/FormatPhone'; @@ -13,8 +14,26 @@ import TPessoaInterface from '../../interfaces/TPessoa/TPessoaInterface'; /** * Função para criar a definição das colunas da tabela */ -export function TPessoaTableFormColumnsDialog(): ColumnDef[] { +export function TPessoaTableFormColumnsDialog(setSelectedTPessoa: React.Dispatch>): ColumnDef[] { return [ + { + id: 'select', + header: '', + cell: ({ row, table }) => ( + { + // Limpa todas as seleções antes de selecionar uma nova + table.resetRowSelection(); + row.toggleSelected(!!value); + setSelectedTPessoa(value ? row.original : null); + }} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, // ID { accessorKey: 'pessoa_id', diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx index 1b83d8d..a318a2f 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormDialog.tsx @@ -10,19 +10,17 @@ import { DialogDescription, DialogFooter, DialogHeader, - DialogTitle, + DialogTitle } from '@/components/ui/dialog'; -import { Form } from '@/components/ui/form'; import { useTPessoaFisicaIndexHook } from '@/packages/administrativo/hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook'; -import { useTPessoaRepresentanteFormHook } from '@/packages/administrativo/hooks/TPessoaRepresentante/useTPessoaRepresentanteFormHook'; +import { useTPessoaJuridicaIndexHook } from '@/packages/administrativo/hooks/TPessoa/TPessoaJuridica/useTPessoaJuridicaIndexHook'; +import TPessoaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaInterface'; import TPessoaTableFormInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface'; import { DataTable } from '@/shared/components/dataTable/DataTable'; import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; -import { useTPessoaJuridicaIndexHook } from '../../hooks/TPessoa/TPessoaJuridica/useTPessoaJuridicaIndexHook'; import { TPessoaTableFormColumnsDialog } from './TPessoaTableFormColumnsDialog'; - export default function TPessoaTableFormDialog({ isOpen, tipoPessoa, @@ -34,9 +32,9 @@ export default function TPessoaTableFormDialog({ const { tPessoaFisica, fetchTPessoaFisica } = useTPessoaFisicaIndexHook(); const { tPessoaJuridica, fetchTPessoaJuridica } = useTPessoaJuridicaIndexHook(); const [pessoas, setPessoas] = useState() + const [selectedTPessoa, setSelectedTPessoa] = useState(null); - const form = useTPessoaRepresentanteFormHook(); - + // Executa o Hook de Acordo com o tipo de pessoa informado const loadData = async (tipoPessoa: string) => { switch (tipoPessoa) { @@ -55,29 +53,34 @@ export default function TPessoaTableFormDialog({ }; + // Atualiza a variavel de pessoa quando tiver alteração na variavel de pessoas fisicas useEffect(() => { setPessoas(tPessoaFisica) }, [tPessoaFisica]) + // Atualiza a variavel de pessoa quando tiver alteração na variavel de pessoas juridicas useEffect(() => { setPessoas(tPessoaJuridica) }, [tPessoaJuridica]) + // Executa o hook correspondente ao tipo de pessoa, sempre que o tipo pessoa mudar useEffect(() => { + // Verifica se o tipo pessoa esta preenchido if (tipoPessoa) { + // Dispara o carregamento de informações loadData(tipoPessoa); } }, [tipoPessoa]); - const columns = TPessoaTableFormColumnsDialog(); + const columns = TPessoaTableFormColumnsDialog(setSelectedTPessoa); return ( Pessoa Busque a pessoa desejada -
- -
- -
- {/* Rodapé do Dialog */} - - - - - - -
- +
+ +
+ {/* Rodapé do Dialog */} + + + + + { onSave(selectedTPessoa); onClose(null, false); }} + /> +
); diff --git a/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts index 6df3555..e542ae5 100644 --- a/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts +++ b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormInterface.ts @@ -1,9 +1,8 @@ -import { TPessoaRepresentanteFormValues } from '../../schemas/TPessoaRepresentante/TPessoaRepresentanteSchema'; export default interface TPessoaTableFormInterface { isOpen: boolean; tipoPessoa: string; onClose: (item: null, isFormStatus: boolean) => void; - onSave: (data: TPessoaRepresentanteFormValues) => void; + onSave: (data: any) => void; buttonIsLoading: boolean; } diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx index 5064271..6ff1e4b 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -83,13 +83,10 @@ export default function TServicoItemPedidoFormColumns(): ColumnDef { if (updateData) updateData(row.index, 'quantidade', next); }; - const dec = () => setNext(Math.max(min, value - 1)); const inc = () => setNext(Math.min(max, value + 1)); diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 67eba9b..f1b50d2 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { CreditCard, Package, UserSquare2 } from 'lucide-react'; +import { CreditCard, Package, TrashIcon, UserSquare2 } from 'lucide-react'; import { useRouter } from 'next/navigation'; import * as React from 'react'; import { useCallback, useState } from 'react'; @@ -16,6 +16,7 @@ import { FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import { Item, ItemActions, ItemContent, ItemDescription, ItemTitle } from '@/components/ui/item'; import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect'; import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; import TPessoaTableFormDialog from '@/packages/administrativo/components/TPessoa/TPessoaTableFormDialog'; @@ -62,7 +63,6 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido * Fecha o formulário e limpa o andamento selecionado */ const handleCloseForm = useCallback(() => { - console.log('handleCloseForm'); setIsFormOpen(false); }, []); @@ -123,28 +123,101 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const handleAddItem = async () => { + const servicoEEmolumento = { + + servico_tipo: servicoAtual, + emolumento: emolumentoAtual, + + }; + + // Adiciona o item remotamente (se for necessário) + const newItem = await addTServicoItemPedido(servicoEEmolumento); + + // Garante que o item seja adicionado no estado local (para a tabela renderizar) + if (newItem) localAddTServicoItemPedido(newItem); + + }; + + const handleAddItemAndTPessoa = async (selectedTPessoa: any) => { + // Monta o payload principal com serviço e emolumento + const servicoEEmolumento = { + servico_tipo: servicoAtual, + emolumento: emolumentoAtual, + }; + + // Adiciona o item remotamente (se for necessário) + const newItem = await addTServicoItemPedido(servicoEEmolumento); + + // Atribui dados principais da pessoa + newItem.pessoa_id = selectedTPessoa.pessoa_id; + + // Cria a subview (conteúdo dinâmico a ser renderizado abaixo da linha) + newItem.subview = ( + +
+ + + + + + + + {selectedTPessoa.cpf_cnpj} - {selectedTPessoa.nome} + + + + + + {selectedTPessoa.email} + + + + + + + + + + + + + +
+ + ); + + // Adiciona o item no estado local (para renderizar na tabela) + if (newItem) { + localAddTServicoItemPedido(newItem); + } + }; + + + const handleSelectTServicoTipo = async () => { + if (servicoAtual && emolumentoAtual) { - // const servicoEEmolumento = { + // Verifica se deve selecionar a pessoa + if (servicoAtual.tipo_pessoa) { - // servico_tipo: servicoAtual, - // emolumento: emolumentoAtual, + setServicoTipoPessoa(servicoAtual.tipo_pessoa) - // }; + setIsFormOpen(true); - // // Adiciona o item remotamente (se for necessário) - // const newItem = await addTServicoItemPedido(servicoEEmolumento); + } + else { - // // Garante que o item seja adicionado no estado local (para a tabela renderizar) - // if (newItem) localAddTServicoItemPedido(newItem); + handleAddItem() - setServicoTipoPessoa(servicoAtual.tipo_pessoa) - - setIsFormOpen(true); + } } - }; + } const sections: StepSection[] = [ { key: 'pedido', id: 'selectPedido', icon: , title: 'Pedido', description: 'Dados gerais do pedido.' }, @@ -368,7 +441,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido />
-
@@ -496,17 +569,17 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido
+ {/* Formulário de criação/edição */} + {isFormOpen && ( + + )} - {/* Formulário de criação/edição */} - {isFormOpen && ( - - )}
); } \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 37f1d79..1e1267c 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -8,19 +8,19 @@ export const TServicoPedidoSchema = z.object({ valor_pedido: z.number().optional(), valor_pago: z.number().optional(), usuario_id: z.number().optional(), - data_pedido: z.string().optional(), + data_pedido: z.union([z.string(), z.null()]), mensalista_livrocaixa_id: z.union([z.number(), z.null()]), observacao: z.union([z.string(), z.null()]), escrevente_id: z.number(), situacao: z.string().optional(), - estornado: z.string().optional(), + estornado: z.union([z.string(), z.null()]), nfse_id: z.union([z.number(), z.null()]), apresentante: z.string().optional(), cpfcnpj_apresentante: z.string().optional(), selo_pessoa_nome: z.string().optional(), selo_pessoa_cpfcnpj: z.string().optional(), login: z.string().optional(), - funcao: z.string().optional(), + funcao: z.union([z.string(), z.null()]), itens: z.array(TServicoItemPedidoSchema).default([]), servico_tipo_id: z.object().optional(), emolumento_id: z.object().optional(), diff --git a/src/shared/components/dataTable/DataTable.tsx b/src/shared/components/dataTable/DataTable.tsx index 55d5ce8..498ccbc 100644 --- a/src/shared/components/dataTable/DataTable.tsx +++ b/src/shared/components/dataTable/DataTable.tsx @@ -35,7 +35,19 @@ import { import DataTableInterface from './interfaces/DataTableInterface'; -export function DataTable({ +/** + * DataTable genérico com suporte a subvisões dinâmicas (subtabelas ou detalhes). + * O conteúdo extra pode ser definido dinamicamente por linha, em `row.original.subview`. + * + * Exemplo de item: + * { + * id: 1, + * descricao: 'Item principal', + * valor: 100, + * subview: , + * } + */ +export function DataTable React.ReactNode) }>({ data, columns, filterColumn, @@ -44,7 +56,6 @@ export function DataTable({ onDelete, onRowClick, }: DataTableInterface) { - // Garante que data sempre seja array const safeData = Array.isArray(data) ? data : []; // Estados internos da tabela @@ -53,7 +64,7 @@ export function DataTable({ const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); - // Configuração da tabela + // Cria a tabela const table = useReactTable({ data: safeData, columns: [ @@ -66,12 +77,20 @@ export function DataTable({ cell: ({ row }: any) => (
{onEdit && ( - )} {onDelete && ( - )} @@ -81,12 +100,7 @@ export function DataTable({ ] : []), ], - state: { - sorting, - columnFilters, - columnVisibility, - rowSelection, - }, + state: { sorting, columnFilters, columnVisibility, rowSelection }, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, onColumnVisibilityChange: setColumnVisibility, @@ -99,16 +113,22 @@ export function DataTable({ return (
- {/* Filtros e colunas */} + {/* 🔍 Filtros e colunas */}
{filterColumn && ( table.getColumn(filterColumn)?.setFilterValue(e.target.value)} + value={ + (table.getColumn(filterColumn)?.getFilterValue() as string) ?? '' + } + onChange={(e) => + table.getColumn(filterColumn)?.setFilterValue(e.target.value) + } className="w-full" /> )} + + {/* Menu de colunas visíveis */}
- {/* Tabela */} + + {/* 🧱 Tabela principal */}
+ {/* Cabeçalho */} {table.getHeaderGroups().map((headerGroup) => ( @@ -143,30 +165,64 @@ export function DataTable({ {header.isPlaceholder ? null - : flexRender(header.column.columnDef.header, header.getContext())} + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} ))} ))} + + {/* Corpo */} {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - onRowClick?.(row.original)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) + table.getRowModel().rows.map((row) => { + const subview = + typeof row.original.subview === 'function' + ? row.original.subview() + : row.original.subview; + + return ( + + {/* Linha principal */} + onRowClick?.(row.original)} + > + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + + {/* Subview dinâmica (qualquer conteúdo) */} + {subview && ( + + + {subview} + + + )} + + ); + }) ) : ( - + Nenhum resultado encontrado. @@ -174,10 +230,12 @@ export function DataTable({
- {/* Paginação */} + + {/* 📄 Paginação */}
- Página {table.getState().pagination.pageIndex + 1} de {table.getPageCount()} + Página {table.getState().pagination.pageIndex + 1} de{' '} + {table.getPageCount()}
+ )} + {servico.requer_biometria === 'S' && ( + + )} + + + {isOpenWebcamDialog && ( + { setIsOpenWebcamDialog(false) }} + onSave={handleCapture} + /> + )} +
+ ); +} \ No newline at end of file diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index 9fb3486..1d9aeb3 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -61,6 +61,10 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac descricao: item.descricao, tipo_item: item.tipo_item, tipo_pessoa: item.tipo_pessoa, + requer_biometria: item.requer_biometria, + requer_cpf: item.requer_cpf, + servico_caixa_id: item.servico_caixa_id, + selar: item.selar, }; field.onChange(selectedValue); diff --git a/src/packages/administrativo/interfaces/TPessoa/TPessoaTableDetailSubviewInterface.ts b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableDetailSubviewInterface.ts new file mode 100644 index 0000000..b5dcd75 --- /dev/null +++ b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableDetailSubviewInterface.ts @@ -0,0 +1,7 @@ +export default interface TPessoaTableFormSubviewInterface { + params: any; + servico: any; + selectedTPessoa: any; + form: any; + index: number +} \ No newline at end of file diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts index 6b90bcc..28879b7 100644 --- a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -1,11 +1,13 @@ +import { GCalculoServico } from "@/packages/administrativo/data/GCalculo/GCalculoServicoData"; +import GCalculoServicoInterface from "@/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface"; +import { PrepareTServicoItemPedidoCalculoResponse } from "@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem"; import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler"; -import { GCalculoServico } from "../../data/GCalculo/GCalculoServicoData"; -import GCalculoServicoInterface from "../../interfaces/GCalculo/GCalculoServicoInterface"; -async function executeGCalculoServicoService(data: GCalculoServicoInterface) { - const response = await GCalculoServico(data); - return response; +async function executeGCalculoServicoService(payload: GCalculoServicoInterface, data) { + const response = await GCalculoServico(payload); + const item = PrepareTServicoItemPedidoCalculoResponse(response.data, data) + return item; } -export const GCalculoServicoService = withClientErrorHandler(executeGCalculoServicoService); +export const GCalculoServicoService = withClientErrorHandler(executeGCalculoServicoService); \ No newline at end of file diff --git a/src/packages/servicos/actions/TServicoPedido/HandleAddItemAction.ts b/src/packages/servicos/actions/TServicoPedido/HandleAddItemAction.ts new file mode 100644 index 0000000..c642bca --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedido/HandleAddItemAction.ts @@ -0,0 +1,15 @@ + +interface HandleAddItemInterface { + servicoSelecionado: any; + emolumentoSelecionado: any; +} + +export default async function HandleAddItemAction({ servicoSelecionado, emolumentoSelecionado }: HandleAddItemInterface) { + + if (!servicoSelecionado || !emolumentoSelecionado) return false; + + return { + servico_tipo: servicoSelecionado, + emolumento: emolumentoSelecionado, + }; +} \ No newline at end of file diff --git a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts new file mode 100644 index 0000000..531d24b --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts @@ -0,0 +1,17 @@ +import HandleSelectTServicoTipoInterface from "./HandleSelectTServicoTipoInterface"; + +export default function HandleSelectTServicoTipoAction({ servico, emolumento, onOpenPessoaForm, onAddItem }: HandleSelectTServicoTipoInterface) { + + if (!servico || !emolumento) return; + + if (servico?.tipo_pessoa) { + + onOpenPessoaForm(servico.tipo_pessoa); + + } else { + + onAddItem(); + + } + +} \ No newline at end of file diff --git a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoInterface.ts b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoInterface.ts new file mode 100644 index 0000000..c7e626f --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoInterface.ts @@ -0,0 +1,8 @@ +export default interface HandleSelectTServicoTipoInterface { + + servico: any; + emolumento: any; + onOpenPessoaForm: (tipoPessoa: string) => void, + onAddItem: () => void + +} \ No newline at end of file diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts new file mode 100644 index 0000000..5f25e93 --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts @@ -0,0 +1,39 @@ +import TServicoItemPedidoAddInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; + +export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedidoAddInterface) { + + data.quantidade = 1 + data.valor_documento = 0 + + // Verifica dados obrigatórios de serviço e emolumento + if (!data?.emolumentoSelecionado?.emolumento_id || !data?.servicoSelecionado?.servico_tipo_id) { + return { + status: 400, + message: 'Dados informados inválidos: serviço ou emolumento não informado.' + }; + } + + // Valida sistema_id (padrão 2, mas ainda assim precisa ser válido) + if (!data?.emolumentoSelecionado.sistema_id || data.emolumentoSelecionado.sistema_id <= 0) { + return { + status: 400, + message: 'Sistema inválido ou não informado.' + }; + } + + // Valida quantidade (padrão 1, precisa ser >= 1) + if (Number(data.quantidade) < 1) { + return { + status: 400, + message: 'Quantidade inválida.' + }; + } + + return { + sistema_id: data.emolumentoSelecionado.sistema_id, + valor_documento: data.valor_documento, + quantidade: data.quantidade, + emolumento_id: data.emolumentoSelecionado.emolumento_id, + }; + +} diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts new file mode 100644 index 0000000..dadf841 --- /dev/null +++ b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts @@ -0,0 +1,61 @@ +import TServicoItemPedidoAddInterface from "../../interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; +import TServicoItemPedidoAddResponseInterface from "../../interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; + +export function PrepareTServicoItemPedidoCalculoResponse( + result: any, + data: TServicoItemPedidoAddInterface +) { + + // Valida resposta básica do cálculo de emolumento + if (!result) { + return { + status: 400, + message: "Resposta inválida ao calcular emolumento.", + }; + } + + // Emolumento obrigatório + if (!result.emolumento_id) { + return { + status: 400, + message: "Emolumento não informado na resposta.", + }; + } + + // Valida campos numéricos da resposta (não podem ser NaN) + const numericFields = [ + "valor_total", + "valor_emolumento", + "valor_fundos", + "taxa_judiciaria", + "valor_iss", + ] as const; + + for (const field of numericFields) { + const value = result[field]; + if (value !== null && value !== undefined && isNaN(Number(value))) { + return { + status: 400, + message: `Valor numérico inválido em '${field}'.`, + }; + } + } + + const item: TServicoItemPedidoAddResponseInterface = { + emolumento_id: result.emolumento_id, + emolumento_item_id: result.emolumento_item_id ?? null, + servico_tipo_id: data.servicoSelecionado.servico_tipo_id ?? 0, + tipo_item: data.servicoSelecionado.tipo_item ?? "", + descricao: data.servicoSelecionado.descricao ?? "", + tabela: data.emolumentoSelecionado?.descricao ?? "", + situacao: "F", + qtd: 1, + valor: result.valor_total ?? 0, + emolumento: result.valor_emolumento ?? 0, + fundesp: result.valor_fundos ?? 0, + taxa_judiciaria: result.taxa_judiciaria ?? 0, + valor_iss: result.valor_iss ?? 0, + }; + + return item; +} diff --git a/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx b/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx new file mode 100644 index 0000000..60198ae --- /dev/null +++ b/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx @@ -0,0 +1,83 @@ +import { useState } from "react"; + +import { FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import TPessoaCartaoFormInterface from "@/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface"; +import { parseNumberInput } from "@/shared/actions/form/parseNumberInput"; +import ConfirmacaoSelect from "@/shared/components/confirmacao/ConfirmacaoSelect"; + + +export default function TPessoaCartaoForm({ index, form }: TPessoaCartaoFormInterface) { + + const [cartaoAutomatico, setCartaoAutomatico] = useState(true); + + return ( +
+
+ { + setCartaoAutomatico(checked); + form.setValue(`itens.${index}.cartao_automatico`, checked); + }} + /> + +
+ {!cartaoAutomatico && ( +
+ {/* Gerar selo */} +
+ ( + + Gerar Selo + + + + )} + /> +
+ {/* Nº Cartão */} +
+ ( + + Nº cartão + + field.onChange(parseNumberInput(e))} /> + + + + )} + /> +
+ {/* Data de Abertura */} +
+ ( + + Data de Abertura + + + + + + )} + /> +
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx index 6ff1e4b..dcbf488 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from '@tanstack/react-table'; import { Minus, Plus } from 'lucide-react'; import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; import { SortableHeader } from '@/shared/components/dataTable/SortableHeader'; @@ -73,47 +74,39 @@ export default function TServicoItemPedidoFormColumns(): ColumnDef
R$ {row.getValue('valor') || '---'}
, }, - // quantidade (componente solicitado) { accessorKey: 'quantidade', header: ({ column }) => SortableHeader('Qtd.', column), - enableSorting: false, - size: 160, + enableSorting: true, cell: ({ row, table }) => { - const value = Number(row.getValue('quantidade') ?? 1); - const min = 0; - const max = 999; - const updateData = (table.options.meta as TableMeta | undefined)?.updateData; - const setNext = (next: number) => { - if (updateData) updateData(row.index, 'quantidade', next); - }; - const dec = () => setNext(Math.max(min, value - 1)); - const inc = () => setNext(Math.min(max, value + 1)); - return ( -
+
+ {/* Botão diminuir */} -
- {value} + {/* Campo de entrada */} +
+
+ {/* Botão aumentar */} - - - - - -
- + ); - // Adiciona o item no estado local (para renderizar na tabela) - if (newItem) { - localAddTServicoItemPedido(newItem); - } - }; + // Atualiza o estado + localAddTServicoItemPedido(newItem); + // Atualiza os itens do formulário + form.setValue(`itens.${index}`, newItem); - const handleSelectTServicoTipo = async () => { + }, [ + addTServicoItemPedido, + selectedServicoTipo, + selectedEmolumento, + TServicoItemPedidoLocal.length, + localAddTServicoItemPedido, + form, + TServicoPedidoParams + ]); - if (servicoAtual && emolumentoAtual) { + // Controla o formulário de cancelamento de pedido + const handleOpenCancelDialog = useCallback( - // Verifica se deve selecionar a pessoa - if (servicoAtual.tipo_pessoa) { + async () => { - setServicoTipoPessoa(servicoAtual.tipo_pessoa) + // Fecha a confirmação + setIsCancelDialogOpen(true) - setIsFormOpen(true); + }, [] - } - else { + ) - handleAddItem() + // 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 () => { + + // Prepara e valida os dados de item do pedido + const payload = { + servicoSelecionado: selectedServicoTipo, + emolumentoSelecionado: selectedEmolumento } - } + // Verifica se os dados foram criados corretamente + if (!payload) return; - const sections: StepSection[] = [ - { key: 'pedido', id: 'selectPedido', icon: , title: 'Pedido', description: 'Dados gerais do pedido.' }, - { key: 'servicoPedidoItem', id: 'selectServicoPedidoItem', icon: , title: 'Itens', description: 'Itens/serviços do pedido.' }, - { key: 'payment', id: 'selectPayment', icon: , title: 'Pagamento', description: 'Forma e dados de pagamento.' }, - ]; + // Obtem o resultado da adição do item + const newItem = await addTServicoItemPedido(payload); - const ref = React.useRef(null); + // Se tiver um novo item, adiciona o mesmo na tela + if (newItem) localAddTServicoItemPedido(newItem); - // submit e error do RHF - const onSave = useCallback( + }, [addTServicoItemPedido, selectedServicoTipo, selectedEmolumento, localAddTServicoItemPedido]); - async (formData: TServicoPedidoInterface) => { + // Habilita o formulário de pessoas + const handleOpenPessoaForm = useCallback((tipoPessoa: string) => { - // Coloca o botão em estado de loading - setButtonIsLoading(true); + setSelectedPessoaTipo(tipoPessoa); + setIsPessoaFormOpen(true); - // Aguarda salvar o registro - const response = await saveTServicoPedido(formData); + }, []); - // Verifica se pode redirecionar o usuário - if (response.servico_pedido_id > 0) { + // Adiciona o item a tabela e verifica se deve ou não montar a subview da linha da tabela + const handleSelectServicoTipo = useCallback(() => { - // Redirecionamento de página - router.replace('/servicos/balcao/detalhes/' + response.servico_pedido_id) - } + HandleSelectTServicoTipoAction({ + servico: selectedServicoTipo, + emolumento: selectedEmolumento, + onOpenPessoaForm: handleOpenPessoaForm, + onAddItem: handleAddItemBasic + }) - // Remove o botão em estado de loading - setButtonIsLoading(false); + }, [selectedServicoTipo, selectedEmolumento, handleOpenPessoaForm, handleAddItemBasic]); - }, - [saveTServicoPedido, TServicoPedido], - ); + // Dispara a busca do pedido + useEffect(() => { - function onError(error: any) { - console.log('Erro no formulário:', error); - } + if (servico_pedido_id) fetchPedido(); - React.useEffect(() => { + }, [servico_pedido_id, fetchPedido]); - // Verifica se existe pedido para buscar - if (servico_pedido_id) { - - // Busca o pedido desejado - TServicoPedidoShowData() - - } - - }, []) - - React.useEffect(() => { + // Dispara a busca de itens + useEffect(() => { setValue('itens', TServicoItemPedidoLocal, { shouldDirty: true }); - }, [TServicoItemPedidoLocal]); + }, [TServicoItemPedidoLocal, setValue]); + // Dispara a busca de parâmetros + useEffect(() => { + + loadParamsTServicoPedido(); + + }, []); + + // Memoriza os dados para não renderizar novamente + const sections: StepSection[] = useMemo(() => [ + { + key: 'pedido', + id: 'selectPedido', + icon: , + title: 'Pedido', + description: 'Dados gerais do pedido.' + }, + { + key: 'servicoPedidoItem', + id: 'selectServicoPedidoItem', + icon: , + title: 'Itens', + description: 'Itens/serviços do pedido.' + }, + { + key: 'payment', + id: 'selectPayment', + icon: , + title: 'Pagamento', + description: 'Forma e dados de pagamento.' + } + ], []); return (
-

- Pedido -

+

Pedido

- +
- {/* Sidebar - Sticky */} - - {/* Conteúdo */} + + {/* Seção1 */}
- {/* Seção: Pedido */} + - -

- Pedido -

-
+

Pedido

+ -
-
+
+ {/* Escrevente */} +
( - - Escrevente/Tabelião + + Escrevente/Tabelião )} />
-
+ {/* Apresentante */} +
( - + Apresentante - - - + )} />
-
+ {/* CPF */} +
( - + CPF - - - + )} />
-
+ {/* Pessoa Selo */} +
( - - Pessoa presente no selo - - - + + Pessoa Selo + )} />
-
+ {/* Pessoa CPF */} +
( - + CPF - - - + )} />
+
+ + {/* Seção 2 */} - -

- Itens -

-
+

Itens

-
-
+
+
( - + Serviços @@ -427,12 +435,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido )} />
-
+
( - + Emolumentos @@ -440,110 +448,89 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido )} />
-
-
-
- +
+
- {/* Seção: Pagamento */} + + {/* Seção 3 */} - -

- Pagamento -

-
+

Pagamento

-
-
+
+
( - + Requerente - - - + )} />
-
+
( - + CPF/CNPJ Requerente - - - + )} />
-
+
( - + Valor do Pedido - field.onChange(parseNumberInput(e))} - /> + field.onChange(parseNumberInput(e))} /> )} />
-
+
( - + Valor Pago - field.onChange(parseNumberInput(e))} - /> + field.onChange(parseNumberInput(e))} /> )} />
-
+
( - + Forma Pagamento @@ -551,35 +538,101 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido )} />
-
- - -
+ + {/* Sidebar */} +
- {/* Formulário de criação/edição */} - {isFormOpen && ( - )} + + {/* Confirma o cancelamento do pedido */} + {isCancelDialogOpen && ( + + )} + + {/* Modal TPessoa */} + {isPessoaFormOpen && ( + + )} +
); -} \ No newline at end of file +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx index 29bd154..ea148f3 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoTable.tsx @@ -1,5 +1,6 @@ 'use client'; +import { Card, CardContent } from '@/components/ui/card'; import TServicoPedidoTableInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoTableInterface'; import { DataTable } from '@/shared/components/dataTable/DataTable'; @@ -12,12 +13,16 @@ export default function TServicoPedidoTable({ data, onEdit, onDelete }: TServico const columns = TServicoPedidoColumns(onEdit, onDelete); return (
- + + + + +
); } diff --git a/src/packages/servicos/data/TServicoPedido/TServicoPedidoLoadParamsData.ts b/src/packages/servicos/data/TServicoPedido/TServicoPedidoLoadParamsData.ts new file mode 100644 index 0000000..aeb1824 --- /dev/null +++ b/src/packages/servicos/data/TServicoPedido/TServicoPedidoLoadParamsData.ts @@ -0,0 +1,15 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; +import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; +import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; + +async function executeTServicoPedidoLoadParamsData(): Promise { + const api = new API(); + + return api.send({ + method: Methods.GET, + endpoint: `servicos/balcao/t_servico_pedido/load-params`, + }); +} + +export const TServicoPedidoLoadParamsData = withClientErrorHandler(executeTServicoPedidoLoadParamsData); diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts index 71b3dc2..273bdd8 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts @@ -4,8 +4,10 @@ import { useState } from 'react'; import { UseFormSetValue } from 'react-hook-form'; import { GCalculoServicoService } from '@/packages/administrativo/services/GCalculo/GCalculoServicoService'; +import PrepareTServicoItemPedidoPayload from '@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; -import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; +import { default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; + export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue) => { @@ -13,39 +15,14 @@ export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue { - // Verifica dados obrigatórios - if (!data?.emolumento?.emolumento_id || !data?.servico_tipo?.servico_tipo_id) { - console.warn('⚠️ Dados inválidos em addTServicoItemPedido', data); - return null; - } + // Trata os dados do payload + const payload = PrepareTServicoItemPedidoPayload(data); - // Monta o payload normalizado - const payload = { - ...data, - sistema_id: data.sistema_id ?? 2, - valor_documento: data.valor_documento ?? 0, - quantidade: data.quantidade ?? 1, - emolumento_id: data.emolumento.emolumento_id, - }; + // Realiza a busca do item + const response = await GCalculoServicoService(payload, data); - const response = await GCalculoServicoService(payload); - const result = response?.data ?? {}; - - const item: TServicoItemPedidoIndexResponseInterface = { - emolumento_id: result.emolumento_id ?? payload.emolumento_id ?? 0, - emolumento_item_id: result.emolumento_item_id ?? null, - servico_tipo_id: data.servico_tipo.servico_tipo_id, - tipo_item: data.servico_tipo.tipo_item ?? '', - descricao: data.servico_tipo.descricao ?? '', - tabela: data.emolumento?.descricao ?? '', - situacao: 'F', - qtd: 1, - valor: result.valor_total ?? 0, - emolumento: result.valor_emolumento ?? 0, - fundesp: result.valor_fundos ?? 0, - taxa_judiciaria: result.taxa_judiciaria ?? 0, - valor_iss: result.valor_iss ?? 0, - }; + // Obtem o resultado da busca + const item = response; // Atualiza o estado com fallback seguro setTServicoItemPedido((prev) => { diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts index c213bec..ddfe105 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts @@ -2,7 +2,7 @@ import { useState } from 'react'; -import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; +import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import { TServicoItemPedidoIndexService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService'; import { useResponse } from '@/shared/components/response/ResponseContext'; diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts index 0f83f40..a710447 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts @@ -4,7 +4,7 @@ import { useState } from 'react'; import { UseFormSetValue } from 'react-hook-form'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; -import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface'; +import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; export const useTServicoItemPedidoLocalAddHook = (setValue?: UseFormSetValue) => { diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index 9045b5a..34876a5 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -1,29 +1,12 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; -import { TServicoPedidoFormValues, TServicoPedidoSchema } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema'; +import { TServicoPedidoFormSchema, TServicoPedidoFormValues } from '@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema'; export function useTServicoPedidoFormHook(defaults?: Partial) { return useForm({ - resolver: zodResolver(TServicoPedidoSchema), + resolver: zodResolver(TServicoPedidoFormSchema), defaultValues: { - servico_pedido_id: 0, - valor_pedido: 0, - valor_pago: 0, - data_pedido: "", - observacao: "", - escrevente_id: 0, - situacao: "F", - estornado: "", - apresentante: "", - cpfcnpj_apresentante: "", - selo_pessoa_nome: "", - selo_pessoa_cpfcnpj: "", - login: "", - funcao: "", - mensalista_livrocaixa_id: 0, - nfse_id: 0, - itens: [], ...defaults, }, }); diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoLoadParamsHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoLoadParamsHook.ts new file mode 100644 index 0000000..9cbede3 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoLoadParamsHook.ts @@ -0,0 +1,22 @@ +'use client'; + +import { useState } from 'react'; + + +import GConfigInterface from '@/shared/interfaces/GConfigInterface'; + +import { TServicoPedidoLoadParamsService } from '../../services/TServicoPedido/TServicoPedidoLoadParamsService'; + +export const useTServicoPedidoLoadParamsHook = () => { + const [TServicoPedidoParams, setTServicoPedidoParams] = useState([]); + + const loadParamsTServicoPedido = async () => { + const response = await TServicoPedidoLoadParamsService(); + setTServicoPedidoParams(response.data); + }; + + return { + TServicoPedidoParams, + loadParamsTServicoPedido, + }; +}; diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts index 8883f3b..6500b27 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts @@ -4,10 +4,11 @@ import { useState } from 'react'; import { useResponse } from '@/shared/components/response/ResponseContext'; -import TServicoPedidoInterface from '../../interfaces/TServicoPedido/TServicoPedidoInterface'; -import { TServicoPedidoSaveService } from '../../services/TServicoPedido/TServicoPedidoSaveService'; +import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +import { TServicoPedidoSaveService } from '@/packages/servicos/services/TServicoPedido/TServicoPedidoSaveService'; export const useTServicoPedidoSaveHook = () => { + const { setResponse } = useResponse(); const [TServicoPedido, setTServicoPedido] = useState(null); diff --git a/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts b/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts new file mode 100644 index 0000000..6b76cda --- /dev/null +++ b/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts @@ -0,0 +1,4 @@ +export default interface TPessoaCartaoFormInterface { + index: number; + form: any; +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts b/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts new file mode 100644 index 0000000..3364882 --- /dev/null +++ b/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts @@ -0,0 +1,5 @@ + +export default interface TServicoSubviewInterface { + pessoa_id: number; + subview: any; +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts index e5195c8..2c6ece1 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts @@ -1,6 +1,9 @@ +import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface"; +import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface"; + export default interface TServicoItemPedidoAddInterface { - emolumento_id: number; - servico_tipo: object; + emolumentoSelecionado: GEmolumentoInterface; + servicoSelecionado: TServicoTipoInterface; sistema_id?: number; valor_documento?: number; quantidade?: number; diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts new file mode 100644 index 0000000..e451438 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts @@ -0,0 +1,15 @@ +export default interface TServicoItemPedidoAddResponseInterface { + emolumento_id: number; + emolumento_item_id: number; + servico_tipo_id: number; + tipo_item: string; + descricao: string; + tabela: string; + situacao: string; + qtd: number; + valor: number; + emolumento: number; + fundesp: number; + taxa_judiciaria: number; + valor_iss: number; +} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts deleted file mode 100644 index 1ef8d72..0000000 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIndexResponseInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default interface TServicoItemPedidoIndexResponseInterface { - servico_itempedido_id: number, - emolumento: number, - taxa_judiciaria: number, - valor_iss: number, - fundesp: number, - valor: number, - descricao: string, - emolumento_descricao: string, - situacao: string -} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts index cc28f01..e21182d 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts @@ -1,4 +1,4 @@ -import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoIndexResponseInterface"; +import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoAddResponseInterface"; export default interface TServicoItemPedidoListInterface { items: TServicoItemPedidoIndexResponseInterface[]; diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts index 19bcde6..0263e71 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts @@ -1,4 +1,4 @@ -import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoIndexResponseInterface"; +import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; export default interface TServicoPedidoDetailsPagamentoInterface { diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index ca0df03..7111f28 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -18,6 +18,9 @@ export default interface TServicoPedidoInterface { login?: string; funcao?: string; itens?: []; - servico_tipo_id: object; - emolumento_id: object; + servico_tipo: object; + emolumento: object; + pagador_nome?: string, + pagador_cpfcnpj?: string, + tipo_pagamento?: number, } \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoItemPedido/TServicoItemPedidoSchema.ts b/src/packages/servicos/schemas/TServicoItemPedido/TServicoItemPedidoSchema.ts index 89bfc54..4b53246 100644 --- a/src/packages/servicos/schemas/TServicoItemPedido/TServicoItemPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoItemPedido/TServicoItemPedidoSchema.ts @@ -65,6 +65,9 @@ export const TServicoItemPedidoSchema = z.object({ vrcext: z.number().optional(), valor_fundo_selo: z.number().optional(), averbacao: z.string().optional(), + cartao_numero: z.union([z.number(), z.null()]), + cartao_data: z.string().optional(), + cartao_selar: z.string().optional() }) export type TServicoItemPedidoFormValues = z.infer diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts new file mode 100644 index 0000000..e17eb78 --- /dev/null +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts @@ -0,0 +1,43 @@ +import z from "zod"; + +export const TServicoPedidoFormSchema = z.object({ + escrevente_id: z + .number() + .int() + .positive("Escrevente é obrigatório."), + apresentante: z + .string() + .optional(), + cpfcnpj_apresentante: z + .string() + .optional(), + selo_pessoa_nome: z + .string() + .optional(), + selo_pessoa_cpfcnpj: z + .string() + .optional(), + servico_tipo: z + .any() + .refine((v) => !!v, "Selecione um serviço."), + emolumento: z + .any() + .refine((v) => !!v, "Selecione um emolumento."), + itens: z.array(z.any()).optional(), + pagador_nome: z.string().optional(), + pagador_cpfcnpj: z.string().optional(), + valor_pedido: z + .number() + .nonnegative("Valor do pedido inválido.") + .optional(), + valor_pago: z + .number() + .nonnegative("Valor pago inválido.") + .optional(), + tipo_pagamento: z + .any() + .refine((v) => !!v, "Selecione a forma de pagamento.") + .optional(), +}); + +export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts index 1e1267c..3f68c1d 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoSchema.ts @@ -15,15 +15,16 @@ export const TServicoPedidoSchema = z.object({ situacao: z.string().optional(), estornado: z.union([z.string(), z.null()]), nfse_id: z.union([z.number(), z.null()]), - apresentante: z.string().optional(), + apresentante: z.string(), cpfcnpj_apresentante: z.string().optional(), - selo_pessoa_nome: z.string().optional(), - selo_pessoa_cpfcnpj: z.string().optional(), + selo_pessoa_nome: z.string().min(1), + selo_pessoa_cpfcnpj: z.string().min(1), login: z.string().optional(), funcao: z.union([z.string(), z.null()]), itens: z.array(TServicoItemPedidoSchema).default([]), - servico_tipo_id: z.object().optional(), - emolumento_id: z.object().optional(), -}); - -export type TServicoPedidoFormValues = z.infer; \ No newline at end of file + servico_tipo: z.object().optional(), + emolumento: z.object().optional(), + pagador_nome: z.string().min(1), + pagador_cpfcnpj: z.string().min(1), + tipo_pagamento: z.number(), +}); \ No newline at end of file diff --git a/src/packages/servicos/services/TServicoPedido/TServicoPedidoLoadParamsService.ts b/src/packages/servicos/services/TServicoPedido/TServicoPedidoLoadParamsService.ts new file mode 100644 index 0000000..f5c414c --- /dev/null +++ b/src/packages/servicos/services/TServicoPedido/TServicoPedidoLoadParamsService.ts @@ -0,0 +1,10 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +import { TServicoPedidoLoadParamsData } from '../../data/TServicoPedido/TServicoPedidoLoadParamsData'; + +export default async function executeTServicoPedidoLoadParamsService() { + const response = await TServicoPedidoLoadParamsData(); + return response; +} + +export const TServicoPedidoLoadParamsService = withClientErrorHandler(executeTServicoPedidoLoadParamsService); diff --git a/src/shared/components/webcam/WebCamDialog.tsx b/src/shared/components/webcam/WebCamDialog.tsx new file mode 100644 index 0000000..fa61c07 --- /dev/null +++ b/src/shared/components/webcam/WebCamDialog.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { DialogClose } from '@radix-ui/react-dialog'; +import { useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; +import WebcamCapture from '@/shared/components/webcam/webcam'; +import WebCamDialogInterface from '@/shared/interfaces/WebCamDialogInterface'; + + +export default function WebCamDialog({ + isOpen, + onClose, + onSave, +}: WebCamDialogInterface) { + + const [base64, setBase64] = useState('') + + const handleBase64 = async (data: string) => { + + setBase64(data) + + } + + return ( + { + if (!open) onClose(null, false); + }} + > + + + + Webcam + + + Captura de foto + + + + {/* Rodapé */} + + + + + { onSave(base64) }} + /> + + + + ); +} diff --git a/src/shared/components/webcam/webcam.tsx b/src/shared/components/webcam/webcam.tsx new file mode 100644 index 0000000..b11615b --- /dev/null +++ b/src/shared/components/webcam/webcam.tsx @@ -0,0 +1,228 @@ +'use client'; + +import React, { useCallback, useEffect, useRef, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; + +type WebcamCaptureProps = { + width?: number; + height?: number; + /** Recebe a base64 (sem o prefixo data:image/png;base64,) e o dataURL completo */ + onCapture?: (base64: string, dataUrl: string) => void; + /** Opcional: nome do input hidden para enviar em forms */ + hiddenInputName?: string; +}; + +const WebcamCapture: React.FC = ({ + width = 320, + height = 240, + onCapture, + hiddenInputName, +}) => { + + const videoRef = useRef(null); + const streamRef = useRef(null); + + const [isStreaming, setIsStreaming] = useState(false); + const [photoDataUrl, setPhotoDataUrl] = useState(null); + const [photoBase64, setPhotoBase64] = useState(null); + + const [devices, setDevices] = useState([]); + const [selectedDeviceId, setSelectedDeviceId] = useState(''); + + // Carregar lista de webcams + useEffect(() => { + const loadDevices = async () => { + if (!navigator.mediaDevices?.enumerateDevices) return; + + try { + const allDevices = await navigator.mediaDevices.enumerateDevices(); + const videoDevices = allDevices.filter( + (device) => device.kind === 'videoinput' + ); + setDevices(videoDevices); + + // Se ainda não há câmera selecionada, seleciona a primeira + if (videoDevices.length > 0 && !selectedDeviceId) { + setSelectedDeviceId(videoDevices[0].deviceId); + } + } catch (error) { + console.error('Erro ao listar dispositivos de mídia:', error); + } + }; + + loadDevices(); + + // Atualiza lista se dispositivo for plugado/removido + navigator.mediaDevices?.addEventListener('devicechange', loadDevices); + + return () => { + navigator.mediaDevices?.removeEventListener('devicechange', loadDevices); + }; + }, [selectedDeviceId]); + + const stopWebcam = useCallback(() => { + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + setIsStreaming(false); + }, []); + + const startWebcam = useCallback(async () => { + try { + // Para evitar streams múltiplos + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + + const constraints: MediaStreamConstraints = { + video: selectedDeviceId + ? { deviceId: { exact: selectedDeviceId } } + : true, + }; + + const stream = await navigator.mediaDevices.getUserMedia( + constraints as MediaStreamConstraints + ); + streamRef.current = stream; + + if (videoRef.current) { + videoRef.current.srcObject = stream; + } + + setIsStreaming(true); + } catch (error) { + console.error('Erro ao acessar webcam:', error); + alert('Não foi possível acessar a webcam.'); + } + }, [selectedDeviceId]); + + const handleCapture = useCallback(() => { + if (!videoRef.current || !isStreaming) { + alert('Webcam não está ligada.'); + return; + } + + const video = videoRef.current; + + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth || width; + canvas.height = video.videoHeight || height; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + alert('Não foi possível capturar a imagem.'); + return; + } + + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + + const dataUrl = canvas.toDataURL('image/png'); // data:image/png;base64,.... + const base64Only = dataUrl.split(',')[1]; + + setPhotoDataUrl(dataUrl); + setPhotoBase64(base64Only); + + if (onCapture) { + onCapture(base64Only, dataUrl); + } + + }, [isStreaming, onCapture, width, height]); + + // Cleanup ao desmontar + useEffect(() => { + return () => { + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + } + }; + }, []); + + return ( +
+
+ + + + + + + +
+ +
+ ); +}; + +export default WebcamCapture; diff --git a/src/shared/data/fingertech/FingerTechCaptureData.ts b/src/shared/data/fingertech/FingerTechCaptureData.ts new file mode 100644 index 0000000..bea8dfd --- /dev/null +++ b/src/shared/data/fingertech/FingerTechCaptureData.ts @@ -0,0 +1,14 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +async function executeFingerTechCaptureData() { + + const response = await fetch(`http://localhost:9000/api/public/v1/captura/Capturar/1`, + { + method: 'GET' + }, + ); + + return await response.text() +} + +export const FingerTechCaptureData = withClientErrorHandler(executeFingerTechCaptureData); diff --git a/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts b/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts new file mode 100644 index 0000000..acce843 --- /dev/null +++ b/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts @@ -0,0 +1,26 @@ +'use client'; + +import { useState } from 'react'; + +import { FingerTechCaptureService } from '@/shared/services/FingerTech/FingerTechCaptureService'; + + +export const useFingerTechIndexHook = () => { + + const [base64, setBase64] = useState(''); + + const captureFingerTech = async () => { + + const response = await FingerTechCaptureService(); + + setBase64(response); + + return response + + }; + + return { + base64, + captureFingerTech, + }; +}; diff --git a/src/shared/interfaces/GConfigInterface.ts b/src/shared/interfaces/GConfigInterface.ts new file mode 100644 index 0000000..b176765 --- /dev/null +++ b/src/shared/interfaces/GConfigInterface.ts @@ -0,0 +1,13 @@ +export default interface GConfigInterface { + config_id?: number; + config_grupo_id?: number; + config_padrao_id?: number; + secao?: string; + nome?: string; + valor?: string; + descricao?: string; + texto?: string; + terminal?: string; + tipo_valor?: string; + atualizado?: string; +} \ No newline at end of file diff --git a/src/shared/interfaces/WebCamDialogInterface.ts b/src/shared/interfaces/WebCamDialogInterface.ts new file mode 100644 index 0000000..55924fa --- /dev/null +++ b/src/shared/interfaces/WebCamDialogInterface.ts @@ -0,0 +1,5 @@ +export default interface WebCamDialogInterface { + isOpen: boolean; + onClose: (item: null, isFormStatus: boolean) => void; + onSave: (data: string) => void; +} \ No newline at end of file diff --git a/src/shared/services/FingerTech/FingerTechCaptureService.ts b/src/shared/services/FingerTech/FingerTechCaptureService.ts new file mode 100644 index 0000000..ef97f8b --- /dev/null +++ b/src/shared/services/FingerTech/FingerTechCaptureService.ts @@ -0,0 +1,9 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import { FingerTechCaptureData } from '@/shared/data/fingertech/FingerTechCaptureData'; + +export default async function executeFingerTechCaputreService() { + const response = await FingerTechCaptureData(); + return response; +} + +export const FingerTechCaptureService = withClientErrorHandler(executeFingerTechCaputreService); From b2e0d50dd6121ed8245f531bc7c5da27d92b5f1a Mon Sep 17 00:00:00 2001 From: Keven Date: Sat, 15 Nov 2025 12:30:33 -0300 Subject: [PATCH 09/14] [MVPTN-37] refactor(): Ajustes diversos --- .../TPessoa/TPessoaTableFormSubview.tsx | 2 +- .../HandleSelectTServicoTipoAction.ts | 11 +- .../PrepareTServicoItemPedidoPayload.ts | 8 +- .../PrepareTServicoItemPedidoResponseItem.ts | 8 +- .../TServicoPedido/TServicoPedidoForm.tsx | 107 +++++++++++++++--- .../useTServicoItemPedidoAddHook.ts | 11 ++ .../useTServicoPedidoFormHook.ts | 13 +++ .../useTServicoPedidoSaveHook.ts | 5 +- .../TServicoItemPedidoAddInterface.ts | 4 +- .../TServicoPedidoFormSchema.ts | 41 ++++--- src/shared/actions/CPF/FormatCPFCNPJForm.ts | 25 ++++ src/shared/actions/CPF/UnmaskCPFCNPJForm.ts | 3 + 12 files changed, 196 insertions(+), 42 deletions(-) create mode 100644 src/shared/actions/CPF/FormatCPFCNPJForm.ts create mode 100644 src/shared/actions/CPF/UnmaskCPFCNPJForm.ts diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx index a756b94..04ad22f 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx @@ -64,7 +64,7 @@ export default function TPessoaTableFormSubview({ params, servico, selectedTPess return ( -
+
diff --git a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts index 531d24b..c569337 100644 --- a/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts +++ b/src/packages/servicos/actions/TServicoPedido/HandleSelectTServicoTipoAction.ts @@ -1,8 +1,15 @@ -import HandleSelectTServicoTipoInterface from "./HandleSelectTServicoTipoInterface"; + export default function HandleSelectTServicoTipoAction({ servico, emolumento, onOpenPessoaForm, onAddItem }: HandleSelectTServicoTipoInterface) { - if (!servico || !emolumento) return; + if (!servico || !emolumento) { + + return { + 'status': 422, + 'detail': 'Serviço e emolumento devem ser selecionados' + }; + + }; if (servico?.tipo_pessoa) { diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts index 5f25e93..524e76b 100644 --- a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts +++ b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts @@ -6,7 +6,7 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid data.valor_documento = 0 // Verifica dados obrigatórios de serviço e emolumento - if (!data?.emolumentoSelecionado?.emolumento_id || !data?.servicoSelecionado?.servico_tipo_id) { + if (!data?.emolumento?.emolumento_id || !data?.servico_tipo?.servico_tipo_id) { return { status: 400, message: 'Dados informados inválidos: serviço ou emolumento não informado.' @@ -14,7 +14,7 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid } // Valida sistema_id (padrão 2, mas ainda assim precisa ser válido) - if (!data?.emolumentoSelecionado.sistema_id || data.emolumentoSelecionado.sistema_id <= 0) { + if (!data?.emolumento.sistema_id || data.servico_tipo.sistema_id <= 0) { return { status: 400, message: 'Sistema inválido ou não informado.' @@ -30,10 +30,10 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid } return { - sistema_id: data.emolumentoSelecionado.sistema_id, + sistema_id: data.emolumento.sistema_id, valor_documento: data.valor_documento, quantidade: data.quantidade, - emolumento_id: data.emolumentoSelecionado.emolumento_id, + emolumento_id: data.emolumento.emolumento_id, }; } diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts index dadf841..985c7ac 100644 --- a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts +++ b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts @@ -44,10 +44,10 @@ export function PrepareTServicoItemPedidoCalculoResponse( const item: TServicoItemPedidoAddResponseInterface = { emolumento_id: result.emolumento_id, emolumento_item_id: result.emolumento_item_id ?? null, - servico_tipo_id: data.servicoSelecionado.servico_tipo_id ?? 0, - tipo_item: data.servicoSelecionado.tipo_item ?? "", - descricao: data.servicoSelecionado.descricao ?? "", - tabela: data.emolumentoSelecionado?.descricao ?? "", + 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: result.valor_total ?? 0, diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 191117d..ef44577 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { CreditCard, Package, PlusIcon, UserSquare2 } from 'lucide-react'; +import { CreditCard, Package, UserSquare2 } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -38,9 +38,12 @@ import { useTServicoPedidoSaveHook } from '@/packages/servicos/hooks/TServicoPed import { useTServicoPedidoShowHook } from '@/packages/servicos/hooks/TServicoPedido/useTServicoPedidoShowHook'; import { TServicoPedidoFormInterface } from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface'; import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; +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, @@ -58,6 +61,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido 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) @@ -72,6 +76,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const handleCloseSaveConfirm = useCallback(() => setIsSaveConfirmOpen(false), []); // Hooks + const { response, setResponse } = useResponse() const { saveTServicoPedido } = useTServicoPedidoSaveHook(); const { showTServicoPedido } = useTServicoPedidoShowHook(); const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue); @@ -158,6 +163,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const handleAddItemWithPessoa = useCallback(async (selectedTPessoa: any) => { + setIsAdding(true) + // Constroi um novo item const newItem: TServicoSubviewInterface = await addTServicoItemPedido({ servico_tipo: selectedServicoTipo, @@ -190,6 +197,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Atualiza os itens do formulário form.setValue(`itens.${index}`, newItem); + setIsAdding(false) + }, [ addTServicoItemPedido, selectedServicoTipo, @@ -239,10 +248,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Controle de itens const handleAddItemBasic = useCallback(async () => { + setIsAdding(true) + // Prepara e valida os dados de item do pedido const payload = { - servicoSelecionado: selectedServicoTipo, - emolumentoSelecionado: selectedEmolumento + servico_tipo: selectedServicoTipo, + emolumento: selectedEmolumento } // Verifica se os dados foram criados corretamente @@ -254,6 +265,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // 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 @@ -267,15 +280,42 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Adiciona o item a tabela e verifica se deve ou não montar a subview da linha da tabela const handleSelectServicoTipo = useCallback(() => { - HandleSelectTServicoTipoAction({ + 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_total ?? 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(() => { @@ -297,6 +337,11 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido }, []); + // Monitora mudanças na lista de itens + useEffect(() => { + calcularTotais(); + }, [TServicoItemPedidoLocal, calcularTotais]); + // Memoriza os dados para não renderizar novamente const sections: StepSection[] = useMemo(() => [ { @@ -375,8 +420,18 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido name="cpfcnpj_apresentante" render={({ field }) => ( - CPF - + CPF/CNPJ + + { + const raw = UnmaskCPFCNPJForm(e.target.value); + field.onChange(raw); + }} + maxLength={14} + /> + )} @@ -403,8 +458,18 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido name="selo_pessoa_cpfcnpj" render={({ field }) => ( - CPF - + CPF/CNPJ + + { + const raw = UnmaskCPFCNPJForm(e.target.value); + field.onChange(raw); + }} + maxLength={14} + /> + )} @@ -449,9 +514,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido />
- +
@@ -486,8 +554,17 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido name="pagador_cpfcnpj" render={({ field }) => ( - CPF/CNPJ Requerente - + CPF/CNPJ + + { + const raw = UnmaskCPFCNPJForm(e.target.value); + field.onChange(raw); + }} + maxLength={14} + /> )} @@ -502,7 +579,9 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido Valor do Pedido field.onChange(parseNumberInput(e))} /> + onChange={(e) => field.onChange(parseNumberInput(e))} + readOnly={true} + /> diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts index 273bdd8..6a87816 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts @@ -7,10 +7,13 @@ import { GCalculoServicoService } from '@/packages/administrativo/services/GCalc import PrepareTServicoItemPedidoPayload from '@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; import { default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; +import { useResponse } from '@/shared/components/response/ResponseContext'; export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue) => { + const { setResponse } = useResponse(); + const [TServicoItemPedido, setTServicoItemPedido] = useState([]); const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { @@ -18,6 +21,14 @@ export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue({ resolver: zodResolver(TServicoPedidoFormSchema), defaultValues: { + escrevente_id: 0, + apresentante: "", + cpfcnpj_apresentante: "", + selo_pessoa_nome: "", + selo_pessoa_cpfcnpj: "", + servico_tipo: null, + emolumento: null, + itens: [], + pagador_nome: "", + pagador_cpfcnpj: "", + valor_pedido: 0, + valor_pago: 0, + tipo_pagamento: null, ...defaults, }, }); diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts index 6500b27..e72dd90 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoSaveHook.ts @@ -2,10 +2,9 @@ import { useState } from 'react'; -import { useResponse } from '@/shared/components/response/ResponseContext'; - import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; import { TServicoPedidoSaveService } from '@/packages/servicos/services/TServicoPedido/TServicoPedidoSaveService'; +import { useResponse } from '@/shared/components/response/ResponseContext'; export const useTServicoPedidoSaveHook = () => { @@ -17,6 +16,7 @@ export const useTServicoPedidoSaveHook = () => { const [isOpen, setIsOpen] = useState(false); const saveTServicoPedido = async (data: TServicoPedidoInterface) => { + const response = await TServicoPedidoSaveService(data); // Armazena os dados da resposta @@ -30,6 +30,7 @@ export const useTServicoPedidoSaveHook = () => { // Retorna os valores de forma imediata return response.data; + }; return { TServicoPedido, saveTServicoPedido, isOpen, setIsOpen }; diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts index 2c6ece1..1269f8f 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts @@ -2,8 +2,8 @@ import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_inte import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface"; export default interface TServicoItemPedidoAddInterface { - emolumentoSelecionado: GEmolumentoInterface; - servicoSelecionado: TServicoTipoInterface; + emolumento: GEmolumentoInterface; + servico_tipo: TServicoTipoInterface; sistema_id?: number; valor_documento?: number; quantidade?: number; diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts index e17eb78..e9e92c4 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts @@ -5,39 +5,54 @@ export const TServicoPedidoFormSchema = z.object({ .number() .int() .positive("Escrevente é obrigatório."), + apresentante: z .string() - .optional(), + .min(1, "Apresentante é obrigatório."), + cpfcnpj_apresentante: z .string() - .optional(), + .min(1, "CPF/CNPJ do apresentante é obrigatório."), + selo_pessoa_nome: z .string() - .optional(), + .min(1, "Nome da pessoa do selo é obrigatório."), + selo_pessoa_cpfcnpj: z .string() - .optional(), + .min(1, "CPF/CNPJ da pessoa do selo é obrigatório."), + servico_tipo: z .any() .refine((v) => !!v, "Selecione um serviço."), + emolumento: z .any() .refine((v) => !!v, "Selecione um emolumento."), - itens: z.array(z.any()).optional(), - pagador_nome: z.string().optional(), - pagador_cpfcnpj: z.string().optional(), + + itens: z + .array(z.any()) + .min(1, "Adicione ao menos um item ao pedido."), + + pagador_nome: z + .string() + .min(1, "Nome do requerente é obrigatório."), + + pagador_cpfcnpj: z + .string() + .min(1, "CPF/CNPJ do requerente é obrigatório."), + valor_pedido: z .number() - .nonnegative("Valor do pedido inválido.") - .optional(), + .nonnegative("Valor do pedido inválido."), + valor_pago: z .number() - .nonnegative("Valor pago inválido.") - .optional(), + .nonnegative("Valor pago inválido."), + tipo_pagamento: z .any() - .refine((v) => !!v, "Selecione a forma de pagamento.") - .optional(), + .refine((v) => !!v, "Selecione a forma de pagamento."), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/shared/actions/CPF/FormatCPFCNPJForm.ts b/src/shared/actions/CPF/FormatCPFCNPJForm.ts new file mode 100644 index 0000000..1fa91e1 --- /dev/null +++ b/src/shared/actions/CPF/FormatCPFCNPJForm.ts @@ -0,0 +1,25 @@ +import { UnmaskCPFCNPJForm } from "./UnmaskCPFCNPJForm"; + +export function isCpf(value: string): boolean { + return UnmaskCPFCNPJForm(value).length <= 11; +} + +export function FormatCPFCNPJForm(value: string = ''): string { + const digits = UnmaskCPFCNPJForm(value); + + // CPF — 11 dígitos + if (digits.length <= 11) { + return digits + .replace(/^(\d{3})(\d)/, "$1.$2") + .replace(/^(\d{3})\.(\d{3})(\d)/, "$1.$2.$3") + .replace(/^(\d{3})\.(\d{3})\.(\d{3})(\d{1,2})$/, "$1.$2.$3-$4"); + } + + // CNPJ — 14 dígitos + return digits + .slice(0, 14) + .replace(/^(\d{2})(\d)/, "$1.$2") + .replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3") + .replace(/^(\d{2})\.(\d{3})\.(\d{3})(\d)/, "$1.$2.$3/$4") + .replace(/^(\d{2})\.(\d{3})\.(\d{3})\/(\d{4})(\d{1,2})$/, "$1.$2.$3/$4-$5"); +} diff --git a/src/shared/actions/CPF/UnmaskCPFCNPJForm.ts b/src/shared/actions/CPF/UnmaskCPFCNPJForm.ts new file mode 100644 index 0000000..462ca38 --- /dev/null +++ b/src/shared/actions/CPF/UnmaskCPFCNPJForm.ts @@ -0,0 +1,3 @@ +export function UnmaskCPFCNPJForm(value: string = ""): string { + return value.replace(/\D/g, "").slice(0, 11); +} From bc2c2ef3ddd30153c224f06a0da1fec15f7452ca Mon Sep 17 00:00:00 2001 From: Keven Date: Mon, 17 Nov 2025 14:46:39 -0300 Subject: [PATCH 10/14] =?UTF-8?q?[MVPTN-37]=20feat(Subview):=20Ajusta=20di?= =?UTF-8?q?versos=20pontos=20da=20aplica=C3=A7=C3=A3o=20para=20trabalhar?= =?UTF-8?q?=20com=20subviews=20sem=20sobrecarga?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 166 +++++++++++++++--- package.json | 2 +- public/sounds/success.mp3 | Bin 0 -> 171363 bytes src/components/ui/button-group.tsx | 83 +++++++++ .../TPessoa/TPessoaTableFormSubview.tsx | 141 +++++++-------- .../GCalculo/GCalculoServicoService.ts | 2 +- .../PrepareTServicoItemPedidoResponseItem.ts | 57 ++---- .../TPessoaCartao/TPessoaCartaoForm.tsx | 9 +- .../TServicoItemPedidoFormColumns.tsx | 122 ------------- .../TServicoItemPedidoFormTable.tsx | 136 ++++++++++++-- .../TServicoItemPedidoList.tsx | 76 ++++++-- .../TServicoItemPedidoTable.tsx | 2 +- .../TServicoPedido/PessoaSubviewForm.tsx | 28 +++ .../TServicoPedido/TServicoPedidoDetails.tsx | 60 ++++--- .../TServicoPedidoDetailsPagamento.tsx | 68 ++++--- .../TServicoPedido/TServicoPedidoForm.tsx | 58 +++--- .../useTServicoItemPedidoAddHook.ts | 18 +- .../useTServicoItemPedidoLocalAddHook.ts | 10 +- .../useTServicoPedidoFormHook.ts | 1 + .../useTServicoPedidoShowHook.ts | 2 +- .../TServicoItemPedidoSubviewInterface.ts | 7 + .../TServico/TServicoSubviewInterface.ts | 5 - .../TServicoItemPedidoAddInterface.ts | 3 - .../TServicoItemPedidoAddResponseInterface.ts | 5 + .../TServicoItemPedidoFormInterface.ts | 9 - .../TServicoItemPedidoFormTableInterface.ts | 10 ++ .../TServicoPedidoFormSchema.ts | 4 + src/shared/actions/money/FormatMoney.ts | 5 + src/shared/components/dataTable/DataTable.tsx | 3 +- .../dataTable/interfaces/DataTableSubview.tsx | 11 ++ .../FingerTech/useFingerTechIndexHook.ts | 3 +- src/shared/hooks/sound/useSoundHook.ts | 18 ++ 32 files changed, 728 insertions(+), 396 deletions(-) create mode 100644 public/sounds/success.mp3 create mode 100644 src/components/ui/button-group.tsx delete mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx create mode 100644 src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx create mode 100644 src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts delete mode 100644 src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts delete mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormInterface.ts create mode 100644 src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts create mode 100644 src/shared/actions/money/FormatMoney.ts create mode 100644 src/shared/components/dataTable/interfaces/DataTableSubview.tsx create mode 100644 src/shared/hooks/sound/useSoundHook.ts diff --git a/package-lock.json b/package-lock.json index 99e913f..901bbba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@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.3", + "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", @@ -1436,6 +1436,24 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", @@ -1572,6 +1590,24 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", @@ -1638,6 +1674,24 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", @@ -1830,6 +1884,24 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", @@ -1867,6 +1939,24 @@ } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", @@ -1970,6 +2060,24 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-progress": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", @@ -2131,6 +2239,24 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", @@ -2177,7 +2303,7 @@ } } }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-slot": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", @@ -2195,24 +2321,6 @@ } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-switch": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", @@ -2306,6 +2414,24 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", diff --git a/package.json b/package.json index f25f061..b918299 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@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.3", + "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", diff --git a/public/sounds/success.mp3 b/public/sounds/success.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..02417781cb4f3f88e00274331696ab36f483681d GIT binary patch literal 171363 zcmeFZ2~<;`H0%Hh_A@BtO-5&#~x-DazFlG&72#g`{Hw4B=Vayc9 z5Ew(??+_Rxg};Mi?86uWV+f3q!k7(=Auxu(-ytwY3V#R3*oQF$#t;}Ig)ti#LtqSn zze8Y*6#fp5u@7Sij3F>a3S%}fhQJsCe}{lhU%NC`r|@@>jC~kG;HySpEKvBW)juZu z7y|!@z!)j~BPL@#V+efJ2#k@!SFQdr;l~j8M+C-5;U6&>>ls7ft43gq6uxTpj|o49 zz&|1|MhgFk$ym=A0$()(W2Ep^tA9-RF$De*fiY6}M@+_g#t`_b5f~$duUh?M!jB>F zj|hwvH2xzdV?ARCe9Z`q1qxrY_Qzx&L*PF^V2l+015n1g#t`_L5f~$duUY&5Guh4O z#FW=hv8i=RJoXde*h3rJ2+Oxq(gk$~4m1yCAX`jYsEX^~e_oPwbI$xzv%I_K%$XAc zTUFyNVv^>}naAx};(Koub1RpzW0ruEJHk>`6+g}Ix>iA&=qC$tg!yLs738Q2^G&R) z>k^w;M4_bU?*jc1TL48WYv4hD5dnN>;~gcY3^RrvZ}*FB!_%O0IwVeG-}8~t^^ zU8{`T(sN_pn*1jLpMS41n)KQAv#vQB0Q`HaSBrOAo%^2w_^;jnCn;diQ_7`IM1li= ztWg7CL%0-r`b8<-o#b$v!2Z`3aBTGd8we=r-`zb10~la*I!S;9x*8;6s3p0PknBasV{I0ZcxwYnIQHn}zF7XDPri?) z^xX9^cl7yX=aC1CU0WvTejoY#OIc}?^ruMNbrs##JkV_Un?W0hyCsYq+cG8muA5y% zvU z0xV2vwXvSB_;;2ziuZO}WP25^ZsUw#O$B5ryT34poW~f#K-Qx=dFoVT1WT}+q~3elqo4xg<*x%qOaZIF)KX>wP9Ga6Fu511FlmJSuDhEM=|5Gsopi?{9Q0+Y?b5 z6B-%$6@&|BG4N=cof%AECX_HA6@b8^-qO)Ak+&2WMm>PgNTU{N1PW4TkmmwayB1rM=tX+0<}gSrKS}}dasZB zZg&6OMNU7wySV;d%erTm)|EG$$lTMIIk>(0Smps^yu!tzn9|Y_K@!ItzF6_cgDsyA zPT$QqibbdFLo7tzFk#(uD_u{zUCO_(y8g3={ue2{xy$=z+1+PRey_r1Dd|2mTO3VS+%-cS57kx8+ z+top4S^4-qPGoTXk@nL!;)Hj~}q%k!qnm|(pMUbk2Yy#1D62*e# zHBzBOjt|DMQ>A&1!K$I=WSVQ;1cN#lFHw7+9wy@JU;`&RgJhveoxjoWL~0#}G)#k* zr)of;R#Vxm?r7C|xAuFtN^M&DhMM~hY3VMvaUj_MS7(GDrW5MS2*XUmuo=;bBn!r4 zS`eOI-q>0+j0Y%IdHIe~gL0My?-(~pFW2ZnB_wZ7JJ1Bb6aK$N{kXUM>Z03@56T;Y zQ!L0GQ|a{mf{KiV#gX%_%yGZ

yTwWpgj++|C~U<@e&K3uS5m=o7qu+ICZa$$ZWK z(g^<|g}Lvh&R%wY00V%E>JPWw#hG&C+XG}@IWNnj;eUDl*q8r*L_odRz9gCMH23oBc&f49s42IuFid|q ztNgV0(z+=&-@go>{;PK4_W4fb*B(8%)0RQq-0A+y(LhlY4qa7H<8kD>C7Wd|OwHK) zy?V#w51D(NOi8P73<6fwuDOlgR-2!!KD9eylhr4`_tNX1rt5Sce*8Z6+VKbTqC$3U z_+dP~nybe@41oC-iIrT+HpvO{7eCVd)(KSi&-s-=vg6;y^04^&(uKkoDtiKjP{%4D zRmIGhgXKj{z4W?*z<%FnB}ujkcI&K+4>QqV7A+=ot0dvB#X*V0Y(7y-* zZ|NxI~B~i9$vB^j;BY&XKAFT;FKq-_`%jhC6e*$v8AU&5+&(rl=X`> zuq0)(B!->P-zi; z@Ve80#6D*k*W}BopXvfH^-XF;Fzg$omWv8^JdJQO04@luGLX5*=%z3Wi^-;Y!fT2= zHj8axvcf~5Ua%uqp;m5D+1Ty4xkB}f-gpCtOR(3*rXBnX7F z-M#%tbhad`t-J!5+>@SilCeZ-4z>afAIMH)0MC%MH#@5G zySCD4R_1qNE?jTM%W9($zVcyg`+3 zaB6yRg&B7i)sT`8Y0~Ud!8=LC_NZ`2NUiWekP-l2uyIU?+Ub8!_2W&a>i!##uiM&j z4o{kX>*EF8XyY9&%jo&LZZ*od)!YnwGkF|{BQgdE*3>pBfn!c240}LyzqXB^lE)by zB|Qm!xV>$TL*_8&%3pzk`RmYY-=05EEeN<`m-aS{mFqI;$ohj?y77LWi_-C|GwW!N z_0I2WU3$$pb+9Y#>-3bbNa}Jgx~W6x>ZMF0SGMSl8*MYLqu7uZ0z~0dqlKB*P6N?| z#U1Tvarm?jCbdK6z4?GY?YOHbiAh6DJ0eBKR{|9(vY>|J5lbGYNXFB6HhD!Y;~3fN z=5FO*F|!ex8C*j#th*Xjh!~v-!Ac!@xfrOwYNFolYq#|ji zNEr@^HYC#(G!T;ghJ+=U2wJAk&q#zAuC@>l&x67|KRL&?>=Q0(zF$$4&(3iN=H8QPTga5aVXI&L zkzOy>ww3d5{nk0E+xX>q_Q&I=mQjDyg%k7{__#ZOR$iah2v2CJ`cReQwC&0=4ZGFY zSofR5fnC#Q%gcXAdh=bNrW!{sw#d}|W|sW=xqV{r?S()@v=$`#>m);vQI((impFMjt4w-2`4R_D^l>=8eW==uRhX^Zk9@B2TGUR@nZcx32Dw z4_^R#^93}0`~CXwd*7T{Lx+H~cgp=mN>e^ud6vXZPM)6JA#@-^ffO>Ffs|X2vhEnSuFh5TXKL zLmJQmw4iUGeNfO;;+qn-qx|Y%ZMhA{BW(+4c{DOLjUW^To}6urEC(J1G=sAM<_A~C zo^k!*1ovd9Gb+n{YoW0WnC^3DRll^(sv%>~=r-rjlwQyL0&TTtc1ng3^-{b2 zy=bFAU(DA$cbxq_-EXD?W_W@9?Btuw$n;E*G#=}Rd|7kDSychqBWL8Gl{=$ERli+bEplEYtZKKM1FxbEv=J9 zNb@0WFR6nnYq-e&Uh`l$!P9D()n6wKqdOV)RZFS~1HcyH5^G2x3pq{AFcIHCE*hGV zyFw8=-~|8=5fN&LXbO@_Y9V*6Lo!HS1o{L6P=c`yVSC&wxyBy$Cuy@K>sh6$roTwR zz*HY8JUs9^Hs`?9wX20sv_T#xUNR`R%$%p4Z2;XCFcqWT4;ch~1cVCYPlE59d7kOk zI70*Q!FaGi5=|kQ+LtCG_%<>Y0^^MbLz1%r*P9tkjE|P4LL`9E78Qk_{(j@11fu)R zEl&LQr3KE628?2kJ|&&{`LF)J{y+R<)!}Ve+{z1w(uZN`$HzZ|j;W&?0@x)5-qy7s zPMJr@vp1z@ZAPNPJb42?cf$F--5#_-kFPf@{S_n2(WJABv+Y^(oKW6mlK*~LdP*C{d(}x)wzYOIg z8T)VLssA4iCM#!-cR+xbQ^IdC+|ocZv4-H~??U5RdW}bplT0q`^h;m=-tTH~zH_V1 z1$tu0(;p)=xK%$Ms|(8SBHiRbr(nsNE{WBdx1`f1P(iSM8qfv%(@4g) zP#)BmFanr1ID7UEXd9|Y7-@hzlq3g=?5BO;JG#bVH?&3DH#v*+gbmcIm&l4PqX}se zn7^82>u8#$<{9FFmZO1V^Hr_KEuMQU@pkH-Pwz7#KuRou&A&WgMG#16Gp$l~u=2=I z-=KI!79g}A^Ebe2Qr-$rK@tfpX7U#em|hjt+|YEF6yyJGqdnowaE0tsld`#Bc4W``9}A?{bE}~0Wb6cNHmq_8J^op z!^uK4trUV}n$(R|2Mb=rw+9aMBSz!pmYnb|D`Iw>coA}nR85cYGTGo|&e@45AiXQ1 zNItc3B36uirVnoJI^%&*U#pE_3T7(fQ!Se`8$3k23*wrinIbw+XrhU?wOEP;jAUzb zqQ}E%-rY_1pH<}sA9B;iZvt1+*~eWztXt?p@F8!$DjxWY6v(raqrOj9gZZ`3&Udzh z+LD8Y>SguqNLI=F>ZN@{EovhrxXWp9CO`IbRy!6*zzF7WmXJ!b(a~&!fc)L?|!kW((GlrPb%A8#u&;5Pyv7qJktndJ5!*b zQ<1*DE7kE!_ZQL)bOd6c=^aKy>Uk*+*9D{2S4b&o>q7VG9~%p7^S55*AH8NRNflE$ zTeq9(4yor79OXBRZqWh>1j@=P0IE=2JV;Cc(SdlC6B)jfY&PSvZyNpGDhUJT6=c^; zTAW~l)8`LrbFr-fGisphgsQ-;kW5YI#RV-!yq!H2*L}rzJbE8{K40arAqnZ_ApS-~ z+!B#A#XqrSGtqsL6 zEV(6&lGrDeM>f#;2pzReK#dtYa5x%D*DOYF@4xlysbKM82BM#<*;9jxPc)TmynAEH z3hN^?{vrj7pChh)`{RM0sKnFPGFA)iF3+5D?e}E@7e@W(&W^4X^F88AU)a3xzvcOK zI54rV!FTGv`R>2HmWEvT+gowc!EI4YdjQp27~nx)x7i(6#`m;&vwYH?&v>7?ugm&j zc0RL~%NCHLEnp*2aN@lSx{u1m6XESv6%`v6WAkf@4I*{t8s?JTlJtqFvG!kqKz=xz zPB;HG$Mco>Toy`_N-4$iN8wT#LfUK2Iw8-bmQ-%HM~=#c73CwWt!TU2c1x|?qf*-s zX0OnUNZ9rgy0N5BC`DJqtB)n(7iO;B9LPW1LeK_~n(>royg@UbjJ9&&fjr(X<5mP+ zLkI+fOaAgl)8`W?2n;QCJtQF$Bq3}`6IT+AP>Hf!B$iLNpwpS+l?Y#*#uk%fF(%>{ zpXjK;0bDL|x3R?J9ig3g0lz%koOGBT9V1-^`F+64BA3|3BJz^rtGR4mz_T&*NKLa2x`}aTGu)&wml69->U3n>vAN#Y@lp= z?%sR%e)?5FCPH3RduY;5DxQwmAihu}q~H4y<|QZzd`oh~LHp|TMHAU2If^r(?Yy^w zVDN(hJ*)W>UV#Wjh{7LU6WS&E`kZIKc``Qb3ThQ&9MHPKV}x zB~Zd*6n9yfekewGlA-*I6f9K{*E4oikA!j$xMxHK+jP&I^5Ww`Xl?2Kspprtyzrdl zlVI-hEauQJQ59I{R&6^}iW`(X(DT6P`$Hy+bi;`!XYMaKKrTE%HDW!xYCg_L&zr0r z4dJpJ)#UDb2HO<5S34S&7Yo<#axxRY+bf%2zxurH!=DL>kt=!OOC5b??TR*7aIH6V zl(@y#!6a~bO+pU&Hl@2{>7*(CX|aE!0{p6?l<@f_}2Ksd|4Y2RAAgd0~~3V z``RgNn|6zgf%~)9rXu7+8n!KsIZLhOTM@wI{WJnpN!ZaRarAPtlZx#c?%c!(P%_WJ zW~t_In~h8;$s>_)n7e9b&@RqXY=oeiQ>e@=sPAN1S%&gA3}E)cYJq{w7ovwu;u(b; z1r8M@#^$FjZ95AJUp-6?B2MkNF4t<%Zo-W24AV(57#p9rgM{PDT<&OnC$A{F^SiEh zb-rbe6BLlu{=u(COMv{~#~J6BDzCXva_Y@|&9H#ZDLU?dXuAm)#m|LIIK>oZ>ggx&dS+aq0}$B9IjIXw zkx-$jz4wO-euT4O==RQ;x!F%BX>Kq%N>Wg?(3uf~&0lrd2r&%G-@*=}qHKzX*eKq(;?CYjHYeqo!}EsbG*+xIK7E6c(_8-g znkjpHu5JA6m}AAO(P!JQ9@4$?7s3>XQe&2eNX4iTsRKu<2*)(*@;Iw14Yg$Xo^ zl{12iom{YFqG53IbA2AAjV$*eBxvunc@zjdcDiJ7858uG!fJMEYf{Lg#o!{iPcX6c z4$a|b=c}E8NB{HaqJbu21e&4*AGn(aoQ`=%hp_u$ZD>I|?c7-tzp8<_SE0eJOD=M~ z+^cOMEwF9DwKVgB4k-^8s+a&Zscc(#!>w3Y z@3oYU{*j@VcCA%m65J^jB0Qwd zdt`doapus-@vYhS9FKM|n?6ER`p-p#dz-fx8N|%k@EzHWl8vQQhpg|NZgf&V)|g1$ z=)V6F&-jXO`i>ha^kSy5=?g6Tv?z;}3uTxU;i!CE~J7XT1$ z2MrLJGTV+7?jQ|jL=kthlV{G;jVe1fcZE<%d{e`mb482vX4BSB(Tzs0evS#<*U{mT z_Ea1hc?Bap$WQ&CJcraJd0WZNN_ene)P{vz>?dpc<^(8* z2173e;DxxGl0@D$%l0sh8W2G|9ufeYe3Sh;!{fycBoAR3+o53xf1W*VtC5%_3wZAT0jfH$Sf!WVqf&UYoyd1Kx)kB$T;`5%)K3CEzf-x zF@VPp-C4A$@GU6lUn^tt;3ynPpG;|D+Az$UgeURNGTE5~UuUh9(Vt+Z+?dNx%L}nI zCY_*Tt6EV`I?bbk^TZ;=CGK490%#AB1m^yY6oMl3wMz$@m32?gWt@n%`Y_Y$@W+GH z71nh}ABtaDk@E%-&QtXwS~MPvP*OlO>i2AsZnPrpn}GoWrjb^YhgkddGV3n#(LK7y zSGJ4_LDKJ05C$z>?XoJT&r~K@mCdFcYn63zTuSqUKl^O9R#;dgJnx?NzGg=ET0%%; z_I=T4ys!K_sC~AS+I&CIL1Ve$lBSh2K@3A6q<)eLK(fj@QW0iDl3oY#1AX?6%?+mv z*YUXa`QwJrm;oG7DeKIk66mj$?~Y~NubaJtxpPZ1mnh7&nuE3T`m?$31Ls^;Q!*eQtTMk>27DqFVBZLZS*Zrg$tyVQsafY%=L33Sf<`)~i zB0Q-3bnN9F0&c=h85w%glai;P2}Mx?X}(H`;%O28riyc^-Y1?y_BtcbiX@2bamGc?BCmqwL0CJ z{S2HDe*nV}oQf!q!FU9f9V8(`0GlikW)BIXrS8**jiCC)VXQhTn;)N6mvJp`N!JP+ zKP8lBy~p5^Q=X4u-qnF*T;Y=q1Yr2y&AcM`D}p_6I!+%SU%^z2F!?=gFhZ5TI2T9g=Y6{0s7G|K3rB;MBdvU8jX}~k=pX; z_#6afz5Pbx>HlC~BM|_Z7+FnV67BHV5H{rC7s2bm$uROl9)xc?;C`;VYR`%MD@%=M za2F%-TXT-nd_@*zx+$H$j*R;UVge@a>uGdk$DJ#9w`ubCpO;>n_T9g0O#kNQ5#49a zKkZ=uf(PEIMIDdb7XfJ&>Iwll))Ir#SU8c>hK^S>kt%<7==9DxzOt$ic;%!Iue@kp z78DwVKnvi^wsGm0j8_aND;!C3h@R%gG1LfM6oIdX68l~S+7J$j3C2L2L&P+|2G1gB zZYN@eDELzGm3}--1f|zdsuz((XLXwDC2U~ zbIdqpQj=IrqGikWY8>tT3l$?D2pEA)+9XIy>G27@pnK7hx|XyWr+vL{=@-StOc=G^ zrohgjKYO!9*vEUc4+@BOjQef2sqJ zO_5dJz>pAo`RBm?1(%Pd9$pl3K(c5ewa%9~^Y$o=GX+uwlt_?POhMVaz72E06@agG<(O`~<077btb3k%3A66;C*fdzN+F|^T-v^e*$Dr8l1hcYjmI#{! z(cx%%`LpIemCbXiq3XTO9Gr?vFv1`2fsm)_j_eF$^86_zj+u#RuDs3Cm@3=M*bf-$ zKDAVookixYlwK%skFiLZ|Kz6G(W-*n01|j9xmQ#6jndOf>`d8e|CB9Rz5Zf6Q3F9XCZ!EJ&lX4LYbq5eX{KDlo)hB(W&3I=2p2a>?*W z8_BGWD$ld0B>&o#Tmtor0Su|K%&nUj!8?pAzJmdN-}wY~u|FhRN1l9;)r*sWg*FD8 za8AY`nM=vrt*C15j{?D_fZZE+a(3g~1GwI9zd6>*`7?6dOe^@O0PoV1G01^hC?*{iLT@@eVTe?e$7a?y z1~v1L_jr)Kx&O;(UlgL zLY$F+qqL+d0tgspgbPgGd;hhz07%3(Nd?cMs2X*e8s_2)jnrLije0dT#ac2;CKpHo zN&My~d8N*#lMC4yIrTFyrPV8x3r9}j@K80XK~^*4?I#w>I(Q|6f0BZX6!PcnVPs$O z)6TVJqL|BiQh0Yzn8AGf-L)l1)20%f^100`4U5MhK{H@RKG!dq@VyBUwHdJi@9pXu^;Y)# z5*H8Mt+b&N!5x(XUCV{Vte`erwze4&J9=_o(tZF{uWALSuY&MAv?|I( z6c!V+ROC+ZhCazH!LuJfr1?oiF` zSl?XM+BB7BBIIgXO=*@uq&-gU05VvUrcZyOOdFG767ZIIk|kYkh&$MdOEWmH&TPh)FaGeO)Wa7yBA%~ufV#0J7Zp|3#di>y zX%1-`j1)%MWS;-vHlHo>m$O_3v*`Cu5q!Pt|7E%>#di!*!8>Ru&w%?AWcmV zDS~a9a5d7QDfwp>Ev-1er|ViTez1leERu+sPxOb zu3e)|WgHHkv7(Q(f@hVmuWl}{rMWH7IW5kCGMo3xhfHT%L>WLvL+rWN4blADqXTO~%PY?vrvXFYh^d*jkvM^m>0%6V92j*lT3w zo!+EX>zCa7=pG~%|5E`Zk0$^jnhCv4&)3o|~(JY%gnxo1Tkf0;l z2+}YmH2g3?RT?|48S$WF@dn;ZVI~zK1X`eVPpRJKCur1;ia$&PnCYg_ zEMkHwlxpNrW^+nLG(T_0I6WxTYUx|ftpo! zBoUKKh!Bp}`@EBau?_CKW*8Ojvc*-ZLL6RKR3EH^tJwsf8cAPDw3^FwFfasgO3dcM zy^I${&JkVQaiM0^Kq$|O}4A{cn) zNE}j8QZUsqMN&y@VS7Fzq74_@$0^J)Rcq7tS~PW#f$?}a8U&S@HfOo04Q@)3sPJRYy z#Sy_#1=rPl>NRA5JlPUOuD(E4GjlRb`)T$ZMVZp-j}^0fPfj6vy-!lNv3)J7p0C!djWl<3 zTy*H^=(bAAOHvbel~S zT>_v|!2qfeYnX0`ihJvu!K%%Qi`L#Nm=Auz8^}at?Ea;)ijEbsR2#BZ!*d`tQa}X` z#1!z;U`veJJ+Y4fUBWPJB5Fj%0<}$~8Cd;E-n}QBek=rsVH%B12-XU~%RCn#sc#~) zzC3KZ$tY3D8RtQ14%Fi10&%)a-8W)|eYN zWumY*9ud*4PEkELyy8W?E$(a$#+GRATnixMWmpCyyXPFB#0<%{vdXX7PeI5OBN3H2 zbC8U?1o1Z!I!^u&t^8w-z-o?d;XJmND!Wh!1jpJ1wkets1b6=$V0yq?ED?CsJR}iEGKoA zOi6j~+HGQ2{BR3o)C-$tZ4~g3U5RfGY^@Quh#klI2Fe__Mp;vafLAzjyBFG zJzs8B)@+262fNm&C7P^n3BW)m(CYKIwKDyDq+Q5lnTF-%u^Rv$Q%BL7cNpLeVi>Fp zhG-XWD?!UdlR`NGILY6p4_W%@gT72bN&GWXu%bA7eVINKTi5+|Ej-0^D(GX=#21@+ z=Jg$XESiLS1Ofvm>67{41)a36NewI;Uw8Xw`Z=6V=P+~Q(DYZ#*py3pD&=`6&^_|o zk`a@NM4Kdf_rcmrHO`RDVv4=ZAzSJMRaSKJ&CAuqL=XuPY3@OjTU(p8UK86CQN&09 zR;>Y{!znIcZXTF;9tY~Zjk!1}C6&j6ImTT*Z5xun(Y50SdR)TC<)7gRj!(=jofLSS z8El0POi)CyXVC{4wzJLlMxcvnd738lK-wLH>a5I@sVDS~(ptB~=>UWvI{Wfu)nxp`R4U18hXUM%(DF z8KsDfz(yDaBNKBqPDrd515Nd@p58)G3J?_6m!@BWzzWDF3KAN`7ij7d)InGQf)^aM zl4a+y)raQIh*8D#QE_=>CVw}P?11So3}vTc^r#F{+O$Mfq zZXgo^izB8`>={Kf09l$W%;vlCUOL_L70%icN;cYD!}&DuSc@$F^_UXM@tL;T5FQ`3beDR>;DB@pw;B%9MF zoJT#W;YEb@*Vb&qi%xB?WHkSIEaB?n7__O{DZt>)AqZb@*1C=}&=j{)pLe(T`9p4X zcGwgi>JFjwUBC zP$3z*VTh(tF~XXe7DX(0Ql6<=xv_-!44;Sk2p+s!*g}zsT12xn1jOl;oeZe#?Z_ki zqwI+^)$;xVBKX_%;Z11AvL6q0?ah0tSbID=x-j(5NTG0N-HC^tOA!29b^uCKSrPFG zeOujOCF3Y*V#yHkay*uU=HTr3`WW-3BGoc7yz7>!_ChYh@#IF`=VXFWVgu1O6#uJU z8KW_ddSCp51JI%sOh7fNQQys0^OO&b$ut9ksJPI`2~teMb!Z8lG<=6Gj%G@v#Tr^A z=8o%N8*VXhgb3@7{M22_5qHJLZI{y}UCGNTf~C>+7s;6CJrbT;;iw#198I%~u$+sV zZB&mtOiPVob$hrKa0Q!k{C$UEEDMcDWX2`92XBopW7mqF5hFyouJqUb8bkm2B9d|K zmE`IAf>(HKz4A{Eyvr=$Fz%-TOT36i3@9+Aqxw3S3EP9Vc9?viPzrv7Btczvx%*A9t=Uv8qb}@cy({cTTUctU5JRJjJThKwv&_l?J7e3V$WCUlZ?xjr@zmTic7hjTBK z&CieSwGf|f))dev)|gdC5OJ>~vL<^7FhVS%D)rSkC#Hrd?Fn`b@O`SgLxkS!bRVy7 z9MY|_mn^wF6EGkV$(iA&0&|SXtzd45|Hs6W+Jd;e0i*z(1ukuULYu5|0%z=@`lowU ztyM#}&vM(+3dO#glS+l;YbUbY+;Z^o>c~V3LZmi2SUBj1L+4}f)n_n{X@za+VL!`= zZF%7zw)w_wavOY7bb0lv?U(ymw>CvS%&%57$2~|S8OU}{Tg6*DasSLKd%g_r-`d-E z7ZhcbK<)#e-$7Yn5vr=61wWCf+Kg~YE#xFs4aFmIOQlQG#2tyUK(PAy1=wT;TU2jJ zv$hc#+Kcv$??BCHo4GUtLj{Tx38*4hy1c4Tb`d(V{FOp301KtCl|;!_^z}7qfsMAI zqb<9V*qj$_*0jFrwWx9ks`ER7KR8I=$4|LU#KAg`u9n1PN z(YQ&tNWc2jG5_r8t5q@F(7o)pf&F&vx3K+|2@98NydFXOet7EIBj9UTYEJ8(Ik zN3(9%tQjKGRT=gPkj=U>*d~5OZBspZvr8)JVTA8rx|#0vdds7OPPd5d&E z-1u?Pzp3HN>sj<`i~;^RneX9%qxb#Q`k20#59P5)#xfEe=fsn1!eeAcjNxj>8IY9| z6Hnq4Kjw}fF3nH2z-#(d%~H`&7oW*dl?LQu=3=V85;suO#cMlS`>u`Kj?tP&KnQ{- z&@|0b1c%eFywjMbc-}71T*amVMZH@P4vV+47Gnz8){ zQIWF6(>2J5aBp&+W?$Yp_a^f|B1hP~yjqZ*g4^3|wY_AXt~>Nu45q%K(HAr)nip|W zcYV=x_{vXPUf2^{_H$4?2^R_BvJJt-a%HQ)RC)7hRXh*Tcz4l}Pt$OiR}v8=C(KE_ z1w+@)6BR}b+INA1OPVxtM>I#mFoc|3w${iR+0fPYSEzeSsa%SME=w&8Lv-LODu?k3ZGi4!KQIpRBGyky1J%`4^#R(f8LB^vI3R&*M7xUi<%Z-tm*%2b$Xmt427AqwUMmJO}t={!< zH(W$jap^lqtrh+_43(lhebK?NIuMX|_^!&%@nz3n%ViUgru&Gr4-xr^s$W3a`YeJ0 zpzAjt0i}wix=$qsY_sn#0uA0Xa*+?&6hlQw8{aK39oJVRI3QCWa!Z@BFQ?DaW-3%{ zJ?M!9xzA>8{x+`&LM4_VIAy#jVMc8S-p&MJ^W^y_oOQYv{w4GlnH(R)EYVw^j%U5G z<^zROwzUL+hNT<>{f`kuS>XsyxZGf6eDJx#V6xn4tzmGwL7_hE53s8e)lzJUNV=q= zbw{#+Da(I-N>x^uDv%J^ASrMSnNs7e;q=^tVH#F&B^D2yNc4&|O|HwrENFAtgSVDF zLb0JKht>zjoLW-Q2u?Y^UjNhFh1X)QYN-d^b9?xurzV{B1z|yqQ${)+C$GHAZ6k%} zc_qDxJl=@hOft2Fu^&m(<#H#1>yCP1BG>Qm(BlJ(jaW1v;;eHgXK_dZ0WS(dnDf1S z=6jSm_!L$n_583ABo;Ap#$3ivne2$XWtc+y4_H`{=ulA#DGz8MI&wt>6{@9}IJ(P( z0K~PaMWSiVz0Uo;+<{NRs%M2+aeclTnk&SlcN&Mrg|!dx9OFCxA_a@Np`H3jL9;U{ z^!(Zi5$kVt3g2vVI{HwGxLVWGv+hi%cgkd?D09?G4oY%sz~zgsI3O83`dGim?3T>H z*IeTPqMG*Tb@8cTBt7q6-eBoIOf0Rke15HJjCg&CYOfdPUBMH^^`gh@c8 z1ES*8R>Bk@U>FTJg+T<{0TmUMZo?=DDkyEE?GQj|ZHLCOo%vOt)92i~-nr|&d+&O0 zy|v!2>z_bT303>M_s;(A{n?*A>8EAtjd2F9%QxAvS+gy!UHYBXR;pr`h^9H6MKL}Vu8Gd4yGbpg*T*WSD;*l2wQc$odi#Y# z_0vPuox2S=78?_h|DT%pfBmsNS2WlW-I-QHBM>6R4BqhWU-HeI;)q|vUrQ58C zovsVdr8pZEn_A5-*tCZl*869O`$zYi3hcyMVYD$!&{-xZK(fFbK(NHt0 z30IHEzmbxGyw{`@Z_J_tNT05m<_y4*O8`-Toi>`Wv2!L#x3_cLq;duu17P0J7$|Mn z06xmD{b^i|-hYDGm}OBf@6A0rjrT0)IhcVM&s66-Tw_JdxUR(-A*G3%%DMv4@7!`K zgF7~Od+i^-!F}O}sUnCM=uZJK%3(Huk)fRB7^|72J=@43rfPq0v78yEq;W(Ph=OfL zlLI&+04&2|#K3H0ut(Vo#r7_^nIj0o686#~9We?XAUQv+QJInp7}t9e8}qK(dgFWO zD<1r~^}yS>3gaQ0uQKOc;$Z9dJHIOJiu%x&d;ije14i<^coq;^MrbO%n78m@x;Ek* z`Q`j|4q8gsSpZ;ia&Z&RIBkYU3||_PIYuwLku8R_@Q;k~+45FVubyUj-Fh9-Q*E+P z1ZBsD%Bevc)vNx~8USnIr9qIkf?uCFHOrj~$1b+uK;tPVse}I>E z2l^jHodW%?ljpHbh0wb4iPKl(IZyM{RTSs^4%H3fh>@?K(Bx1th3br*1{D`H&2HVr;ff%3%0}oxxbmWlsc1V^5`w{rg z=IrXq;@Hk}Ir<m60kh*B}_H4&Z zwY;kb<5W~#oN?pNm+$#c2(@CDh0|D8!=Za^>H~CzCys|yp0@B)>`^Zb?aRLunVaB1 zp1PbEyP-3yssAs(@SpJzxA`I`D!PC&hMiPoOt;jEjF>hGNVZo~)8lBKHTFWoJol)E zS7ZoB_)WRXBH1DCCs9H$FKrJ`YYSDyXtMyzI1^BoC80!cjz)QTZ02;WW)PI%oLX@O z&nIM$0?_puxI?O^o6)_DDA^4b0|{*=F$XgVA@fx%IE4Ubn0r!Ya#%rvnBM>>)-%wN zmQ&^w_5#tj13Xa~lp{&$s7xyojG_blr4*de+a~QXJg5k2xfuN z1@0_l#RP*&pfNmf^84)oL#JygF;PfrzR{`R3BVwYUk?p{f9@W}WGS65OX`a69F_rm znVcyFSkgwU#H1J#I$%qhcJiBQuqMYA1$dG{=;Dzdz|_Yt=)$A&3v@I~ZW+A)54i13ePuJo>%^uajjE zG0y`rDd5)t7W=S9&ec;^ zcJWktuHU6P#%;NZJ$JA3wCO8mXm4V)BMVZ#jw+=$%s^oXt_1#`$JHydj@}Z#wX>&b zn+c|Ptz<t6h^c%0i=(n02VPOTz-)jG?x*5}7gL|pD(cUR7b5KA7QcmXKt2`^hdU2`P1q(&?4sk*3z>DH#E*NzO_wikyI za9l@=pw2~rqcM^-U!#*8D+#qjyG+`FN0&2>3=qqMusFV)4dH-x-O^w>fr!hpmaN-5U(Z(xXO!vI)!R9&v>s^e7Hg#+NK8O0gQKhz{$q9a&i`_^4%-m!BIIHr1^sb=Ug1av)FJL(@sDV!Fw%N&?J0JzA<;`p z4_eDGjw9fui3_BIt}@B*{r-ZLuTxsMlLz7Q`?a@Sd|+wnl7DA{m>a`w$9g zRq0&1BXR`3NZL{d)Nx#x^t*<4@rk z5`&?0gSWEzpbJKRHMrzHB$$isp@?h_%KJV1ZK1J%TTtH6F{b zqIj_S!@m!CKG&qC@(f|eQOBfe7Rx?28rUouFNU^_Es7do=vZIQ<$sVAt;_NQn3GKG&jDdV|kRxIHr*}SDue9clDAFw+ z{_Oyi;LEcWsY>UD)l0rHRWeFc?SoU^u%=>m!PxP%d*{ z>!nC{trrEX=xdBT#j_o}Eve6MiT0BeGad2QTPBh0m z)BD4kNA>+HJ%0DhJ(jS+|M;hqKi`Sas{NMQ06&|0;sHcpS%N#tvD?UI z7y*VvTOrf#(;kj+fRD_DC6{0^V2ek)g=hpqp`tVrpAm^Y z3k~YKH28PHAjDM}cH;B_o#iH8?tB`p(aP3dbA1{kZiylb#hPa16Kz<6Ktp!d05SzS?3I5vsi!2F<#qwDq+w8dm{}cUJE*=McW9O zF(f)1kYJniDX(Fj+>ZBjZD*58)q^Ki-T0C~%}0?CqY^*DM#`f{+n==5QSmo96m!`M zLN~GOm(nY)w9pMEc?!O?S)x}W^6lN@H>y1GL@V`$iN%?2A>!t>@$qm3rF)g|thC9F z&%JVqYGTwPN~`U7aAS@TV|5(&>aO!uBFj6lb8qg+#y88FhTy<6TqG*)tP?3Q1Ty1ZA3C|{1o5yysO>Eq#KEXAYYu#;i zicWdcyw7t4*3|$Q&S;IWeKdHTw z8v_B%Dxjn$*o2j^DDf1oGMHM|Zt4`+7TL#PlmCnqX8%!qsMXix_!n)c7Ju~^jTF)g z`5P;o-dv4`Db3B1$S<07`}}hYW+7#hkyVPvXdlPxUPa?i7+1do0@OiF&HvIb0%%01 zE-v033oE7m*<7^TG6&xGX$uo@vV&FWLkfvAZ0j{Kc}wO*^GxA&m_I5Cl~@C^3#525!V7N5&SF`T{~I^ZU-`Y47YJ-|^nj1w*0|Pv2=%!~ZiI`0H=~ zg9L6d!;?xMY5}P}2%?CP4*j6I0m=%p@eEp_>g3xLbSZ8*w6Z_IBC$s_#1XBBX~qCB z0TkfBGiq=$3gTWg)V8%th`bKa(U`xCd?l8Me`S^>moH_J6bkP%le+Q@casVMHz4?X zE&!{r8MmrG2<6R$+ZV|fF37SjVwsa5f!4Yss5!m{Us6xnXpZ9fh^_l(hAI@mVb~UB zgDe!j&nTj*7_-T<%dYR7db8K@Y9+Ex)p+0=R2kpwf9t_d^`6xT;N7U;^*Dk#%-HVA z@$C9E$hd*6yppnv^Mb3HE!B%!>|l2p%wzandg%G>96fSsLu03jpuH2X;vyR`MkG6` z=gomDjgELoj5R_56@F|J0LYFXzi6Fv*a8!J(^cX`_guyuU3YpX!=d%8bKLh;Hf2xg zic;sL+fG^sXTX~lQP(@2Dm?bB6UL#roE}rK`2&0Wp(ZzuD?sub-<^^OA0r8a5G)W#t5l6&_lKoV0#)xc0Z8G z8vXDL{O+gG4LfvcIU>EroFEFCX@w5RagXHzz2f2Bo)}Y$S>_>ipzg4*W8E2D0|q!( zaO})9s0!ga6S4Fo*C9|Po(?qT9yjmpA)PR1{DWeJ@;9e2+3+yxDC!i@isYPQFAF>J zEaOk370G^0jBC_%a3f>6YTIE4^BDMKB3;Nl?oAKT z_|^cy3MNFhaV5N*eR&6lvCac{{0_CPjj;kzlu@%tu5^DBcZTG8T1jm#NmPML?7e)v z>ik3DJ7XuyA||Jzz<6or^p?YCDo8#vL+7kZIsIE||Ff@a9BV_MA0O0AX?N^fx-{`r z#ZUi(0sQsrze?aV1$4UdEr^V6)oTd=on}nKp$1;8t7!mkB@e3#j6sH6ArwmEw3qDgvmFRz}2+vz9lE;+xj z$#mZyhP6Tm&KR5Cw1%Y|Il&O%0XXa5W4IQX2%~}WKDQ4NNA(Dh{Wg4CF5!fB%qEfsh`ajcrdy?BFd4UOEXAY`Fj@ zx$*6FxH+Pexy)YCm-|4kr0}u?0FQTx+|}ixB}jk6n_ixM+qdadOZFM%FDLWz#l4V` zDC}V!zvz5o)10KzM$6WV&n>5$ynoDNWrMV(v88Oarcf=7N&2gr4z!r1q~xf>rG1zXJI9; zUq}nvhkX4@tUNyZnYB55e`Ro2GWYYvSx1UaQ4dm0vQO6kJyO8k4(=ass~xP1k1E=r z6kDRkU3!?s zU!-0sU@xSLA{>cej!TM^lAqn{9GPe^~-L*xj8Qp#O( z*^|=(T1bTE_)Agk1?9d3LP@Q8G587Dg=4<2N^B17Rb4J;(Hn`|y{OH-yI0oA6WWjE z-a347>T~?KY#98>=Z^^C{ZA>?rox}oB>;!b78#_2=jB<-AgZzU*z9;k5^}Al+pD;s zaDUusPNS{!Da+k3WWqrx`ug#C81c^wIh27eL%7CbwX2ypXw`hzxJ-|)#~v5bu*kz| zN>1qWnlTK%`uL5JMjvHi0B zMiW@6S{j1EW2IyVzyj2n!8pJX9Gu1pHbGkZp=RBWS-XoCTnqgT%QlNF&1gLJPAoFh zzZkAHR=(P=I=`HmPT05(e%oZXI%jqN%RimM&>z)@>X-ZDx9mfkB^rhb$M6#H-kcYKAj!72sjPyxLI;UM5XV( z_*@-$%0y@z-tTASj+-l9^v_-K+m};@|AYnnb;-X-;P!B7i1+kXik-ER z>&)VVR$cmb-GFQ2bOn82zHM{b9&QGG*d-qf%CqRf6Nqm^$aFEWO3EyQ5?Iq~yHt~V zMZ`)mWJ!MlzbZYwd$5qu0gt$QS?M;-nM#DHV2g4u-Xg?*>^wKBeK{gS_@ZK7BUNx_{@?2i$uK zFj3F@LnD7#uR?gjvo-)I3NnSv%ppP$inhv#jJxfBsy)VgsAfj4QON`bgCOM{5p5k5 ziJcjpip{qQnvH8PHEJ+5Q8FaxXjnOxY}ut;W~MlGXjhh9)(w1n$tViT#3nlYAVtuD zPPOY2bCWUov`uvrblbz1)?W8J^BmK1!LkG(c0`rUVo0H{7TsTw(lzoyA$av1(`Uv# z8SEvYk|Mv1x7^lc+@Cz5OnBw}&HkzG@73Dd>u)sxbdsxe{bA6Or*F0;p)j!$HG@j> zHXC?1t?2MHE}y65%Jxmv?9l@nmQ;(!0pbobHH)bJJU2HB;Tq?1B}dwI+};-%6OCOR z4!U;kEboqXBmzr=Kn7dCAWmDqhMh^n;ce}r$^)*Lmn*?jH?ZbdLhYKhEAPG{g;Yqi zpO~jgR;dKl_1hBm8y$UU`KMDj{0B^-cCs0lw0^^Rv9B9gXIG-@2(mKiPk;PXg-)9F++ zr_d~yQSj1MhE(T%@e;SwZ;fL-C|Gd&)VwmmuIr~P_^fB}uzVRsvR#@;kFW?UUNnHi zlWTW~ml}~QATbdm>0&r?%)tfOt?+gyH*Ah_)F79GOH{q`#qr!tN5rRNvrLMNV#pua zsp~{%RKX0J^t2;QRc#IUo)flPSIpMlTt3p~pDya#cfsv*mvJ!Z=BF#|r{lsCYjHg< zzx?*k1$}hndnV!%HT5OzN2(K47_NhoX;9O3Y3l;1#CDgna|q%p?hvMQY|MB>eFu2! z)ID!H&9g~Z3v(j{=(#dm9fS88gDJLFghNAI_t{o(JMi`u(!FgH$&4Pst18vhyrcWg zdyGI{5oaD{dfmOdW7RL#1VbXq=j?{hBw`zLR z9-sgA*XSpebe_$>HRhuGoclvP5n<;t^!a`SFRlTx3?h+nq`=u>Y(Slm80Nd`>OOC^ zHzqbdFY%F|iZG@m{^D;@wY_7rwS-oC?mCHW0};fnO0PGDEBr||MP%E@W5h@k5jJ46 zXQhTgska+!=EW|KO}#rZjg!8+aJT8<3oUsE7v~=Y<`s9U-_A{cx8=h5(wkkEb?>|r zDy+++^Tx{VoHo7A`1)Gq_Wr+PmvA?I(dxtX!G?*)J2qFNe7X01kG%@zZ!MododWxx zqF?b5rr+ z=4msA&{-aTQ9u8C`~L6z{G}%*bx~?7MW%8bZlmNlO<3Z^ChCDU>j8Ac%Goso<<8by z3P(pv@HJ`!Krupxl^H=j(<<cfZxwt)!4NppJ1^`eR6NNY+|X5T zV_P{(Dmqcyji}cI#pIJ8T1d9u^IvI`nsDI;|98qRns?jr`A6L5lR0b$?vv3WY_F!` z!Z96!=qR&AtZ(@kML4lyPQu681?r+r&I?b}=Lg8-TXHY)JL7AQ0=%?~*?KVP&f*?V zT+JRg3a$@GK!Oe&Ym`dNv-6!zO=0i3ksQDdu<~)4q{hFm%&DgNr2n*TjxlsP=*8~v z<418u%Qu9J+v4w3&WYaa)KjoxJ)`%|YBYSfS7Cb2JX(CR<1SO{Hho~{pMk=8-z!hP zzdU#p=f8f#&mqe-cm1Dl2<69CIB!IKLgqiDKQ*r2xOe*0)Wx+F2dy{_fo5yB`$#3S z+^#Wz0VvnDx?Ib44lB32>LqPG-xmU$a>7s~M< zyyZY|$(T=CevOS{)G+8Tt3NBe$dMH1x(8&U$xya&-x?|8u{;7z+dnA7YV2YLa$|WXMa?Z z<}qQvlrAX3J2O`~!MDJt4lurWKW)6(JlQBB5j@f}foU)on`kSgJl2`(hVnmv4W@;_JJiu~y5+UJ=G}M6 zgwyb3&xkwr%eH>plFKoJ4Oc&{+(Qd0Ny3KqbV`*J)9;ZmxH zib@4$z!<<>iU|qV)8NyjIxhDa+yIzsKq^o%BCLWiK}G3;$jPl#mEUiVG@IJzM(y#i z3MKnByG0>UdY>_`&ho3}ukO8$sHrJ!yU{9QZ8OOgepk`BlV0Ir*N0s5ol-3u#gr>%LMfS4QeMVf)@`gAoqS02XjHYIq z!`gnIk6iig;N;>$x#EQL6WfcycAHZAx47@A4Dk5z*9daGljd8?FTP$?q(49%|2Q_! zHKp0sZJzqD{!gc{%=hZYz1IhI4}()S+zzpl{yk9Wn0D~=)n`%y)ja#OEkGA8#m}H? zjAISm+M!BpLy0NfzHy@ir*#>`AII{Sf|>o&K|R{=kZT6wjji;9!ih3nhdC`9yMm)r z>nYiKudd0~I~~eNwiR3c2y$c~C%OEZ8#gS8lIR!|wr1siI+FP;O^~e}d!JD780;{Q z4Lj`&ZvXjGsWI`;8LV)nk{E3q$6H-$|jhBPOU6T zCNH95+^1QB&mBl2UUnbuXdeSGnvJ&0)M00KC)oMVv*9e6bC(vqX_ok+Y+uB8xhu4e zyi*tdvC?Q7fgLHkKlNro7mvx_Q!&WP5hOMUZOyS=0EZ&S*m-Igjb`4qabt%tCfJqB zrjoaHP=0 zOJJLQ>X)95FWtPAHcZQdr!*%6d5gyzLNe!x8%;{%baig2l9irjZ%TN-*N3G&tmvo7 zOA<{BLw-1zwC**xXV%f3|GeQZ^OO<5Z&M!*oKEznrS{2reuX!2c=b#$Te@ZEK#oO` zl{qP#b@hlzc1s=5X-XGg+u@bS(i`f7+=*nqImaXNljqRGy#WPMNg_QnzVgJ#X=nA! zJkxeXyWacJ_bXf~>HFEaVgO}$AVsO)N7uTG5=}paoH^mRECS`8g?9>YcunX;!olCC zKVCoYTl<>QA1~}!#IoL2Jgq!wgM0t&KkxM8n0N}oed~+n8E@-PI6p4h&>C$ix$AxG zRTBRUno{^7O#a7}pFYCp7Vn&DdfO2k=2d?T0uZcVR5EVw0PU*;)axgWUM6&1S2qwyzKVM6|JCHikIPl0TRrai|B{OgUWlkG40d`PR+eo zI|v?Sbt0#aJM84?OKJt%$ZatRGc@(Mk(io->4Y`y^OcWH?E)OrUH#)CztOztVP{qI z%i>&omXMoMG<2SQV)E+Q#oQZ2wtN~&zVo}55VB$F^OFCfE&O%)pAtA-YaZ|GRL+q2 z;cy5qNYmD>XqaD7-Asu6@P1U00NFRynfUnvm(~ifU^m8rUsIP8Wa?>G_Zm+*0RyB6 zJJ<;x;y#m&Q*zZ5iy#3JTCR>DYxNXaBgNSXx@rk|a3hT`teCm|+(6T-BDOE9RfQoACR^KNS~!Iez7G!DLgG!hngxY-NyOahtmn7(0C8%u8})?-wB{ zY_r>jn9a*qF4`bYcxAk`&BSbRWW(mByX@4HkCN5Igi8r2BgZMnvbqUtPF?%wt$zMC zC2DHw*VA6q0^j&@3WVRI>zyZ6&B5UG<7PF@qmw=K*VmS=yr%I$cMOTiQ4w(zJDCp1 zh1$a}!9P)CN|y+lly-C?-rui z%XRG9)aS~^W#KJ*Pb#qp^s6amf&@H#jR$H%=UtW@F(%U+?5b-QXqhOWB`D2R%h+^* zL@)~Pw9Zk}s(jw$Bp*g}%^ePo)B^SOyYpLB-L>O53{QVZ)0bTciMBx5zQg)0|* zQqjectH}EdpDvADVJpfyN*%JA8wev_suu^C4jWmduL&THMW|v$J9al#lUXK=EM0Qr zr&XrC96UzXR8m%xKTytQGXcQa zI!no`@9M;4si_-1T(a$)yeKE?l-vmmz)IDQIL^pXTX@54zf7)7?)gbXV7cZeK(=zc zv&gTACoh{$W6zkqN4WCHz9fG99`&GS{S-|(>vh0KpZbXjtn0&jAPA&1QW) z|0ntLgQT7R&PJxDg6ka^ia@pjU=U%F+t)!vrSl9Z!2*f#fJqm%60>S!NZE^-k>%;E zDT=$}UB#==k_aou-S-SIMRT8Jbc~&GkAP}VP%_%t3S$T%6%tPYTarzb)Rd?VVMhH6y{LEWWPE;;?w+y_Jnat6vAQ zKoF8&7MLc-PB6Bi6i5e=hx?zfV6>gM7bIutJMn~43e^fjSCicSzJ0*H_6;51dtG0b z@Ob-%J&`p1UCyzW>q53qLyHgLiRfrv5wyeGp$=n^xu|-73E4Kyt6a)8U)Q6^TW*dM z;+Jt$3JA6sSpm#`pNpTlM@ZkBF^^(pVT$>Ag}k}J7^sf0v(oRkS?U^?H*Z6*gI(SP zgZ|jZyxrAWp9Z#5$;O$L%(xfuG8ay!eq^}qYsZ|~P#A$SUKam0U=>zjG=g$&eo6kX zs`g*k|92&DMktCe|5*)4C-Ez@@Z2=6JP61Gd$#SwpWD@A)fDumF?We$B7;M>_gLQL zsdI<7<7|KqgTvl>j#_8lWMU|pEj7@@7?ofg>@1sEIR1Tz=S;{+C1s1)%Oj%FrBIyB zi}pC&$gB>3;tq*bZ^!<)y0l@NkC_z;$I$($Gm$+}4)K5KliO&Tnezp!XZRww+F|y-E4GS&KGPop>^O{&Nn{-c1X1hr>^C<(aR0pzF z?TVkVkI@*TKeH*Zb5SKv483B95tKG=>Gs~%vv^mHnkAV#&K-%D<{mijx3%qH@7Dcu z%Li+zoxKh#+#hb@%?w_*LcNc6{_>h&`>rzi$qvodeGA*4J z$7aNZs}gWjf79SG`s$4P?a27$U*`UKrys{s&`2TIw;wG&=nwZiUXQjuSf<>agBBm4 zb(T)(BpBdtk94uz*CGyWN%zwL2vBkP!THitL=#fXZ2!S>j2uOMJZ0td>N}*LJu4KPL}6~EZ_elP{x!EYJn?yXHE4nn9BXDtKm&zl&F2ue zr!PHs%=o1F?`r>F@B0rWPzn)*rNe0F^rTD$Oqf||-68xedImP`g~kn$HH1H__{GA@ zl%%%8h25MRR4$`XnCQ0HzoN?}SN6W>x-7>)k^9&YRp^wrm$Jw&Qo(8|K?>q;C_sla zDevH3&&jYXk6LG1jR8=mSSVDDiOmnUx$K^Mxu<;?j!Sxclf3j)jYogR9L&pSBiVyp z_unN42XM`u*WBLHL{8mj!gUVY^{4*+p_~UcUfym>h1lg|0AS!`bNPVDm-2(p;8HtB zHqU1vwS1&b6N%qBU^C+~@sPrWWNKQ>!gkb0DVdKR4XY5 zKshR6xS;Goa+_OBXzjVEiq*QNpME8K-0ov#^V4)dZ(Ik4+I%isiT9t4G&X$mW`ARP zjHc?f<=Tu@bT%^v8HgJgkx8*KzmTZzWgZV4q_V(_c0(|^-z$zl!8k86T^qDJC@{Ak z;3@QxSYV@qo7lTpDhio*LQy(fAXsF>C7)kC$HdELavbxcjrrTzX>H7y5=@jk(T(o- zbh}zLIeY2Gf`H`+xZ_YwlA6AZBl>*eSj@G22pXl`j197L z#O(`&tUuN=jc=`RVy&32al@?A<=zskiRr06e1F(tVKBOu-7mij`E;xA^{TkWf@{l` z2w~(Qt<1+A`41KOuap1#B%sj|9WWUHAsC$qPlmFU%RmGJ@ynpNA2eO1B)$3dDaI%sM);Cgnhqd-9vd zIs70uD|(sGeGh@02M~juPXTlSHGb(30uUw#>^uQ{j^NNrKRO{OM$d$zwNf@8&oTn@ zdZr&L1#0VC4lizr@VAMoFT$FA9QYi)efVGi|3*m2wZZU9iKDFtQd3*zpOL>R$YP3T z%sqBfd*$T!yUwuCQFVqAPBxE=z^ZxKCuHxN=#Ei!#D|NWDf3*^?V|!*hoj4GPn7Ki z0@YwOmgpR};}Y0CCP8*V(8JlU-FrEcW1iD7@i(WSz}fkR zjDKI-QIX@nW5XVCM8w@$#hVKGA>|KGpcE^R32sr435iG*0IWqfl6MU>`zoZD zcG%Jh$HUKFwAbD^b}0QI*4Qyw+l;d!&|zsp)u7*j<IznLWuo$2~ z_GVhrQZy1kH|o=DG25jSUUBs=S@Wwi5LtUL%8-%w7$Ji8CXvY-wG?9%pKG)FTZ*z4 zaiP%ICiKMw?I)F;TL4`-L%W&;aAD-&JQUN`NZ{MkTOt*?kbnFBY$smd3a`G=MJ6A@ zNFz0Zn>Ny}m`2k>&E4^5XIGVr3Ic00hafq!M}5MFl8?ii3uZ>W?C4VlqAETtT;<54 zD1ur+2HHeX-hjopL^9^dUQq;X6uL(y>U{}mYK#R>ApZMmqTTpDE zz7h1xhmgT7#YKw_t)pk=LcpQ+EU^1B1my91jPccRJ| z&gsQENNAK;H0@9UhV9!Q*islczI1!@QlVc-ZFC$dQvXf>+%`wQb<2LxTg*vP9n7~6 zF|LQ6c)aC%CSr|%+;@2idyIKmM6QEYpni-r+iR(S+|N|cu$9K)@EB>jG{(`{h@0$% zmuigJPz?iscM-FMG8|lr(nXOg<(V6;a``5DSiD{u|6ny>c&YPyMZ$hKWwrNyQ1zHF zpn&n4F{r(H5~@G3_GW=?bwx+*OV#Nuo2c)BMpw$X@4lj+TY4IHqPO_ZW9Gi8mgbq? z0(ud}ORwksEzzjJE%6O~vaj~~p4`VN8_J?Bb$8une_Ur=J?&up52H!k`g#XIUp-22 zDeF9i^4ic254%8VbI{`hww!>o-4D+mUP7lbC zNsLLk3an<+bbvu4fut^iiKr=jd4^en{pw21Cid+%w^^>`TS~}wPac8KwCO7=KkPBd zNUO>h=e#Ls$)*fEzx!*-BW6c~^YNYke2%H9ms$7zdn*6eyZ%=aI9*B!WR*z(8o1jf zw2vz_P+(?^xyr;Cn`n2mF$JuGVWDL;u+!$4vyMgwylwNu|WF(%B7dJv{$|3*_?4xY_bp+3KBwoGYFCFB% zOBrN5P^dG7O7#)YH|m3)y@(I{V@nz%$mYVF=`r_`%O(+HtEI$0$R`uOBugo*IUq9y``lm|1Jo_~T%eogmvlclh2MSCUSau8I;KTO({+>g(9k>!rKsX7Q*xSlCUrw??0!8u?%8>|cNX zKP!O}1~IT6<#&&EkPbeI=tMGpg(<_Gm6}VVNL0`mbMML#B8kqR(7DH|NEGY47JA5L z*Jz(o*SA~1FyKE`O6)L}JOQJ>?ACry3!q#uYi!MFkc-ZsE6Ap&aoPJK$7<4>k*}&a zcOq>gKMB_J8XWg{%^?;oW&|t)dbT8gyL{6wx9d;JU$Sw6>oEz(r$!SbZ_zo-|8}tF zzx3j>sU}4LG>VhJinonu-kbshz%Cf#4T4Iwngye@=arSDI8PgBP8reGv@I2Gj_grb z7^_UqOSvdwmdd7WnDK!S2V`dZL6o`;W6=+OgBw|cgyEld9XY9=FY0ivYqAz;#?4Y< z@xh!pj1bx@CccncJfLqNBoUIA&MB6@)?Cx*vvQ&$t`wdu;W`VGccaz&GgAhU@f0{r z1pwYCkEcWk6ETO0`+6s=i8B&!?+`NlYTcc*pK_}n*rl*Ue2NJv z8>qt=&+wUqy;$LnIH0LE{ClUEVa|;T%VRl&`-TYfDJFF1GY+ zl`oj!7vRpD?wcNGmtk@vEsd-QX+^S}-FaBh0_5iXI#cnri47+5tabjdI?SY-%EsB+ z+B^skc_=9xSc8i9vHbt6_W$+i|Dgm<(nJj99TC8#YFYMl2q?%2!h?900&i^&%B3AG zPseKpr*dgKIYh$XeSNxtB%p%SY%$dtv{DDY$8bZ>qc%7!%3qX^_>CVr$~qylrkY)w!z30%CJ+ zaq#vOh&NQ}x;0M7G@64pNPiKX7xIAYiv}zrWz;WmFb$1pf@3jxEZDEib%7^iA55WH zfod+5lw`6zAD!tjU0pJR#KI21ix5U4ou1aCkg|=XH&h#6CDw8&nR*So72-d9fJb!n z&XK@f68neaJSn{$$h>&@0hHw)SvxQoi4=`e*{eH2J(-bqZBKiXy?NZ>um@|N<^wdu#6UaGz~W9l{CbC@Xl54H2JlmAyFP(tB3vCv3C3iA{S79y9wnHdD) z%~#nb64-kJ5?@coc4Ck&R>G}l$|t@Z8G_$h#H1yx4Lzb`NNGRJy6~3ywLffGD}wBp z{h`u4Fx)}L^pm;>xL-X=i`#w~*m!$W>XmZOq~_PO>du5*b^kbri!M&Rzn&f>V&v?YB$&d0!#NPc1a1{Tc>k}fTYcgkT;BCN zCkEoNB9tBbk6|^@r4I^C!CxK8Sy#9_C^RMDF4twx24klSx0SzuD_H-N`g)gBo~_0g6Ku2<7BYBol3tDu=}6N?N=J0lE}& zCj1#GV6UN(!k+8;0~3#?l)$HRW4_UtquqMIS2 zwCf%t9VMzu4NMk&--s|QiZL2}i7)a3SRf87e$;HExGlgi@Lj@7yiCQBu)6vcL6bKa zoz?i4jxX)EP!l&+S**<;_sR|K9lM2ceTFX?2pu&u^0y2to;fx3aPa?%E&TOQ|CB%} zL*#VOE(KT))^}ms#U6kOs6O=*M|;FpYxTk&IRlXim<_bcC!7vG=E@_Z;o<#oa&k#GkUT#p&N^7W4TpBztq_rfL2E!}p(#rdmqKa*Ch7(Bac{MbKLd+M5~ zkI}426#`Nw94QyskwSS(A9>gtTVz3kfeis5Ud7^qWjZ!HNhBMr7tQmgnSz+0O)k!4 z1^fEpQThA_oNTm_8qWqsF>rH`G9NFEWYYjdx1}J?FD?6V#A+(v+LJlPA@QyDx|bZ7 zk<&VP9tQ;aZRIzaj)ui)u(Bja;=J+ss)#=Tpi ze(Y*}X}F8~R`2OO_=grT?Ny4Zv5d)|afRi@r*GCu^LI{7O&n-4nfU?0df%UA6Y1Cc zqBy78qHm;3%X`s&e7c8}s{^7q_)gBBc}5#FQrLG}zjxyC`VFU|E!@$RLTbJ;a}Szl z97_D;%Or}efm~o*m;>@eT}mziLnlL4q7Dj6FMrUp6nlB3UayyTNcC8=Kn+K^sNnx& z?>(cMTDP|0l}Z8u5(pR&Foa@2kOTsPq9!y01_)I^)KH~~G!?8Xg&vTm0-}Zv3JO-R z5jr9uO~npU#NIddd~2Vxzklzacbw%LG8? zdEZK85uO~-jC7RRz(M7rHFSG$eS2tr)pNiwu%y6&oxjEjU#&->D#~Res#nw2K+4g< zYayL@=I=}TAF{x`Ci$p!29WgOb2to1hf%E~BF5Da-!j$UdiIpCJ8t%BRdTG!;iwhj z{2WiTj#OSZI(0eC%h=|o(UvI8jLhuW#%1Qw7IF9Y6lu=Jv@P7nY?0NU)n{YOH|7Is zA!aC)qy$+(PNcM;j*tqbDojY4h9U%+mob2-W4}ndd7j6%;@t>VR1m+V_c6a`9B0E* zS%s<@!b9$rDlB4+$tbyen!Q5~P{qp8Q&xP?ZNDh~fZ9`A6!|DT=R4gm+Q8GKm7KnDBX_P>CvGcPLITNlkT0 z$5`g7qCh>$Ig6JAc4W_TNs3UMma&3FMMgN1@(xjaFR#cI;N)Lg3uk(R3KifkFsWU+ zwDfu8U84^DVEfD^B>9rg!IDJloj#eLUoZR`xPA;-b4q1vKW%Gv_Uofy$y(WUc84ypFpr000O|+;`qnSbWA|I@*{a~o4Ry3jueK23D2>1jl7wMIl)UPsL!j;CWQg$Egv1u+uaGF|Mu6!6-OMm=4A$^OS$q6o_6% zgVB3Oc{z?Y`?j}m2vxOaxrxqp+@jgfLqRm4I>Jd*PUhRau+Kir{iz@Dxe++;C;nb= z*L&1UBXWnH^}x*&H{ajfd9Fyw3OM4v;qCB_2ZoVnWWRSX{f92~zj|z=xih(FoCkbT z07_Rb_#~o%ob`4VG@v`^y{XZ#99kWY6Sy`UW{{Z4np8L%CH|zbxn1R>yr|5n?0EfU zp2&aD{wbdJ&N8F?ZB)vAcE?vA@} zNB?a79F}nDcGHzkN8fY1+O9sX)44ywqK+K~;k$JG-o=rd%+)4#buJMYo{WD~940Bq zqml5PS{CLQ&-b9Qq>qhD(BuQ)G&5<9zQUPGW%U7YsRjlw^3f#K#*-C2_zq4@SoJdN z7YaDS=4Bfe5oWTyIHPl8_XC{HU%K~6IK3Q+RUf|?yI=Fsiy?~6*^L&L&iq8BYA-`A zY^1RDRC1+&eCBD7c4;#yj-FYOWs{FDbuBqEPFn7J5JNQyziO>yhXewh@0bTh)9@M$ zl7_lmezWo2{yiky{iK=4su+Y6Zk(=Og;VFj%fT~qey2B|@71(jSmiN0$HhM0c_PJ& z>h_6duI`h&ns}n5@sM^b=OGv}7W>3Sg2{1v(b)E*V*J*9bGB!FPha$OU*f3=f9duuL(6+>5a7nr-G}8a$ag-Frjvmtsk)n0tx|)n>4Ak z;^|38=Z*H26mB&z+Vq6lZDB5Wy^j3VejyPb^iNP=SQ!mOm-TZq!kmvq7jr!J*3ugn zoSnZD=kl*j(KNdZ9%Xx9`jj#@yGzT&%jKB5&GEBuA2uC*k^HZE-|x@=S6ZOi&O=vE z3k29IJ2RQExRcf-Dw^01BJ5!E<5xchoo#w95`|xDJj(UOt2>Mk@OP`>OpQxJH5#zS zl!Q~&?Kyp0f2~X-dd4FM385k&0E-t>vnigh&VJ_BJ3Q3=yy;NtL~X+1q}S&PSCcM{ zWXIclIo4lvVa4jG(v6!Iw#eV!kKVg+?{faH$LG?i&!T$G*6}t%(&bGq zQO6HCII}9Xxn1jMOoK!ZjMQ-D>7yJSI9tJ7jvof4d3_13qBiV{l|)N8yXutD6!=W#Ec3Nd z#aitZ)UM6deW{l0uCB@pL4>lHuNddIj1f$h2>+9=B z;q)JN>59%MDQbFt!0Vuq1!AcLisHpu8VFZy5TwR=x0CHP2{@B*Ml0Egh{G9FFa++- z-l`gPuqU`f-G)XnIgOFm#Ejl+2|-J?^LjPm#^)UWnLg+{{0R!9!J(PVMfl&PwbWq1piSV);e~tJa0W=Z!+$??CWC#bR+m*>C?Yo>R+_LZgO#~ z{v#fY7;9**hgLau9Vs`pX;M zq#+IwKmf@v+Kb~NG>WAfIGm6*jfDBU2&8tEwd4Qj8gk=g*4c6`PET3*ANhPMEHTzm z;rbm={Kv(Y>z}pezTDYWa5O<_*~;_L#s@;}!s^C$f`Er4VK9l;^@j=Ez#3E0ojPJI z@xET8K_rcJh2hVgEU$#|D?GEc)#ifn{f&OUO+nV(BHCs;QZK$}>YfcWQzmca~th0%!|vSH?$6imL~nnU2l|RG)l%UmKgAc0l&R zCEa1Y+)J`6tsp@Ppzt?@AGYS~q)n^slADa;bcv=jb;rs5aN$vb_$t+zMz)qp^Xs@s zaJxP4tV-wI#gJ|oKBxG*SspJky+&SWTw!Y*hq`}xHjTt$9N+1xXis0pB_8_}L0f^P zEB1Z*`C>_R<4u*LR?E6RoXvjTc}6CCNZ3EY)E9Xr0Ps4A?I;{su)HPnYmaTTQ>woP z(#acIGu>KGlN*pr^9Hz%C(aG;YWwv62g5po3L3l&sf<{ltmFo@!h}Ze7`^b-)Vsh zv`lifff4(SZl zThW53pa7#fkT85E@ij)@^^v+zPC{D=k%VS=WZV@MzjoeUJVCpm`o{j1lR}%gPJCJ1 zlJxgA#=e@jwaFjl2q)Kmw3;p++u2>ee??PTIbT}aJ0bgF5mRtuqXE*>qaRQP=Of9} zEA0x=2)dupFaTb=_#D65ZgMIg0B&Pc*CFq&a#JIfyx)wIKdN|NUon80XJ2Ivr}x>; z)(=m;56|FZJU6TLiw)s;IXzLPo&Ga?O-{khGke_j#ev?2OKFemKmO4_taZgxD>A;O zy6RZl?!$uRKDx{2zseS9oU{CG+jKdUQ3xYo089$B6Zm%Q7s1Tt`d55PK|8YhnT1C$ z$TU`4B%Vpdv6R*ACyi0^!cJIgUMKLbu6&vQSponT1$DB6(xvY~AozoW+E1!!M0f3G zYVfFv;)+fIZ!iICvia#9~O74Xi!Jyx!<%_S^Vos1^{|O4jDkEqfs)LtV?YwV$35SsU zk4V7|O`O|tJ(6y7Imhw5^Ch3Jnaf*U)JT}%eyAr(`4!HmYQP76y_4MVzDK z9bq1n0+^0lT?i^RUM$r9T07!$#>|TadwP7`=2AW{6r00 z@~Iz{Xoz!mMf;Xw4KiTiDsa5O6;z~`7v0yXvc$ma9IUk#(c8a_zDP&%{;AN2JOP2i zv3{Po+9n191(o?)v(pPk0u$ow4k%L4t3mKF)3U3TH*&r*dI(d&(P>8B$08q}_py-74|jD%4HM*R?VUL}aE8&?TOB+(T} zM_PJ2I~{Q~mWJ}h5lJzGcp@{|Q&KD3KBL=&CJl)^Pw3YCUNU zpN(!_ts2$}OR(O4q~b^p)in>H)*6L9uw<3R5)7|1FmCe5*PiranHjO4V+=e~aJFyO z1ughO#RdE97ZuB8O&+-y3?+cAXBvx@WJ8i-3r}gHJ#-2@o$R&9ahs7a+_R;8r><~) zbJdb4rQ!hH5^0(o;bkZphv)Ob=ee&^GDb3*(%MupNeC}MioYV1t#fdZbJik_E8-}Rmv|Dwaz?IE;eg*Py9N_GQYvF&CZK=Q-n z0hse0j%nxttvXEy6Kh>}o8Sb%q2xW?BZ;Ko8m2{KvOCw4TVlqD_Bz6QCWiW=k%Ej% z7^LcYHQy4y9UIug0KPqvlD9fva7%S(L*&;zby`L-e`OE5jQU?fn2=#$vu%4ZhdWnsQY6k@xE@4T)h+NB7Wi9)eJV1~*x&FO6}_wGl3Gp6q}h$N2Y#KAx)3M-!%qDB z`2Sf8TxcS&RJC}33m`87hZQA@uFAtmy30uk#&Yl)E#(?VMIqS;d|HxIcq121WvcMV z=6V1pM^!8b$K^0MOXby9tzPGdqyuzo$0)i9-1R*Gt$i`UifZ%l@#pksT2A*TTrj+V zz9R2E-?QQ4AB`thPBOd}2aQ*^Z4UQgX*M5!_ADjX^5|L}=V+PCJM+6?5nP_7stp2F zo23%xY`oPh*TD*ttZ@i&zGQ-<1nEES<;~-57$#`45~<5AR_L6THaA-*MwP(VSOJqH zjLXi#5_%axq9`7O-Ko5Mj3v^8<#eN~vot@|$0V%$U|AjJJ8|mx8~Re+V-y$4dOe~l z(Wkoj&1c($<)xu2&`8ObHtGJ-&o*p0F5Xi&|vYnt;^UEN|o z#*UzSSzxDR%uX9Oip3GDzKGGmPmRPtxO$aF5wZOFCzKqS+Vf#rOi9l|E1-)>_%vq~ zraTR>4RwnE7RTfbCW%&s?lW+|smjamVHbO>Xs47CN-pOt_eqZOSyCdIW85+Dq>d8= z={dU1Ph{V3>VMHB3j&^i_BR%cQgNyL3`IGyVg;;o1wwbaT*zd_!7?hGc4{iYM~j+V zSFk^INT$6^0r1c|-Q6($ISu99`B0onR)+fh?^_(Xl$9WeCj0{wpi!kivxiY`_CGIiG|%6jv2bxS2lFLz7)_g3U8?APR7^~{;v-}EHxQCsTO+7AWO?1F04EoIqbQyupZ z1B%@iC}+rsixUC<)DEyDNZ=81%#uKJc^{pvc;Z-J#{g4`OdqaI=9A}ABL%JX6so-q?a#j#_U)sd7h;Z zufTOJ{@Lmk;FqxLyCs}fK+VqRel8^NtQzQ>G#H-K)=u|+Hls`MQ%!FEB$Cn8H ziWJ<9`lt7H@G`=TVxUMtC8^f*|IZ$-hf&XBk?U6`SK5ybYbLm`H92Xg+>eaGJmv5$ z$F1U5WBJb>X06od|9I{8Lw(Y;?As9Tcdqci8WI3#=Lmt9`VPk2OGd(hgrChkvJRzi zAPhFZlT*|S93bndO{MXgZ*t-^(K_;StV(3Jg}*`}t{Y({(nHuo1Cm@t8V<+lgky)Z zk}_7AZj{s)>dquEt8G(#YM-+;kH@)MT<#29@i_C)^4j~B5x&b$eOb7<&4KKnP3P>| z@TS>{rm%Ci~D(_2Y#Kh>fSDI~zNwDjvV)sT3e+hf=5R0&86QXG4eRhoe%o?LVNDI2D?oHGC88- z=fX_EfzcJ}Drzk>XHtOhm{ zRFIR)wbS%(KkX=XzsM$LJ zH;>t`6J~ONE$Khb{8eWRG3pPw*5R3BU}FRAj8nnXE<3#oo3#WgDL^!ZrOC@ic(%oc{PrL0wX4;xiXD(H!->r;Kw!D+GF4$dseVwm`j<`T=!|l7Wmq{wT|K*qB|Ml0; zKZ`N2)aL;kYw#u%NNE>9QRPaf-zB|+iM*ZQ`$!rqYtTsb;!C+fx-ZU{Vez3;`b+Wc zlQZu&=mH}cBm6*Bk#)&LhIv_Pi>Z7h{&~Z?xhk(P@Gb3dLM#5MJ1wA#|2qE??2QXA4Y2iZ) z{Ksq3oEMSyt_UfeRHPfl|c(-ES(f<*57gYOhu`XSynXa zK+k$~Az}P=;F__XU*d{|0|#XOTjkgim`Xt`RB>Tz=pxgYsxZEz9F0peS8k%6SEnIK z<~;7I2`%f*{vpdT4*5=|WJ^$2GTe)R3W_d7B}N8c2o5({t0>VdlE3GNU=dO>S;CN( zf6_e4>p}__FB-D;Zmt|7ua2ZyT?+EO&{OSbb>m5L9Wm@>k^Al(zr)`3Yd?IK?SAoq zd4H|!hrz7Cptp1s*L1&t26)wKe{u&UWFQ8AGPn!9NPmCoB=1T`Xz>x?`I z6h0bdsS{^EL+OJOa=-*{*$=j0fxJHR_GvET3g+D5+U?8Sz3NWos+A!u3H!I-h`W?a zyFC@X#Lno%7(IFbf|9m#T-Kns@bs1SB^cVd~CpGEnV8~=Mi}6`J2-v z8GYRDG`Yju7Q*xU)E=b!C+e5(+ql>*J@RgRZfseb$w{9}$G+WpcW{6F%Ykln{S?_R zf+aUXB|!?uR0WCwbsgX9dC*NAJ6qA8UED{Q!Isj=JV0DDz1p>F6T2~?E<$Odt6z%h zsH)~|?_X;ihrMxKB1WYtHKn@20g6JPh`+8OApuK_ev0?LjJ~3H{%}dHXUUff$g`t- z@=9j~>wT}8=3hhIUR4Y=xVD|tJe~|q8_&BvqUh(b46J!%*Dhn<;P(0R7^2n0yAxiF z8#b|p&kb-9I2v#7gUg8qVmnE$_Ll=95$j7#K+>|i^Q)wZX97#T(yZtp+W|2eDZ!-ccu zD&O2?lJ3z!8cOb37mIn=D}OwQ57%$c5(9?82WF_jC25*zZIC1!5H zsfjRJ6#PlFIS|3<4IzAOY2^5EJY4lZxj9@uY-w`n{6yg|)HG0;@BM1^mZHm&j!pQR z*yxJk{kLQf>yAI?sbl?f^;wv+UU`6;Uowf`)n0|8%?I`qEYdQ?#`mlys(SN&ELTjG zo--NM(--l$29QpeQ<41KVx*HsmZ1(`AW^W~AZ3y|&$hgKI^RVGh^k8kYNj0@Pv((O z^cEf5OG64)FDPqQhfYoZ*!KV*JpUqGQ^eN2yo$8GZhMH?u?wr<{$NOS))6z=qph%m zA!+_EeSdjV0^R{lftOq|>x4i@=Q+C9V8Ak3%FMS{l`qcrx7Wy%wH75Y?giJVz(`t8 zA%<~5&(^I6ipN1)>~i@%RR6NUaF!JMXC9eZP+u`QPcSH$8B_={dHUmyJtI0S=gv#h zV_}~X*4N;dMvvb#U6)ey^PY~j?58K?MdPi#uf5Nr^RbMoB0$x3 zqqKdvNeOmfAQNsq(G=vF;_ZCh#Yq_>MsXe-uxL_L!t5YpN@wgoVl@YrV@zQJri!Hr ze)-^5SDGJ&oLn6fwDDob=wFe-9tae!b_5sB*1IM`qYt9yrOOkIvS^1P+14;RB-$Bl zk&;{O8%x`5eP-3IF-Q#J`q>53g~V~d^rY}wY4pXQet(~@p{E~OtRA=~y4fa^rPPKT z^V&2sO8!5%-0vU#w_6~ef`REn+@+#ZgA;Z|y$S+c=sU|~Zf|O3%VEw7CRHvgV_?$( zdPHbOgf5UN$0<-(NL56NU8!nCW*|PW-5&9>Pq&;zCd!4}V(n{Z8LXhyaiW$@v`&tv zYohoDx#r(0vivgbM@r5mW-YPt9Bhbu{rJMMmr3f=i3hWxX+GJz#cf4;Pu`(Cd10s3 zaPnZK&FQH81I0&SS%l+B){6*BZ7Lt}p2AJ_F~G{0QE0;>+G(}gy6Qi6Ebd7NNh&$9 zXsZ4EAL=Loc-oNw&;YHHIWv!((hpao<8?{{=^kG22#LH;IG9P# z;tju}^bDaQ0B2(JF87tDfuMey69sQ@{P3FR zRPM#%(; z>NV$gx|kKN!gzTGt8@1WdULXr`Oz@TwN=q#qDllXVKgT1R!E38%mIm?+LO*p#_-~F zb2isDvP*5zXc(MX1?ktY@v8QnOT~!wz^Uk=yGs@w*HcSgs+)C|+TgopWA#4!738WS z2oxaQB_hRWeRNqzurSQuA4(s%U@fGvf)$X6vj$N55P0lXY3Xau1!q^Sx3XVvsjog| z{I`4c@AvzcEO5~-FRIs60>I&m{E$ktCRYWPpwsk~_#vJaFhAPc`GnLCj$yLIl|$w< zkf|7?*m?sDN2->3i2Z%M0vnRqTD)D6g>x0`b6T)<%&B(Tl*%I-jmXuCFlR=Q9p&Wx z6(9BQDtVr<_U*QeqlHJ;DEQnx*min++}(Yq((lQY-Iz-|o(3-*kS+AQSh##=gmuc` zA_$03Sc5Vp12#9#2>f&ue@`;5u}$}o9#+8P4F`vn7>6?DfaTtw=`qV`QZozoE1I8b zNCccU$wY^>o{mtY;E;lLXh9*FM^Ic!i0+qU7R-1wA~3!|8iEK{{*43GxY=~emUFT9 z*9=Ff2ekL=$BD0dcorTq3c7EtX>rQfB19(J{IFIpqk!Ptx)^{t)V+_ZgCmem0w>G0 z%0;-+mQ~1%2hYZkAg9>WdJSiNlYrTRlTya;$L+zI_f|#5Dl_fnFpLS;sseq)Lsdgv zZMDYxWU-c!+)Oz zZXq|cNztrCF49{{E)YzWr0^_w!(|@?W3>W)$NS014~xRD;WE*l4}m2N_GCA?GG7^R z64@Lf(Y$rlNVZwqYe>9I4b_PbDSR4Z_msfP2;Uwb;hhl(A96r<$`WV+n89y4t^Y*<&cE__tCnB*I-_nyy>)IS!J1GPrBCNI^D)6|D+ z$btNqMcyQ9?1nUqp1kQYwWq6cQby9JZ>%{n2s`cNqOQoPkXypN_~Bm6MKz;Sh{wG) z_O)U;t0s9pXfUdMTlWEVm5ozjQzWIDRzM$biFY00cF{kIoj8Y=WsmZh*nJYgz^0&_ind3LW@;*F327&jND2PIV#hxSjeKOF(T$r>O(C-z z5_m7>5OB3ip-3T1gE(t>J&bm2W&F}8J-jQ$^~nm#;dM)!qk?zl&Ytq#mlhKFkh4K1 z`pX|;}xg*mD0DGOFEAuo@) zhAqMJCR6r!GH2)XDBwW{B<)?Ci6SQi0k%_AiMmR+iPv}rN3@^DA> z&pe=GPU5g_wXna>Ssp`x(Jp3`2B)F-de*D@_+=OI4L&M&Hf>zHmty+8P-W|@x@_Ug zX7;DH)3z&Y*4h*aJWk*IRFDeql*#r+TzV7}AsLTd55NeIn&O<>(j)$~cT(3g}aR8o$IY~cezk+LUwfk(0{`6(`}_I-ehcK|u~AF)L0}PZ#1UUm{>Bre%=>R#aq}`*FP{jl16ROh6z4B@=C(VQrqZH%2sshyiWnYYyU+@j)oC6^sLV~>B{qi(a=yW+;W zp##4z_Z)YokdXl%JphbrX&kVKO!R^gIEDJ^6Qr*WNuQo)d-SbT!)aK@z_EHR_PGUv z7c;dDwMlttOmb)1M1I^*TH&>vfy?T&Nk}T})0oHBLy55UBr`kvpgReQ`0fYEv8u*V z35!x*9H>_G$Sys$&FZyPd~4mqE5>ebv@RXGR}MNqgE)h2=EKLLd3l9SF6ljR0E<_G znMp~Kk>s?~9Dm@s|G@oRT80|IE#Z*)yBMhiegiMMKS}b9_jvbi!W)wX0)5`QL#93W z`A!CKAzw&#dRkhSiX;L0LAtcv!7JniTg{42``x+Ux$=<3*4^w!ks0%i37M_2D%h9> zs_};2{`L_JSrzb?ODeQ^R~+R}I)Ac0(+YiPT0tUMCSIRV2|BbivZ9?z3UFCuBhh6%F#*19bY z*3|J?c(5>g#rl=TKR|(x(IVa^h&GiE-Oo$qXEe)W{A zjgH?F?_8?+Xzpw4g_-dyGTE-}m0N$e9{&B;^zS|4C((H=2-so1SR;94o&ce$Z3R&P zHR#cmD`D0X;c#S+nkamTUbB%O*39BItqIOx@R;h_oAcq%Jw=@`DPD@~8c8-`@`Vx% znTLVb7qqYup=gYN%-^9GQh%pE|MXxa{02`?HfL4O7fzFa~dBvK&A65k=FMfkt6Yi zE^H}`SO9AzX~|KvV8sVil+ueIO{6%(+O!Ro<%-$K!M{M}HbvExf)d*`ypGAk6A?~o zh=ZsM_zO(PhvuSD0IvaP44HLP91K7z&A`yx7}Ma$ztJ^ijbB`iXwMbu21j}Rv9vk! za^dnr`U*kc1<@W+hhulvzPNvmd3detbKj3rr*@b+5#nJ*3i$m$q?3+pBD_T4W>rZ0 z4m|DzZMBkTc!0AwIGn5ihnUV-k>i}Z&mqUHEMZoj*W-4k3D-K33W?4FGh+3hpfHxc8FP7CRd6R+ z(@Bpgth_abUXhXi{Ds3V$$|9sK44{E< z$ndcS_+w{p(DBqc-Ngca4+@?G)A5<1*;(W3&c!&=eNLm3XKCwK?t8RY(couN*=2js zNg3X~b!q*wpr{R=&t8^Fvh7kJ;bFn6pLP9hlk#g&m@||olvaV=ocb#*TSji}_~xTA zeL>uUrqYZzXu0A#984h{;Z{LXan`w+-8W% zkUBZ4Hb%~jtI-LE9SpBD3_TE^*2#BY75z$wzt!ep3WZ?(2jiUDi>cKoH`=B&LIX&R zo_N_L>MAnBGOXuPjfgEzXX>Jqa@jKU3GWd%J@qCcA zs{`;N)Ywycr~#O}y9=KS zpZ1as!2fv{{r$wh+XBCkscS(3emQ0%vjsZ4_n^29|6m>NsO^xM8$OLT8RP98Jv7qL zlO~T&L*RcXbwfY8>r^P0B?0irI4HWH=L&gj+86gai`ik@%MDF9}Ba7p<4<40EC zjUJwES3F%C_0Z?b5eE%Ier|=b&pmI&wTmjRx$jbXd@1qpI8^3e_#E-8%VWzs^l&?# zY7ICG-hhCY{?xQOIz-_TXKu5tDx-@fsnhS>IynPN6-snrIi2V%gJLGI&Hg>pvXRe> ziQuAL)aXyg$>C}+^E2kRQRg|%XG)uM7Ay8V zJ$rY*(LL9JbCTIT@BhewG*0ez-ABBxgJ*5q$OH6Nsi`klg3B`8vzYRDnw9I<&SX4? z1dTJ{9#`ry0k|Y6kHGh+(RZu3`H5qS7xaQcNi#r_4oxHbaRGYrf+By$dc%2Cx;B|~ z)6h-*ty=p1FvgE<{Q;+i%q=Q4Zm-Jv@)y}GI^^kBn|@|6c1ztM%R!_2NGMR*lk4t` zB~y+TFoBH`axNKTUtpMGph$jm@yuL8QvN;8oSHntLm{bySqK9PEWMde5>*9?b3%F^ z$q#+|$or^vq+Rn@e?_A*E?D3pNnA;zX51t*2-j$s%nE}I1HS06SFo@9PTcJ;_$yLa z34y}iQEn#6#%5QEE%zamJ|r615g_i;P#A5=_N*8CANSlRoeQ_v?X&02!kpO+o+8xD zZ7OIU{=5D4_j!MV1w7$wM>VL>*fxzT_YjDfBP<~^>s7?^q(wmopWfpK5d=ZlLeKiN zVENBb%?vnyd&EzghihjA7GvCKTq2K3;-G~Y=R^o@dtR}dlS7IQrqZ33q0wQY1`KVO z2y`jFY-X^`{P4Q+enakyQ>jAQN__L!;oCLoynxS3-?D!-s<+(U0Nu!6+f%FlYydiw zq1|mL=05Y)RU^FRq0Q7Juw9r~knUEIrlF83LbEqmcmLeOVW`s*{5tHtHsX*>$~WVv za-Op&t4FND&vv{-YtnEkbY-yskmbHpXvl6;gO?Yf3T#z;;(A-rSFlAB$7^~*qtw!B zhpP5{Nr~BTF4p78O_T7pHmLRB{dhxN37c}iVBLcZst#|&_?llLqJ7}7HcgPxXMo6H z>LHmQu?~73b%<46`kxE3U}>m~9t7B*4(`d!9Gd{Q7l0Y(GS? z{EpfzL%JN05^V1BVljaLgKTbjepHl*TZ|i$TU{~poF+*u-@uS|NQzW~tKX|-zo7(+^J}d~%wM#Gm=uqn9ol(77MM1( z1OoKJ&(goaO@ANpcUnN^n0r8q5*Q+ou0j?Wh=-PwWJIRt_blo;Z=Y3%Td9B}X$Q_L z#esIAU^y>7M_>qb3=OW@t`HQ6>oB_pCf(>}e#Y+Vgj6A>sF67di19oY{Ya)h)oHxj zTl3?^4~~zYSynM!r?(Wigr=wJ-MG@M;F*ci@3FelCc86Fg7DyFbKklj>G8h+9Q<5y zT=5!jcmo|gS~ClvcxeSO#6gtM5UEsB$LP8r?BIkGd0;3<*~Y=m$y6Mga)|!`3=~Dy z4g=;vaooH*0AQN~Mto_w66UCQ+miiH{IDC?^_2vY@VVmp#rI#O&Lb`)I&FP57UI6* z&~a#3J5#3g5(?Q(k3OtuE2p?mBGUk{I06I$C?Gb7>iC4d8+^NtveQnLW4?sZW81A; zRHuf>6J(e2Dw-QL^`zmxo9yav4S>ViRUQx9J0)>iM5Lo6&an7Kdd4xli;dBdwuF1n zCxXuPXl6%moI*|)l~xEC}<3={hZhd5bL~3DX1_>033r4h(%YYiU%@+ zjQEm}cGrhIMH?7%X`P>Z_ko_t54@>MmsRHNHr|cG9vZu}sA}izQ5ci3tYA0ipOFG_ z^*_@GQT0F42jx#tM-tLzt8&nEg)mR zhQ5n9Ot_n09M6!)eP3ydhFT6B^CZXjf|(cgaxzIQEDQII*T@C!W}P|e z9+hj6Y~1M;ZZFm^xc_(p-Gv}^$m6z@m9ocli4O~)p-r5%-R3bgGE3qmr;aCRs9pW7P^obs?AB~mUI91%1{qiuoTLcr2N8gPLtMjnQunlHnpnXprdtx#H3?!( znD~S|E(#5{-@i*^n3?yjpo!^>CQAUVqV}>CTOjffaOr8f7^DG>qk#q|CzHv9(y0D7*I|+NnGZIj4p$o(Zsh2}3_=$1^&4pPp&j9qw!C zZVc)K>`_$lA)FUsijoM3!p=Jg%G5Qp*N!gyl|HP2K;hUZw+L!UCbn`2eSZ!~8u5QY zHHD!tYPZPwXy4$fH6?=+Rncc26ns?$bGez0w&DAC?w(tF{a4C8uPck%WD9*^=YGS* z-+zPu4JW=vK0Y8-2ytmv6!!-t{o8yzMoMxdr)9kt*~sUGw+i6 z3Y9aYFuck=t>W2O6IuV(rtd9px5&f@G2x_dV}pf z=9t{gfsAEK9GpM|TH2|4kTtcVaV@mTt*?bAjg9hKX}fs6TI!q@9Op$I{J`ZHqr@m! zwq1b3Qu6(GnrLyET>HSxd!YVg#DkFyDeE6iqNK zI9gc#NT^#;CI)HfsYm8uVCD%wp1vBE!m{{Lc>v&6@iH$Rkm!-{C^l8wGuPAAr6vxV zP!c@G6w%SmnKNswzp^&$)-AjKsi5)n_*={L?~bAc=$n|b@b$nA@u?p$$4z(6>%sW& z$lcZ+apF~atHf-lXDFhWq_Aq0gZ%pNmw-A-2cQMNkAJC_=A)!*xI_-nl2ERGo@Rx7 zvsZyTGNb1(nix~P4FXJuNBCt$BkcB9Yo4?vsw{LYMGajtco+Rm-gnbq>4T<`^UZyu zo|)AUC|u^?&;MCyG!o$c0SYvwp6Sn9>*?Q0oL=6Y3HOqF46&8!$KU-MobmS~|2r&@ zL&HP+p+JDgHJ?r;AA2h=9w@12e2w7-ZdZMujh%mp)m;438tt^GRSy1CPZe+y)=Vz} zGO~=j#AtQzPMC0auef0`0xO0iN=zy@u#=w1g%T74$kFrP5$l6E@vW^1H2H9aWb7oJL)HQa{9y$DNd(b|&cTKkZ(6?O}ST+hisWvJ}Q(6q|u_Y;l$As3wGJ7{D zY_iCU(9zibWb&ka?~`a>N$7)CQoXa`f!Vn`p^nnnho^tgwR&~7t~<0M#;d(~8*~%h zeGZP7z5M~RSO*dtj$J`w#A0Ji2}yB0!2(uSp|JdHn4vRSbjt!HyF)Qhq5tSS`dW|80ij zF-(%eQ|C=ZuI}8?C$+81ZYIAC6mCs~AgO`%D?>|f++PZf7DoMlZhZg^Y&Joo z5BR=635`Z}a0nExM^dl7sIl}N+za(bFP$WO0RKDu_V-)-`z-L?&tR|(E6~9WWI0#0MF^OB8^r)lEPNH= zS4^@XnV=TkGZ3b!Ff#OFi~5f={E@eEQ>1B%-g=(~$FA@*UaI11db(y)&+OfgRnOke zn?89m5;=LISth%75xeMO?*aJMEt429;H;1$P$(kH>ozQ9VC(wQt?P)I>Tv0eTuT07 znEbM(5{#-v#0DBi&-(Te8l>)m0obN6zJ*F>BGaxDF69S-%W-I=3BO?}Rxruq<{%Yz zPnwVAqf@-Q`@^5#GTk3^-z9G*^ii+&dV zVH>8Z0Adfmf9rleD7de~Cj<|}SyHVN_P**BqVaa^h%?=|tL842UxqoO?zAA{`;l{4 zg;6|Bb7K$*2$Sx53fsHhJbj8+)lW*58l17frob9a@vQny9|w0_-TWG!qw_K6b7P-) z19emDe5_!jP5Rabzq?agsZp~h{?uk{ex22FpGkWFbsy5;lZyg*$f;sLC%Euyxu9oV zxQ3l|(ozkP0QH%EmO~jBczy*3T7(*%(DbfkM%rGzF|cwP+Ti zDbO<76I4f=L(AM>D|wpmGDEK@{_J~l=Fqw`vY&h9bpL&>`TNQLDGQuLmYf8Upv%4q zS`)p#*pC*bL%1eFJG*LUYQ^Hu@`f2fbadF=a2`20n1=7~VgdFJLDaZynR|mKq`VfQ zgE|a`fZGez*{tVH?M*Fys|?{M)6p1?eV!O)j`3Tev9-^Echty#2>v#^Thn_rF)QlO zIwxy&?7duvms`rtv2s?+#%p~}x@}>G#-mu$6h| zJEz`D=m0?062vtp|EwHS%}+G6Nzc4oD4^Kt*6wz~%c-$LnBK$YHC`^&%FuHQ)($(Mma$>{66cbP(|Qxv!K zgRKgTLTl+z!8oZqIWc*48QaKJK^e?=35Ztf6?_DkvtM|{#te0h!z_7WrKClKwTo(6 zDUZodTb(-avp=l*c;=w=e8WZmA~l_8^OV0>&ct-1J_r{|Va`ydI}F?@w^!9*W?Q7=r?GHVSeKrj zT_+Jt4j>d8!b_@RK;b*mcu*e)kJ5eQZ3i*~gRLTtBdBSr2q%J&#RMegXqYrlpNcK< zjxD3~;9GML>3g0IQhb(_?GEZQ_G^Dqcr+okZkuIkgy`o{|9vvqBa=hdA$Om7wB*G% z=UZ)d5j>MPe2tNqho^3c3eW#4R0-ExS4><9fIMba@YR}No}2!XsuI5BUA#yQPy}3X z%VCrOQnISq7vw1ha*D2xM8Sb664GYf&ESY2i%JNR1qAMZ8L3M?b-#ZT`2JMJy zdj;cXR2ii%lToi3-!fi3IxCai6SqO-SDnNy-Y`oa!2Hl@8ZvIU01hinp(fDoLDLN@ z*jh18N4&{aw7PVp9QXfY?>(cMY`1mcCzTKagb*MgVt{~jLocExG$}ztQ$WOpl zL`i|rJ3>T2kdC4VqKG1)gCZR(DoC@7q9RiAYi;v~l045H zCf7Z4&udU` z0sgSqP5`22BmY-QVfPCM_Mm8v$4Rrm9J#g$?szX3IG^5=gkT7TdCA7X(lJmA|z;8O|; zBtRDhAjE@}fcg4hp&;Vx99>}NG&%%f)b|y zN&6(x0;H{tPYj}=9R=i{bP~Kgt2O*6+FF1!@F*1{PMpg8IhDyD^hSq;eS(k?5Gq#% zowTYNinnps^i?m+ZLcd1Tskqy@gjsZm>xMAEz`7li1U157w^aS2Yfhhu=jp+x;Ei` zd<|-2GezHFoL}OsMg-$@H3-CSibS8YI$jUuSs<&@2coiO@ad3YqKrT5DVqeRnH~1k zOoBW@JXFc~B7m~MXmW=qQnuy#64CrBV?pIg8jBhGr!08pdObsNK`&Ax2Fe zt`muFoc!<|gRU=z#-C;IN!RZ^v4Z}N*%XIJ)5Wq{-pQBu7O}!-I9Q~t9vMf+`?<>3 z0%r6a<;=k^`U#oxrTG3(Sv0z|kl>6YVs{ZkWDHbjB1mM3X=rXfj@uY8=|k(H9fhNW zg~rGrU%=KQJ-$+xL0j<35v4IeUS>D9{xy6s;Zq8y?^6?Ce4dd%35LUW3QMX?9S(tC zI897c`J{|AZ(f|1_6nwhviIru!M-BsbE@L{n=brPxBCTu^*(q#uWyX{4*~4&MgHq8 zu!<{gB9AHs+Ll89H5kuXnXuv%=U;+A$!-A~;22qh0QSkp7^1H%J^%bJ&V zxzq5r{fNoqJ$~~?U20m+dS|>Ft#;?{$9Aj1?@9|nxsvWBaG9Cw&7J}SctP#v@fXZa zK@desOt0@J81RG8D_tc?ufLTb_cem)-0m2psC2DOdEcTO93XSgGeM#3ncMw6IMPds zFq5tW#C4W-4U+&TfUrq+uY5;N{ZZGT_4AKUcpZc`NsU)0*_DL~KC`({bU)TLmbd;? zK#eDvmT8l$M4$sOkmpA)odydpwgMpI#bjq2c;QSHAP)ff!p=;UcPGpMFRq6-_X8YF z$=zZ>PKj_LEJI-P(vT|bMLL%N7RRXp=5^dmvkI6mM^hynuB)^}M_d zkz(Gvqcpj*K61y3%bsD1r(MS9?Qhrj4)NB%)b_1^C1D(`_@wJ*uXkON)x_vQ^cNYL zpc!2;8Zw3g(YT}sVzU8BM6BT6`g)o-y_q(g4tIppLrV1S3j#B6t}b}5Q@X9VLWevz zwHl;Bot_#-R8;Mfx0q8s0*VwU@;#O_-1oBPXmVg;_g|fYqpFP^KO@QPs*w(Fu|*H? zo3Smcn!&RxBCpP=^ji1AqSuRG=G)Juz0YeC-S}iaJS6d756#~z{Ie{;yOPnyMF;B3 zVZrCX=IN-zHxAz5_M#@sAA-#!z0g;Vq#OATAMi8Dn%4(aiX+&tcs%U}4y@laf1wha zwp$45Cjye^>b#&Ryso|y+)j(@^ol1ts~(SkDtn@Ga{nWH#RT76V$$j1v(K*OIvvdF zykT?%8QrvcSpA)T0$($f>9NJd0XqTg(lRknZRd+}N^(Tp?2no2){IY`@D6=Vk(JX% z05h0yBZq=|ayy(I_}bTATRL3Z@dYvR=F!;!_6 z0wwcOaaM2^98ACO*fw%~HxdlN*!d{SyK_qWJLrK5SFS%kx~=V7#`EU2%u&}Y>-Zg< z@k{xqM&D}eo-G7q#eK*7p@r3KTh zz+l38zLa_{G_JD;Lt+4Xv`;>LN(b0S%(u|2d}yu&=#q-{8>p2KoMB0VJ4-9H2x(aA z1Zthm{=&`Jv@RKXVfE@q6-@WTu;TI{zZdU*Qk^&c8a_zz!-q2+e{c%Y{B8;7Xc@JY-)mWxXkcbMBl?gV+}VC7A&hoij= zh%;6GM4eKl@OOs{zrL=AlGMu17u;7a2)JOQ1R`N%$tB7pWJDxf5Pbb z-j&-N{-CdmtT<1dKXyjJ*-8?L3o}98vrxd(dORc?RPL&6i7hc)l^Hu0hh6jeWi^|v z{au}MS@_evU58IDU%abNKEQVdvr?y5cjc>J8?bJ=bdPe908{+Z(xvtD{!48s$`=?I zMr>9HPS5oNVv-t$0R``7st9TTCAi5O0%Vo|G=zd04tG^`Q@sfeEQ2!>z^MS`n5R_y zlye6+pwo;Y;QaJXX5VAaB-C|!`^cu_vxj!5`_mC_F@;od{pOkUVp< z3;LcHwW{{0ZhY>-I**@`5&NE%$ z#jb2I{l^0H_iFz<3v2*;u5qQT`vl?Qv$X<%2+G%tSc0R*1)8hY!Mf-n)*l`i66cY0 zC2~kAU4#JVAfg4~R;A;Shbv|GU-)6=n>iIHSptweOb^605uZ#U(^S6a+--L(-hE#> z*d?@xsdVt;d^9R6jTEIB^sW1OB=PgEjkGvBBJVSQFI2r#{=NYMOGSEKXfzRvmlhJ! zBj@(uwla#-$`~ZtLkGatL4seWoVNI(>%1m zTe8Q9^1Fam<7*7>XC1GBe%}DNBL~A-tT3x2(RL%rhxF zy7NqiXZ2=!>a3I^={cUu3fcvyk`AWaycJ(G9ai^NgkRS44HHZLVLc4hm&qmp2!S^4 zFi@f?*a1>adpA$Tv8>CPjoR@AWOVstCAya)^GP=xl!U@b5}yrYVJ<`!6xI+A#bF0& zwf#)6g+fTZuxU2~P2!@s?Yhb}%=>m;v+y6X1Y*}+R|Uo5hz!Dmxi2B~8*#I5iZqKA z*eaW2Ndrzvr;qF8wXF;>WLCdbCv~w{`?M7;tS!4`X#Nnl@9AtANJR~cG(!T(%ebc} zQ4S%9t~W;HNmQjxLW6@8S*V_>-r`UWjP*d-6;-$UNi~E>H6`im+7ObOkSY1i(bnU8 z2G7Tw&Ne<8ou0_N`ok@VFKC?N&!NZa^PK{}odP@FSoL@_1wGKFdNnMNvCL0NUiBr$ z?tkwa-?nPF(+=_SFz;Tk$}jFe2dlqV{l{3K(xNtqjzK#kG@vIZ%h&Z_vkyzmwKetW&38?~k$)s&W@5!a3J-a_8cVRuV@_^>{>9{Wyw z`6rlhXP`tz#J-*8R<5yIKpv+@@kiFak{3eelB(ibk>sikQb27pK!{E=|%0)DRts0~nB!r{`p zdPW7cHFk7?okpJ+%1B2bHu`790*a))>P@Tm&7NqSIR5F(P~p1!S{v_T}CAYH;ZwB*O~yfDNI;?a+u#y>&-80|Xr<9j9--ULZ6XOD_X$y&m6L8Sf$jVL=lIbB-zQKj6or=snFwsT(o5RnLEg^g`4>By?*3yC z`FoXrx&^cWw{#mtI!HPmU3^8*-8!(e#LgNUl^e{&qse0DKfEoJe?!#T(<%%ncDU?G zMvWwGGP#2lM9Augl`RbEfE0yAl!dH2LC_NkAR#^4+&na2tS~XSUPRH*y2iqAQ=4Xn zlhc(PtAcOc!BwB!=bqhuv~gUt?#S&wCI~HSMGNoz>B!>&CyLAgI`xe_jDw^2oXYA3 zcY?fyh4y|bk}x`Eb!VY+!^E;Apyyh zHV}_C1DJI5I zxy}IzBUom`GOO7b&ZyX(dK0Y+=_%Q4QjU42Se96^$%%N80~eTOA>#4sgW)0HV%#WU z$={zfzu(1s*_8iv{qj1Gw{r8x<)+;?zER$6J_LnvGRYUO<&3gbiq9prV&SrgQqzE! za0xNkgGDm|!^}9UweNKr`4lpah5?Ax);;a!=h9Kd{Bnns5AoN(2g2#8I1{2y0I-VU zM!o?KeSLJU_mH+ip3Peg%aoo*r5`JP9}8Co-?u#ex;?y^x6ww8y0^#&-=E0`JrRCJ zayx&JB>$km&q#`dRBYA!EJ)@b6sGz8-T&zr{rv|1!4{~12G-IAK-|>K=A#-dR%gF_ zLemChXDj37RETJw0OK!Zafo|uhr^|r36}6Zw5u-JC>RW+jD=qh?G6D1xU6v?gKnDqB*!g&p-e~KDj@uM;vPIEoh(xra zn%_y;+x4m^a)C8I zf6wuLxmspBV~mEGbwA2V|y)xf*)RL{j;yL}c2jZH|M`V!zP2$iKV)X6ZfT-6Ao z;pM>_D(!I)jy3t^;|A=eqy7&lG`uhty(d6j3_RO7Eo{-Zwtckv+v-?!j`Tq-a9o<> z^P>Fy*ROgX-g3Rzb@xInUmB&9bNWw8LHrT_q~Os(O|~=^jB2KcE>_8FE~%0~^C^Xg z)5I$JKNyq0m-{DMpa!Z|OD6=|&u$j*<|;bLSHMmnyeZY=qjWUAWH@-iVN8$lprw() zQj}q_$ySM!>tYCP!Q*%;LBoI$i7VmnPW7N36sQLLwikptA2 zZzc8Z=M&S|v{D36>&5gnG35Akmg+j_cP7v>pBXlcPU28T0ac7luouybBmjD#QIT4g zDXn+uy!U`BbQ#gbluNtYSadE%OrLC>ZfEfFt-Al|D8tKZ5AL*oY2~f*Ry1=DpP5j} zv9~?eDJLK=4FgkbbFHD$#V35&lJvJaE<&lQEKId5QdJ&j>P-YUKU8%F;ge2vGrU1u zc^ZLBEPTKX?f^X6`GYTO+~_2ey%j&XD6bI5I4Zxme9ZP3X>4$*2EIeT%%FUZ@j&CD zO?}<&Vrkziu|?~z9`ACAeRm-CQVr5?V*1qNC|ZzdX=VEYsI@YQC^#WPYeG9qnqYL_ zHB~T}Y&eudTMSqlMpJ^NhS4HaF^WZ*>-;92q8d5RDIyL_0obW>sNLlo5^!d=U~zn% z^k#=kn|;+U8~g4Q+t|}NnUB>hQL;L;3C7otfp5Q_*YsmuAFsbJ`dB=f@B50!<7m&y z)m}=ftOnJSk%@TlZZavL^W@2{sXi_}_hIJbdjJSHLyR%d4SBEagN%CCOeJDcz;p*r zj~hT35kI9Q2x^1~hNt&7B1xnYBuz!cBNwO+myaEIDnjz_`n5++T#TZ@EbKhFNt?K-0@VB)ayz0 zfLSj98sfEIhwt1H5pTte&S+t4n!>Vz^ju9kWA9zIIlGC_+Q#eI&75$t?Wzx*e{(-& z^bYtFkOiVY?#*eC4o&u~A7NUswe6+#WKD4hFelZ|cNT$_j1a#bWJwyo`XWcgC@sfB zdl<{Fh0GL=w^x1#8bv=ADCvMy9|2mW(4WE(rkSth$QXq<8-*!RNd3)EOqWJ{nn{+D z4ex4${o`fX|BbUh!w212)!mm1JE#mxW9g^{ifC$ul2+(j{^X2V&q2%I;luwn*Z+|3 zs;!fQxY8~ARDuQ6U|{5MXlerMeoPn1`JRZ7FoOq}8)zyUk_{F`v66()mXY>@_h6!?z0@AD0F;%arg?gD60gAaK$EwG+^_ zRYduzd7;+NHRTlygBXf9mYKra+i%3K)jZieHFylg=`mRwfuUEebn5mV7j3-Ku+ zEs9@Ps48jsl;fG zEpXH+EMAzpNYf^QH)&H~fEc?_Xsa3u)CnN0bgBIq&y+jcz)WV(7dpwI(n6Zf#nmIH zlYu=W$@KO+b6+EIC}bMkz~P$}D3u9E)2-05CFODQ(r8;n!rpTUEbwj#>CNiTDGBNT|@&?>O$ zoz22AL&NBYoT9+EgRNp6*Mi+YPxNY>O1smzEo#@w`fB6)-Wqg4kSFR&HPk7RPIPr1 zZXa`DrTT3)ZYcS(H%9=5=C=EKOS4kCYUMq%Zga~Stg|TsNwuzJ0ejIfhKBhyaa4>r z6e3n02Q9fQ7JQCW3G5M4{gMVT$FK>H&*m9M-^6BGRybY1`e^X`oz>RKsQnimkDXo` z;w@$9miz?)f1VV?H&m_dilS+BzMwIxks@07hf`2xEMd!ie}@nMYp47V`dZ6OuBFR_ zePm;(El063M14{~Pq;2qzQ?8q1x^tLtM=eUaS=Od^xI5sPXxdr;X=!8Vqf7Du#E$f zQ~^MUn6Cr%OEb#}AsWyKDpCQ=*O|h7p5&{CH825bjZnczvCuU?K}AAJWq*#!tA?C> zLFJd}N0*;FmrA$*hVNT%d2;DEXRLS3^aTs{-jfCN8zI(}o<>+O2<3xeG5gA3VhFew zrUw$|=L)q#fsdZDl}#uQ5GKkC5vc$X3fB1gP6U#9s#((s)xG#t+uxOR%dLm{SgJ5vo3*7mooMk$^x3 zm3&vsvEF~`uI#k6_z^cOz5@FI`c+|Io0O;mZ9f^hFJ>vIa($(9Ltw3q^_4w(!$gE` z_r~kMmzybYt)3#Sn-RC;XdGJ_?wD5}%_kGuBu4Bc8V!+ioh5aB$Vf)*obpt|V;Ez} z!e%PKsKRn$znWZya#n>ygFpJ=cBam`hwh#Wzu1Ok+=UFgIZQ6#@NDSd!RPk z_&2vejO8GYE0=Pm!i5PUdm6o2leH&$mcJ`uNek?)pE8&ENkS)DDnPwG)vYZ2PLFo5 zZlQ6_d8kge)Ebfsit{EhyJ6Xqt&o+8&8XPldHB`dlR`6OX^m?qmMC+ABVu z`dMSg2`8Dlhprzbky5eFH(p)!f3dK#9CM$|+y3)C@5bK6xh5dTg$my6)*!Nz!9|hI zt#Qh^BBI}jbCzb)aI{*q@+D^gf6|#g_`orD{18RYSSO!Uji(W5U(O9NKoo?qcggi! zRE$JPANM6diwm*?JI7S9oiYe3ziwN7h9JX>yGnyu}c)c#`o2r zL>W?-!r=-1}wytn{bHp%1pK zoZndHZCSlxcHzVS3v&O;Dcs|0mo9YBPFDP-^g-lCh2kG6$#E>7Q}F!l6#icf`ET`_ zS{wdJLH?V6sDzM)f&gx$!H0S~zKm3xm0?9H2~F&Ng}yvLAN+! zx7dlv^~+K^9}B;%A8)(O-!R`_aSo_BOly9zcQLZ3?MFF3 z5!hsGRE&0UQF3txsap!76Om=Zo>>^>Z5SLfH)yPVT%n#cFCv7}MW^Zl# zdZJJBy|}KeYT^KiikGDc;zGENUU-qiTZ>%yvo)&-bTk?VS*ebXmv*<_Uzm|+@!@Gm zR{P>tR_bGM_gvm*p1U!>?2Gd7`?LQ8ynk)RCdLmR_~NCLmPX(C^-!XTe4;V(Eo1JF zb_%V(iN^oKX8-m-s^u2c(iMr0s{u!g#3@(k%DZu&aM4{~t|a0!(RfP8DMl^07zKbB zew)l$OusYX;Dp4lO%k|};C6!U{sJ=rI^K0LZEL&5i?{^1%=QMZtuyNuhGHiScQ~Nh z$9p*X@G%^6>V4Ej_Ue(_;TKmYU{~FGL#;CQ|ICQp0151{_3O&6Bc}Px7!WlRKasUV zubYm3-~tifw%NIAx)pX;N9i{L91k2K)bX~CwT-_^*m*r_8eQIL%A@N^GJ{31%{(q> zYRoX*8}CqH)IcoOHaWaayc-mjlSFxQUe==h)lYyxCEA1m8Sq1M2pFoUls8OLW4%8$?9qdPRmvw=VVR1g zUl{RHmcBmV?`Mf1#Vg(@+zcAUT5=C+NtgI|Y)#Diss_dzrP=jszIZBdGufx~$J+bi z7EXl=+R_CtbWi28{@gtnp18wXWT*Bbee?7%KpTD|3?Y$DG3X}|sMGfu2$flI3LO(e zlu7Um>M;@EXb_}#rr`|W4d1tfIK18Bun2%Sd7B{>C~ns#+nc9U6N(ZrJ7lg)UMMa( zOns?$esKSg=@;IsUD1t#O{=_LO^5zt1pnorz=m`ySwAQorG{Fn8Xw7|hz3;1i<}5# zeC7)p?@jajN&SBO&$0k7eW;Bq<(_{>)7Zt94uBSWn2G|Wd#JjS4#vchms0Rs2vJcG zR}qSg7X@CEuFzbqZho?PO`|4`19<16{^7dB$%(u>?s|!Lef!Q+w#FeS`F3%XfGg*> zqJ;a(5&~Q#QlF-Zl-fL+kXOt&;(jXS)+@={Tq`xYgylA&yux-G#tFg~5qn+G2z(tT z-5O@3Ld0A2#W8){(6A3^C}oeXf@VQXre{dtS*h0r`P8mU;?+_E5m492X0!(0iQwcl zDw=A2D{PdTAyAZOxA{QG99*KFea(ElpvL}qZ0*e}o*4`I!Rr|IzOdi6`ttlOSR7^hW>=NA4dbi>7%bBw` zzkI^&<6OV?X_<{_dsiFmIb0Ygq-it8*y=;?kD3kj%g7H?cFet5FvxvB+b-M`34 zTEnoo@iPHgl?gDTO8Vr0T!N_#_DW?wfz*>_+p?+1Xhe2b#mJjX=qV$6Av{acFaFZo zh^zZVQuExiwJ8&IrWZ7!m>o6y3ZqX={l{?rGkRcE^IJI=Ceu!K)d(KvPnS)tkmt)3 zl5Xdg``#n}J}CUhCj2*ic7w+vPO^(rvz02asUn3im7<% zZ>y@h{_Xn*J4gAAiQwE<5p&saoUYbjIpi6 z!6;$F9W3nT@PVmGql1}DQpX#39)>3Gmu^y^GWjA4ZHDv~=sD_xE6#}@JXTTdXcN(j zB6;k69Umi2s>1>uXP^QIKJU)OVv_SKX{Q5Ca=>~iznH1 z8Xa(^26i`>Si2&*4+iVssx8(gF|EJ^sfrZ6{92Xa;yW1IiA#pn$2*hj(wBx5NdMv#5_E_7G6jA!wqxBY-^0TwBB@n!SC0!a#;|4QekULP zQ}+D#c<~aC$>>Z|khA~<5jyhq-QGCo9V3eI$pE=bI1hEpOq-}8s{;m7&9>>FW4N8+ z!dno`_V)1!e?Ku0XV^VVCy-cLbET`rTx?i{tGcX@*i7D3Mt0dJBzm*qzS^ee6&>F- z8{j=l^Os-rMlXcTx3$T&-izJfAx#d5P99?X^x12k-xTAg!noNnFR*u$azs@2dq$Y_ zQiqgOyd1c_vhM^;+*q5*RrtE?c@TYu;4{{zkXzE-NOf*WQ79o!uc!?cxeyPb9IvER z_N(~S%svjEBffEuxpnsQ>A~>t1I7cDgo#hjK9zOlYd`3phm~uQ=|?sh_a{k9VN0&` zPYx0_P6me45ko!^iKyPh-GGoQ1rC<*r!l13jy#V zF!7P<0G#$Lj4B35sX^H@yF-TIzO-K=(oeO6>3F0i*lf%use?wvW1_}e*_91!486wy zVdf;;(P!woMWn_DTdNBl6%bDOAY5&kMv+#$zcV?YDk9T?ab#ue=HH0Mzc>XJWLmZN zGGEXbI9vTaJlaY)u}ZG)V-4eRZrRhp?%yYcztOh;x|hAa#q?4)=?^ePC`nr@3qlVA zGxi!F5Ex{ksHScPz}og^l3X8_m5^N3>Lsl&$9WJD5-ZvW=nQDtjkcQ=$35XUZZ|*+ z$u1R7BnqmXi&mF#p@eKPY(Iq%>V^C|?YFb;fRjVy!l5dj@sFT%a^qLt<3&gINKU?( zayIrs6#XL`J#adAfgGSTf<_OD>QsL;mK7Tg@cLHb5uh7_aFFqShf)FKc3n8D3=+^A zAJ*z1k^*kZO6eNw?aCZU-2yl~mw$V6>sFQRXA92))#LXLXr>&Odik+{=YNOCd-hqc ziJ$q@xlt3SVQK92hNPuULUj0)v49M;UPn9$H1AR#K<_~*xJc{V^llc{@iITAD&l(X zs)SS2fLqt+=K~hkgY%n8B{p@UM=aGG{&n_?ei;gbX^;GMqYTt$4 zZ+DU`e;5#8$*>x!AT^)&?wi+g zO`jV_X;ih|snUguF(|@9s6gEJWB!q)VNfm*`4KxHY3$!KYg>ssQZLf3Sg$hWR%-KzMYaYigy#27S+ei4~nkg2Pv>A~*P#XO8qkWt^3Xx0~eh{Ef&osg|fgy9`^YqHWvw_{Vb`kL|0mgp+*(T<-5`IWbJ zp3%+td@*CgG@Y&aV%NpRi2aJ9W=F(?;;mqQDIiYCz5Tkd6?zX5uC(zqbG{D!kIk{^0k;fLr)DZie$jmgB|YDYv|zaI2|OMA6-W#)2_{$b?i zm#wn0sW+?imqQ+WT|ax9XK49NjG}w^L#_Gd3l`&HIJ#~b1Y}^GOHUcbA~BG8ttXX8 zA%&7~0tSV;4N65qD0>kUD59NgW~@@n1LE1&(#`M^GSj`EWGc21sbJ!)y8&j>4YcRZ z5=850@4;U`3~SxG&Y!+MVNiEyJ?eb8Z0xq2joZB9=u6wYZZ*_0`C`o&d=CV;LdJ;| zIS4u7vKhmyiv}UY;9N{D_E@g!eW-+ifA3pk4)b=a&kM$=*c^!3P_SiMozXgYXate@? zKf;G-T9^eteBjuMLaSu!`1MeCG3CCkZ+|<5zs0cs(%00+7t{h$tC1J|v;-LpL_zAJ zPlrd^V0C8n75El`3qY!$Hn5l_+`&?m6QeVAwV_riZ%kRkiy@YE(bLt?G4jx zvEU%*EEO~#=5&r2UF`98Kpw!YYk8N%9VzVn-uk)a74LJznRsV)dy;_sIW1eiLMaiP zWH4=tc4-(9V_I6?1AwS38Hi@HE6ms1%9&=AQ93|Mm2#V zgye@!o#R!{`0llh+n4jGye8GJ_i>(=hw{Uy9gl`qUxc^tI-Lw#R(Hg<8Ml(GfBjVW*@^jg4T7@I8G*w8z2y z?E=sJ#6@%q%0e6?_?12<3SowAQT^~Og_JU=#iV}zG`7jN_w2cmbk!QKM#)KEmq%d3aD2JflCO<|f#oAPE7(V#*S}q$-A<~OqceIsg>G;=as>4#lB%|bC?CC}Ss{yW9=tIt;m@vR zVY-EiAQS>30S3G(bn>z zqDVh_!dLc04Yumn2I_oTbLM@^bZ{?W9wCTG_s64)a3{u4U`-umiyk0UzLXAPK^xUD z{L1jdS_}n+FX&S`ei55sc%!26gytOxcJ0N!2<&m zVxD&@y~fZ$(5A3`E3(NYY-1#@z7|9R(IU7+=NykY?UK=Y3A1zkgA>y4#5Q?aqY?)d z6lq_wv`hA6iqAR9h19w!3r)GO88-`#c-v1d`8)0Fe=DO--Y@hw^+ozCr(mmO#ft8r zhW)`QGIp;Iha0#F9OY=LcIqHS z&Ew2iEOYAwrqV!=d3iWSaM5rjvUX6pOwQT?sqrF^8q)9T7H;EQyRY_*qIQ6Dtz7+V z+-o69qNr6@s9vPfg|_eino}|Bpy#W35b88y(^P6bpd0xvvF*L; zu@JTKeHGbW*4&RXyRPZ$%sdvyi0XK z2t^b-`MNWRl)Nvi*GBOcYzSY-S-WHp^4xduZ1KY)7we?W8NHh~Vpn)G_deChHZLIF zWCZgr{M5DCr@l^d2pPC49QSh|X290^=cx;vQS5*{Q;UAihxR z5xb?IYy~%veG}m|jiAYvpz;!{yL~RpcEZsUnGH`BA6J2X%_!7ezd9DhkXFA$PH_(s zy{Egei*@wK;hlA!7ykbgO8)^+{vv2(L8AT$9|~xZzN*s4vnj&W=j7@>+t`fcmYut| z}%wSSb$v64Rk7fz*(8U3<&pzxYLecZlZTDMx3TWSI3;0Zoo zOPQdmZQ>k`8AWaPteoasx>--5UXrL%kSC@7gWK8slidm-xG=brXt3B*QiDCF!t48k z?lLbUFp0KF`Vy-qVza2D^1ut}2jL0ZR$>uH=fiUKyO972JgG~ggCG#&3@ZigJ|kHL z9a@BJfwE8{FrzTzfJJFZ8MtR%s=Vy(ZCBFg({O3-_%0(1c^&FT;;SX?*G78OE-@c=2lP`P!#5YYn&0?6`7s4I+kx z3MI##00Q^)-0fE_gRr=zy12`rZorzr1+hbX$gNJMi2W_n8WB`4KLS?Pqh3TnSv>jE zt%og6*TdzZie$iI^hf1_ppl`nhS24<c< zOZ6{Yi=*0`b7F*uJvoNkWt+#k;+Bn@Z5c^`xEnL40oF*FK zppCYG0uKAL^{l@gcz8=>bAc<<4;_dQuLs;dlD%SeD)$*whhpO5P9UmLWOV6(D z+A)2X*Sh|>Wo32yAX8+IwVdl?^nj3?PBT^udaSbN?x-Yy#Bt05lSQnU&~7UMIMl52 z?voo*r_)$e_?E&uKg}>t&6!R4rN7R2bM^B*n>|-qA(!&PKJDl|SaDBLA`Gp(Q^0MF zus!|n2e?0-f?}!CZuOW^YGk+3-0>`maLhTrQ&6=zBceF*@XeRM-)g__^siw7zMyfo z?5fh^j|dd>1N5C&CS9^I;#{hnAPywGr&*&qFe&ItvQRi*&y9Bgo9 z1tkX6)lnk6N-as8rXtol zz@!bhoJ?{OsmjI-fi(pyZUt+Mic5qaJQN1PZS}&>of`mwvP6#^JFIJCuX@f``%q{| z)0s*Btdy%~V;;Xhef!9{dp)YL%Zo>dvmz5+41nYp4;An7%ILWjc2vUgCWFNeRN)Xz z*q^AbJAFsp&ZhR65}S)`C2DkE(8zrYbaQbi^+mE?SHbB5F**R(*n%nx))ZJ&7*VTI z@42x$ksXd`SFY3-{cP}v_qazj<;?t@uM(f1@83oL+oAR+r2q+1+RfK4Q6uM!1^;jg z)#qe{KdX|5`GUrKJARiw{O#ub7rk;{;C6-MH0T{&TSwO7rK}s8a)6nLq_SXvddWh) ztIAeNl^e(dIozWSAo81Cmjy})SZJ25}F?cuglixI3C(d)M zC*Fuj!s8i*Iz3yU;&Aa)E35`Q6$&x%bdYgr%{PSY@!Pp+7hO4dpy=(J&ox1(clK_2 zPO=Y>XsLGI^J-4t-bwv*F|B7T@!D$ zMIJ5%y$kBhL~#1PT*5bKq2p1g%{pA4cKVF75KuCqzArDhxDunEi-fUk?a%2vmgqe) z1&Af|fmVb9DpA*A@(Io!TH=0iE7ZQN3{<}3*q-v5g&fPiu4fMo#h$=-3QN}Q1K2Xqpv`Zm@b@wGztCM?^Oy$Rgn?RTYXy7g zA+`1*&}=f^$TjoBcZjo?w82)0IoVu5CY=f2!yf2YOGW+sh3LY6Jeq$~`HP&pR~+Y% zkRbMmjY<5sdfc`0c(vFP}U3iFe@9$6c=$*_R@;Jdhe79S(hp z=qV^OF+~Rr_dkgT=rU4!@yu> z&j`(0;{mk$1+>=B%-83jRcO<7(b3Y9Q$crrzSO^%(K6Pt@``5?M>OKtMO_R%_baz) z{HnnYG4DM;S)6X0_8xu#DW?O1Z_!0jwLEWN^W=a~$i-TD2?@m319^bGy) z4y-@J2Z%cVq_BOI8e+-k6pmAbbNS&zly&=++!^xVZ=Uh*ZuI~5H$3ur%r%4PJZK<@ zxRn{T1564jj6| zODP7k-kldISG{z*{~bzWP7RqNU}2^X3%T=n8+UY*;UW&Rn2ft`FuqFZ_Yv39>$`&a?SyqE_C{-$#YY{h9p{g7%dYBw?A-KpPz?XLM z0QTIMv}yG1)XJ~`EyMYz-nNEW^VlaalTRD0=z5hQ#Un6kCp-_$!aYp7XeN`$wiN`4 zq-~QVTAwe<14w3|WEfjUm}3f;jo6_a@ z>zu|G>h3pwa?Vq~FEsipT+=&^o2YmC>-ciTuI1s_iPh27=Kd4G4%0tq`<$G&?=*K- zA$qd(({)IZN+?d^N?{rkpzL%DFwdUl{ZeX?;OMp7DXYfDeA%6g+Y;`b zzIl0l^~TE6Yu07x?_%;~rPy}6&a0=d{bLdHXZQdfm9=6WxKH)(|ix%4NDj-#|HGUr!Eyj{w zG-d)aAtR?<%^n}NL(ry`52}fnQoh#-R-Ab}%>d!tGEEZ5L^bogJy=2J+sLO6hyTC! z&NHg1Z0+MIlz;(4kBAtgS3{GkBoHtlV5rhbXwsAtkfss{Rf<#zMFa$FfK*XbOaca^ zNmU$0>7bw@SV7@s^nSQ&&6;&T%)L7El5ebY;5_^J{Wt&h>~i)oq=#g-=g#XyZ)m!v z*xr&b-4R5U5*8uY(C+C&hR(yZg@iaBA=~*3+1dGoQafUx2)p)9ZqV0Oy20o*=_rO^ zVd|`u%)tJVJgfW8bkf8#$()>`vTrs;_4>jxWKK@$)rVr$`#i6ldK@`Y z^IC%;`o?exVWnF?B!==SiJ`)3S6b9GB!rM)LIzP)krgPb-%09->4AP-HlVXeW-Eu$ z3dcAzh;9l*9e;D8e%jM(E;Ew*_nqn+2%V4s(~|?<-_A@yUs9}$Ea_2;Q*ggVE5O9D z422}2mZ%d`AW@_g5P;0m<{(38<4N-Y??~hN!|Lt22~ZyXFP$P`rCqJnFLT;}C2e-4 z=ZvgziQO!;@T)l827_c_p$8>GQ%zD9KI(V3Z>-eF1!#McenoHiZVKsB+hvVXqO$OA z;z`tiNTY4#LEq;TD)=w5_A=jx`W3qT+L(~tUMJRTrr+`aV?#}o_H!iKB2J-c03e>s z%PylX&mIjG$~o2kRc#YX=AX`S9z#3^MI_Y zj;=%-uj|OH!q$L|EK{ zRs`*@6)ByCi5ks$^k2O(w%W@ZI+9;~n$JA?!*qFaJv+`Ds_5%SnB~2BRN079(2_tl z9qZ|!9|EQ*=$reWRNYp7VA-Qz;W_{LT9o;9Jk2QzF`A#X_ zbEkwARu^BrsD__Ux`A!U@UYi^6aK7M@6*z7q+s#TJDKZ^$8o5k&%%Df!7?L{VzMncT-5?Q{Tl%iR!`~la!$mkC~c= z{4j+Ye&*h#|0Vpt?Xx4Dv?_~e!kx*cQB~B^RR5ckgm);^xI|AZ7#@Q`<-7r~q(1jT za}+`eu}dW0VA(PR1y2d#N89O!gT{qoMPqiv9xMzOLO)GLSercSYd#|Ka?R!I@cs(CEChEU1pCx_q-^s#t3f>9-JW2E+dp5*m=mrCfnfyAC#OC zNAszWS&|lfo=wdotyQxikZnd%dZ3z0d=F>}4MWq)6kcA~`^3vC&%H^jcc#kXqai-x ziv~JQ^zCERPv|t?O(BL)b#wS|vJ2<`p}zvBCmijhOZ#EBgJSE`l!ZAr!Kcg%eFGhVO+i3&g9u2H8h$nlvw5Hsl#05@P5}Z|%*nCi1OSg*wljbM+yQ`4OZP}(r%Z$H z_{<+bXr=JP@J+;t_?v3t{R=6Tft|%*fAi4Z!RkDht7jbzG}jx%_z*v-ll(ZLaY#&L zb4ha0(nEZ7U~~8YrT$owEYGG_xsyOh6IQJ?Z)L4F}RI;m^% zv+yA`vHeSk_@wz=Zvbaa`&rl^WLg~c5q3DM@eW`j`Cg(FCqQzgy|8znkJygd`L&W? zi{In0|Cy$JHk13`T$b{2=qY}^7<~1crhae1Y-jRq?vA(KY(57AWO$4<@=uNf&X zxScb)BGOw@DkMk}tI7k>#Aor4Ti6pF+&|Zk)H3d>JJ?pKsELMJJ0DdJhNtr6%U@xL z8LjkK_{oQ#s8GZlOL;Y?G4@HfY4aG?YY(qD#3;T6Bu5HG(reZH9nhr8umj#0FKF2b z$;pZFz)@r{;mds7p7R6lpqw5~N8~*I$<*NI^jPVEYLFMp?j7$Nou_C!H9(*{TgCf^on}L7h_K<5^;w1cNAAkESi82U>PuGTq@LhMH!I2<&8qC` zDefl??JwF6WaEkbt+yVmjD=A_**qcx!_5Mi7`9S{v)pO*(1an$x*9Twj3&V%DVqK; z4x6PtL`4s}_3S>mI7Q#LoZ(j8S7g21zjn>;CXM^7J)ODJx_(dZ+tIbLLf{J){cjbz zb1R9<0V*!OQwt~iM@z$d#bz1lDTKBZtzsep4WbHt2fZF&N;Q26VV|7lMs z$j}oBq>tkh`F4R+@Z#rvlyg~Z&^tO55(s%O<$I9uWm-JBlMbu46RU+m$CHs=1z;d< zvI{@QRdY2-u3gwz%Q?XrWcB!V_mk1p(??oqa|T`Zp_}jEn$*fDMK3_4D=#oIE_78A zmdCwdAYe&;!I^$k0Wg(tSwsOR5o^V1sv25qLF@s@aQXIOJTFiz#GaUK7sljr^K?8} zz!8;7j+$%nxHnyAEWgos*O4ul7d7Ke%h|t>WIo0&gJ_R0+PBO&Fm7_c{6cW0IHDDq z)(MX#Akc|3sQ!1yo*)q!p6GG7oX_%8ZHyO-Yb^rNE+%dTvv_w>`ef!tXm(`n8edu4 z=K}cPj7zcKHl6D9)uyB869C#L&#V-twdM4^T0H{Vl{lTIep6^z5#)(*W1G?z(xnEM zpjbRqoO*znb`K^n+1$6RC%SflMj2kbjp~?mnRwP} zyX)7@=5$KZAMajx%DYkhR%b(R<4%NgO(iGkm-L(;QwnW>&Cx>?9Y+wS33Eh}j*AKI^IxrE4J#9O*= zmL|FGV%ee;d*vd?%cZtrBWN2`S4-0lW2Z**+-`ZKzM{)zrE}Ww*b7E32gyfatjjg; z^kXS2-^!@B>Khw9b#s1ahUXRL-r3a6k%dnRjt!2s;p0d#;vH2Yk>eh;S;0MiX5+E8 zZmvt0uUsZ;5-!|-b!@n9cmLNm00$2z)ndnVv*-zlq~k>2W%^jBo4lK>F;VsfW4+p= zXj$}IN7a;M+QmnsGfAm#dl@wfcro_H@CfOOhh{5^=}>KQ5dsHxhu^qlR)R7)Q8vn} zzya?yb={$1JxWTE_EI^&Qu^Oxe>Vlv5MlGx zhp0%mqvhcP0r@7JOJVTM4UNW!xy8|UUqXI$8=3KcHi3%bC>e|1oltH!Uz&*{t_> zzjR(xNnfxz?tA9>Ifb(qUfg=1R=oYl+T&bXhrEL*HV__;g%NnK&=N{U4kLkK$yjM# zTx$9bF&_Y2*5d{Urm-sz00bxZ-OPz#ZMa>f_%TKZ2?I9f^JkzlZ#uPa93Px>A>Yul zNO*C*Nu%Y%#p+LCh%njZ>i~o<7D1miqv%ErTuNrof4bTWBPohmT}>7=Tr%8^I0eqt4{=Wj~{5pdU=?EWJTo!C{Y} zRza##IH7%lYCfYvweR*S1BCo_FF)yLAyt)*HP)|Qfngt83!Tr$%s;= zejrbru0D*Bs}#JPSO6BsiI<&!=o$3L6{Ox1gMvQu+;!SlMW{=?`09*t+M8E19WV44 zs~>N6yvouZ;gx>U?K#MUgLDq}juUyrQ22TUJBR{SSotKX9hi`F76lTEi!qh}8E@xs zP-sFYmm?>1^QCMGE4K~4(a!0@bIbx`WXI*?D3+dy3IWiC9+~Q~i9Ww~-Je$1S3aHE z*IljKD|TC{0ao3E1tKjlQ*Ki;aQy}rphgt1;z=Y5j>Q@4i z%hFN&=H@)3z9~Ex&P|k|9owuFdS{r64}TZ2ep)+1oOF#4bRgE5FSzxPvX7BwYj_>@N@8#>bX^pHp&!*P+Jx zVYCDXmJs+d5h6xys_FPGWwh~XE|8spi+YzGcJ0dIX|MEqIte?&W!kG+Ho{Nr%rSX! zGcdN_+n=77CgJ^@^A{u^l_rZpmqqYF zG=~9JKXYOoS=Y9YpU`$#_OOC$@2QWR39-GlyS2-VB;jr-I#oKEm0U}L(pxQGO^NEb!#`@hIKCq@qS+)s7ZRB zPAQK)RN_`~*;(MBZTEo94JX=r(mzS}f3@|yDWok^b~3UH@8>D@spGWc!)k_v$Rh#P@wJA4Z)+NB1_QqBtEoC^%!YY%Bxz zA|&M`&j)Q1O?+FHx@`4(m^91zFtOM-OS%dRn(Jf7+E0k^Z2Ye3?y49$%YGT2a%(<~>xKb`+);A) zhnHRZiMzc`0>!ycW|!S>UUqYNB>I`Ewe!A1`A*K6!qKVfY_7MF3C>SS z7O@MI6%6i~&A`ZUYr^q3h&R;*Nv#zkS4w1+^~QI`;QD#(A`He$ggCqHPSu}85IqTn zYW#JIHnZ?Kzm|>R-sOOMsK18&@AB6_DurcN5hHL^q$Q5LX$lc0#{B`C6OBeO>gnRZ zyAw=P_`8Vp)7o)QhVC$yx8sZ;uU#H|XHdaD=PVd|ItidpZX6bpi}7Vcn(VE&%MPDE z3)H)8efPQ^?wi`RIs_x5NZMKJ^=YSDs}Gmk7WCFZ(@pQwn$zvhKn6)n` z=Q>!(>#D0?t6!#;Ggt>n%xoZ9sa!c0;R+LQ_;}s7@5|@)PkpB^d-*>~vq^z=R)JFZ zZ1>;qz&f)a6HR>URNsBJErW!U5(Sh004iv z!*jxWvt&*PMDno|J4DRSA<_+J2QFi{#Mj1pKjYW(wu7& z?ky9-#ZwYqb*cvC#v>;iviNLBTty&V%UD~sh*An$z=qRjH8DrHM^YAF)(I?K%?4n* zC$s<|nT|$qp9q?YtzygRNqDK8zH7^irh=*4Qt$sOAhza1evBT1`gStpcVtpk%Q{E& z_HNDu9fCs%bC65P?tmU$+SVkqm9zGQLPXRAjy?mRwwvGr&D1tF9?O%qGLWs6`wvs7 z)nhdR0N^kv9{c;ZBv4z60W(`B0VaWeBk;o%mj3Mu^G7BDCV?$YfN2U_T7G8oOagx- zz%+$FGGSg}64=rNn5M9$*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2", + { + variants: { + orientation: { + horizontal: + "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none", + vertical: + "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none", + }, + }, + defaultVariants: { + orientation: "horizontal", + }, + } +) + +function ButtonGroup({ + className, + orientation, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +

+ ) +} + +function ButtonGroupText({ + className, + asChild = false, + ...props +}: React.ComponentProps<"div"> & { + asChild?: boolean +}) { + const Comp = asChild ? Slot : "div" + + return ( + + ) +} + +function ButtonGroupSeparator({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + ButtonGroup, + ButtonGroupSeparator, + ButtonGroupText, + buttonGroupVariants, +} diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx index 04ad22f..5206aab 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx @@ -1,131 +1,126 @@ +import { FingerprintIcon, WebcamIcon } from 'lucide-react'; +import { + memo, + useCallback, + useMemo, + useState +} from 'react'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Button } from '@/components/ui/button'; +import { + Item, + ItemActions, + ItemContent, + ItemDescription, + ItemMedia, + ItemTitle +} from '@/components/ui/item'; +import TPessoaCartaoForm from '@/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm'; +import GetNameInitials from '@/shared/actions/text/GetNameInitials'; +import WebCamDialog from '@/shared/components/webcam/WebCamDialog'; +import { useFingerTechIndexHook } from '@/shared/hooks/FingerTech/useFingerTechIndexHook'; -import { FingerprintIcon, WebcamIcon } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; +function TPessoaTableFormSubview({ + item_index, + data, + params, + form, +}: any) { -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Button } from "@/components/ui/button"; -import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@/components/ui/item"; -import TPessoaTableFormSubviewInterface from "@/packages/administrativo/interfaces/TPessoa/TPessoaTableFormSubviewInterface"; -import TPessoaCartaoForm from "@/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm"; -import GetNameInitials from "@/shared/actions/text/GetNameInitials"; -import WebCamDialog from "@/shared/components/webcam/WebCamDialog"; -import { useFingerTechIndexHook } from "@/shared/hooks/FingerTech/useFingerTechIndexHook"; + const [isWebCamOpenDialog, setIsWebCamOpenDialog] = useState(false) + const { base64, captureFingerTech } = useFingerTechIndexHook(); + const handleBiometria = useCallback(() => { -export default function TPessoaTableFormSubview({ params, servico, selectedTPessoa, form, index }: TPessoaTableFormSubviewInterface) { + console.log(captureFingerTech()) - const [isOpenWebcamDialog, setIsOpenWebcamDialog] = useState(false) - const [base64, setBase64] = useState('') - const [statusBiometria, setStatusBiometria] = useState(0) - const [classBiometriaButton, setClassBiometriaButton] = useState('') - const { base64: base64Biometria, captureFingerTech } = useFingerTechIndexHook() + }) - const handleCapture = (data: string) => { - setBase64(data) - }; - - const handleCaptureBiometria = useCallback(async () => { - - const response = await captureFingerTech() - - if (response) { - - setStatusBiometria(1) - - } - - }, []) - - const handleBiometriaButton = useCallback(async () => { - switch (statusBiometria) { + // Define a classe do botão de biometria com base no status, sem estado extra + const biometriaButtonClass = useMemo(() => { + switch (1) { case 0: // Amarelo (aviso) - setClassBiometriaButton('bg-amber-100 text-amber-700 border border-amber-300 hover:bg-amber-200 hover:text-amber-800'); - break; - + return 'bg-amber-100 text-amber-700 border border-amber-300 hover:bg-amber-200 hover:text-amber-800'; case 1: // Verde discreto - setClassBiometriaButton('bg-green-100 text-green-700 border border-green-300 hover:bg-green-200 hover:text-green-800'); - break; + return 'bg-green-100 text-green-700 border border-green-300 hover:bg-green-200 hover:text-green-800'; case 2: // Vermelho (erro) - setClassBiometriaButton('bg-red-100 text-red-700 border border-red-300 hover:bg-red-200 hover:text-red-800'); - break; - + return 'bg-red-100 text-red-700 border border-red-300 hover:bg-red-200 hover:text-red-800'; default: - break; + return ''; } - }, [statusBiometria]) - - useEffect(() => { - handleBiometriaButton() - }, [statusBiometria]) + }, []); return ( - -
+
- + - {GetNameInitials(selectedTPessoa.nome)} + {GetNameInitials(data.pessoa?.nome)} - {selectedTPessoa.cpf_cnpj} - {selectedTPessoa.nome} + {data.pessoa?.cpf_cnpj} - {data.pessoa?.nome} - {selectedTPessoa.email ? selectedTPessoa.email : 'Email não informado'} + {data.pessoa?.email || 'Email não informado'} - {/* Verifica se existe formulário de acordo com parâmetros */} - {params.map((param) => ( - // Verifica se dfeve aparecer os dados do cartão - Number(param.valor) == Number(servico.servico_tipo_id) && ( + {params + .filter((param) => Number(param.valor) === data.servico.servico_tipo_id) + .map((param) => ( - ) - ))} + ))} + - {servico.requer_biometria === 'S' && ( + {data.servico.requer_biometria === 'S' && ( )} - {servico.requer_biometria === 'S' && ( + {data.servico.requer_biometria && ( )} - {isOpenWebcamDialog && ( + {isWebCamOpenDialog && ( { setIsOpenWebcamDialog(false) }} - onSave={handleCapture} + isOpen={isWebCamOpenDialog} + onClose={() => { setIsWebCamOpenDialog(false) }} + onSave={() => { }} + key={1} /> )}
); -} \ No newline at end of file +} + +// Memo para evitar re-renderizações desnecessárias da subview +export default memo(TPessoaTableFormSubview); \ No newline at end of file diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts index 28879b7..dcba5f4 100644 --- a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -6,7 +6,7 @@ import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/ async function executeGCalculoServicoService(payload: GCalculoServicoInterface, data) { const response = await GCalculoServico(payload); - const item = PrepareTServicoItemPedidoCalculoResponse(response.data, data) + const item = PrepareTServicoItemPedidoCalculoResponse(response, data) return item; } diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts index 985c7ac..6e60e0e 100644 --- a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts +++ b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts @@ -1,60 +1,37 @@ -import TServicoItemPedidoAddInterface from "../../interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; -import TServicoItemPedidoAddResponseInterface from "../../interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; +import TServicoItemPedidoAddInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; +import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; export function PrepareTServicoItemPedidoCalculoResponse( result: any, data: TServicoItemPedidoAddInterface ) { - // Valida resposta básica do cálculo de emolumento - if (!result) { + if (result.status == 404 || result.status == 400) { return { - status: 400, - message: "Resposta inválida ao calcular emolumento.", + 'status': result.status, + 'message': result.detail }; } - // Emolumento obrigatório - if (!result.emolumento_id) { - return { - status: 400, - message: "Emolumento não informado na resposta.", - }; - } - - // Valida campos numéricos da resposta (não podem ser NaN) - const numericFields = [ - "valor_total", - "valor_emolumento", - "valor_fundos", - "taxa_judiciaria", - "valor_iss", - ] as const; - - for (const field of numericFields) { - const value = result[field]; - if (value !== null && value !== undefined && isNaN(Number(value))) { - return { - status: 400, - message: `Valor numérico inválido em '${field}'.`, - }; - } - } - const item: TServicoItemPedidoAddResponseInterface = { - emolumento_id: result.emolumento_id, - emolumento_item_id: result.emolumento_item_id ?? null, + emolumento_id: result.data.emolumento_id, + emolumento_item_id: result.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: result.valor_total ?? 0, - emolumento: result.valor_emolumento ?? 0, - fundesp: result.valor_fundos ?? 0, - taxa_judiciaria: result.taxa_judiciaria ?? 0, - valor_iss: result.valor_iss ?? 0, + valor: result.data.valor_total ?? 0, + emolumento: result.data.valor_emolumento ?? 0, + fundesp: result.data.valor_fundos ?? 0, + taxa_judiciaria: result.data.taxa_judiciaria ?? 0, + valor_iss: result.data.valor_iss ?? 0, + pessoa_id: data.pessoa.pessoa_id ?? null, + subview: { + servico: data.servico_tipo, + pessoa: data.pessoa, + } }; return item; diff --git a/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx b/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx index 60198ae..a08ca8c 100644 --- a/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx +++ b/src/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm.tsx @@ -53,7 +53,9 @@ export default function TPessoaCartaoForm({ index, form }: TPessoaCartaoFormInte field.onChange(parseNumberInput(e))} /> + onChange={(e) => field.onChange(parseNumberInput(e))} + defaultValue={field.value ?? ""} + /> @@ -69,7 +71,10 @@ export default function TPessoaCartaoForm({ index, form }: TPessoaCartaoFormInte Data de Abertura - + diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx deleted file mode 100644 index dcbf488..0000000 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormColumns.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { ColumnDef } from '@tanstack/react-table'; -import { Minus, Plus } from 'lucide-react'; - -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; -import GetCapitalize from '@/shared/actions/text/GetCapitalize'; -import { SortableHeader } from '@/shared/components/dataTable/SortableHeader'; - -/** Permite atualizar a linha externamente, caso o DataTable forneça meta.updateData */ -type TableMeta = { - updateData?: (rowIndex: number, columnId: string, value: unknown) => void; -}; - -export default function TServicoItemPedidoFormColumns(): ColumnDef[] { - return [ - // servico - { - accessorKey: 'servico', - header: ({ column }) => SortableHeader('Serviço / Tabela', column), - cell: ({ row }) => { - const data = row.original; - return ( -
-
-
- {GetCapitalize(data.descricao)} -
-
- {GetCapitalize(data.tabela)} -
-
-
- ); - }, - sortingFn: (a, b) => - (a.original.descricao?.toLowerCase() || '').localeCompare( - b.original.descricao?.toLowerCase() || '', - ), - }, - - // emolumento - { - accessorKey: 'emolumento', - header: ({ column }) => SortableHeader('Emolumento', column), - cell: ({ row }) =>
R$ {row.getValue('emolumento') || '---'}
, - }, - - // taxa_judiciaria - { - accessorKey: 'taxa_judiciaria', - header: ({ column }) => SortableHeader('Tx. Judiciária', column), - cell: ({ row }) =>
R$ {row.getValue('taxa_judiciaria') || '---'}
, - }, - - // fundesp - { - accessorKey: 'fundesp', - header: ({ column }) => SortableHeader('Fundesp 21%', column), - cell: ({ row }) =>
R$ {row.getValue('fundesp') || '---'}
, - }, - - // valor_iss - { - accessorKey: 'valor_iss', - header: ({ column }) => SortableHeader('ISS 5%', column), - cell: ({ row }) =>
R$ {row.getValue('valor_iss') || '---'}
, - }, - - // total - { - accessorKey: 'valor', - header: ({ column }) => SortableHeader('Total', column), - cell: ({ row }) =>
R$ {row.getValue('valor') || '---'}
, - }, - - { - accessorKey: 'quantidade', - header: ({ column }) => SortableHeader('Qtd.', column), - enableSorting: true, - cell: ({ row, table }) => { - return ( -
- {/* Botão diminuir */} - - - {/* Campo de entrada */} -
- -
- - {/* Botão aumentar */} - -
- ); - }, - } - - - ]; -} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx index 9e23bbf..1f5f866 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx @@ -1,24 +1,128 @@ 'use client'; -import TServicoItemPedidoTableInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoTableInterface'; -import { DataTable } from '@/shared/components/dataTable/DataTable'; +import { Minus, Plus } from 'lucide-react'; +import React, { memo } from 'react'; -import TServicoItemPedidoFormColumns from './TServicoItemPedidoFormColumns'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow +} from '@/components/ui/table'; +import TPessoaTableFormSubview from '@/packages/administrativo/components/TPessoa/TPessoaTableFormSubview'; +import TServicoItemPedidoFormTableInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface'; +import GetCapitalize from '@/shared/actions/text/GetCapitalize'; - -/** - * Componente principal da tabela de Naturezas - */ -export default function TServicoItemPedidoFormTable({ data }: TServicoItemPedidoTableInterface) { - const columns = TServicoItemPedidoFormColumns(); +function TServicoItemPedidoFormTableComponent({ + data, + form, + params +}: TServicoItemPedidoFormTableInterface) { return ( -
- +
+ + + + + Serviço / Tabela + + + Emolumento + + + Tx. Judiciária + + + Fundesp 21% + + + ISS 5% + + + Total + + Qtd. + + + + {data?.length ? ( + data.map((item, index) => { + return ( + + {/* Linha principal */} + + +
+
+
+ {GetCapitalize(item.descricao)} +
+
+ {GetCapitalize(item.tabela)} +
+
+
+
+ R$ {item.emolumento ?? '---'} + R$ {item.taxa_judiciaria ?? '---'} + R$ {item.fundesp ?? '---'} + R$ {item.valor_iss ?? '---'} + R$ {item.valor ?? '---'} + +
+ + + + + +
+
+
+ {/* SubView */} + {item.subview && ( + + + + + + )} +
+ ); + }) + ) : ( + + + Nenhum item encontrado. + + + )} +
+
); } + +export const TServicoItemPedidoFormTable = memo(TServicoItemPedidoFormTableComponent); \ No newline at end of file diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx index 3bd40f8..4a32ea2 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx @@ -1,61 +1,105 @@ 'use client'; + +import { BookmarkX, IdCardIcon, MoreHorizontalIcon, TicketIcon } from 'lucide-react'; + +import { Button } from '@/components/ui/button'; +import { ButtonGroup } from '@/components/ui/button-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import TServicoItemPedidoListInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface'; +import FormatMoney from '@/shared/actions/money/FormatMoney'; +import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; export default function TServicoItemPedidoList({ items }: TServicoItemPedidoListInterface) { - const money = (v: unknown) => - new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(v || 0)); - return ( - Itens: {items?.length} + + Itens: {items?.length} + {/* Altura máxima + scroll vertical */}
{items?.map((item) => ( -
+
{/* Descrição */} -
+

- {item.descricao} + {item.descricao} de {item.nome} -

# {item.servico_itempedido_id}
+ {/* Valores (grid compacto) */} -
+
+
Emolumento
-
{money(item.emolumento)}
+
{FormatMoney(item.emolumento)}
Tx. Judiciária
-
{money(item.taxa_judiciaria)}
+
{FormatMoney(item.taxa_judiciaria)}
ISS
-
{money(item.valor_iss)}
+
{FormatMoney(item.valor_iss)}
Fundesp
-
{money(item.fundesp)}
+
{FormatMoney(item.fundesp)}
Total
-
{money(item.valor)}
+
{FormatMoney(item.valor)}
+
+ +
+ + + + + + + + + + + + Imprimir Etiqueta + + + + + + Imprimir Cartão + + + + + + Estornar Item + + + + + +
+
))}
diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx index 3b5fecb..6e87f4d 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx @@ -2,8 +2,8 @@ import { DataTable } from '@/shared/components/dataTable/DataTable'; -import TServicoItemPedidoColumns from './TServicoItemPedidoColumns'; import TServicoItemPedidoTableInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoTableInterface'; +import TServicoItemPedidoColumns from './TServicoItemPedidoColumns'; /** * Componente principal da tabela de Naturezas diff --git a/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx b/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx new file mode 100644 index 0000000..f713e80 --- /dev/null +++ b/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx @@ -0,0 +1,28 @@ +import { Input } from "@/components/ui/input"; + +export function PessoaSubviewForm({ form, index, pessoa }) { + return ( +
+ +

+ Dados da Pessoa Selecionada +

+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ ); +} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx index 30dd726..ac68696 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx @@ -1,8 +1,8 @@ 'use client'; -import { CalendarIcon, ClockIcon, Pencil } from 'lucide-react'; -import { useCallback, useEffect } from 'react'; +import { BookmarkX, CalendarIcon, ReceiptText } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; @@ -16,14 +16,13 @@ import { FormatCPF } from '@/shared/actions/CPF/FormatCPF'; import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; import GetNameInitials from '@/shared/actions/text/GetNameInitials'; - - - +import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog'; export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPedidoInterface) { const { TServicoItemPedido, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() const { TServicoPedido, showTServicoPedido } = useTServicoPedidoShowHook() + const [isCancelServicoPedidoDialogOpen, setIsCancelServicoPedidoDialogOpen] = useState(false) const TServicoPedidoShowData = useCallback(async () => { const servicoPedido: TServicoPedidoInterface = { @@ -42,23 +41,19 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed await indexTServicoItemPedido(servicoPedido) }) + const handleCancelServicoPedidoOpenDialog = useCallback((state: boolean) => { + setIsCancelServicoPedidoDialogOpen(state) + }, []) + useEffect(() => { TServicoPedidoShowData() }, []) return (
-
-

- Pedido: #{TServicoPedido?.servico_pedido_id} -

-
- -
-
+

+ Pedido: #{TServicoPedido?.servico_pedido_id} +

{/* Main */}
@@ -99,18 +94,12 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed
-
- +
+ {FormatDateTime(TServicoPedido?.data_pedido)}
-
- - - 14:50:31 - -
@@ -141,17 +130,34 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed - Impressões + Controles - - + +
+ {/* Confirma o cancelamento do pedido */} + {isCancelServicoPedidoDialogOpen && ( + { handleCancelServicoPedidoOpenDialog(false) }} + onCancel={() => { handleCancelServicoPedidoOpenDialog(false) }} + /> + )}
) } \ No newline at end of file diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx index 05b8ea2..077e883 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; +import FormatMoney from '@/shared/actions/money/FormatMoney'; import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; import TServicoPedidoDetailsPagamentoInterface from '../../interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface'; @@ -13,28 +14,29 @@ export default function TServicoPedidoDetailsPagamento({ items, }: TServicoPedidoDetailsPagamentoInterface) { - // Formatação monetária (BRL) - const fmt = React.useMemo( - () => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }), - [] - ); - - // Helper para evitar NaN/undefined - const safe = (n: number | undefined | null) => - typeof n === 'number' && Number.isFinite(n) ? n : 0; - // Somas por tipo de valor const { emolumento, taxa_judiciaria, valor_iss, fundesp } = React.useMemo(() => { + return (items ?? []).reduce( + (acc, item) => { - acc.emolumento += safe(item.emolumento); - acc.taxa_judiciaria += safe(item.taxa_judiciaria); - acc.valor_iss += safe(item.valor_iss); - acc.fundesp += safe(item.fundesp); + + if (item.situacao === 'F') { + + acc.emolumento += item.emolumento; + acc.taxa_judiciaria += item.taxa_judiciaria; + acc.valor_iss += item.valor_iss; + acc.fundesp += item.fundesp; + + } + return acc; + }, { emolumento: 0, taxa_judiciaria: 0, valor_iss: 0, fundesp: 0 } + ); + }, [items]); // Total exibido = soma dos quatro componentes @@ -47,35 +49,57 @@ export default function TServicoPedidoDetailsPagamento({ Pagamento - -
+
Emolumento - {fmt.format(emolumento)} + {FormatMoney(emolumento)}
Tx. Judiciária - {fmt.format(taxa_judiciaria)} + {FormatMoney(taxa_judiciaria)}
ISS - {fmt.format(valor_iss)} + {FormatMoney(valor_iss)}
Fundesp - {fmt.format(fundesp)} + {FormatMoney(fundesp)} +
+ + + +
+ Total + {FormatMoney(total)}
- Total - {fmt.format(total)} + Emolumento + {FormatMoney(emolumento)}
+ +
+ Tx. Judiciária + {FormatMoney(taxa_judiciaria)} +
+ +
+ ISS + {FormatMoney(valor_iss)} +
+ +
+ Fundesp + {FormatMoney(fundesp)} +
+
diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index ef44577..6af3173 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -25,10 +25,9 @@ import { Switch } from '@/components/ui/switch'; import GEmolumentoServicoSelect from '@/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect'; import GUsuarioSelect from '@/packages/administrativo/components/GUsuario/GUsuarioSelect'; import TPessoaTableFormDialog from '@/packages/administrativo/components/TPessoa/TPessoaTableFormDialog'; -import TPessoaTableFormSubview from '@/packages/administrativo/components/TPessoa/TPessoaTableFormSubview'; 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 { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; import { useTServicoItemPedidoIndexHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook'; import { useTServicoItemPedidoLocalAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook'; @@ -51,7 +50,9 @@ import { } from '@/shared/components/step/stepNavigator'; import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; -import TServicoSubviewInterface from '../../interfaces/TServico/TServicoSubviewInterface'; +import TServicoItemPedidoAddInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; +import TServicoItemPedidoAddResponseInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; +import { TServicoItemPedidoFormTable } from '../TServicoItemPedido/TServicoItemPedidoFormTable'; export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedidoFormInterface) { @@ -76,7 +77,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const handleCloseSaveConfirm = useCallback(() => setIsSaveConfirmOpen(false), []); // Hooks - const { response, setResponse } = useResponse() + // const playSuccess = useSoundHook("/sounds/success.mp3"); + const { setResponse } = useResponse() const { saveTServicoPedido } = useTServicoPedidoSaveHook(); const { showTServicoPedido } = useTServicoPedidoShowHook(); const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue); @@ -107,6 +109,14 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // 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) { @@ -127,8 +137,12 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // 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({ servico_pedido_id: id }); + const response = await indexTServicoItemPedido(pedidoItens); // Verifica se os dados foram localizados if (response?.data?.length) { @@ -161,14 +175,16 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido }, [servico_pedido_id, showTServicoPedido, reset, fetchPedidoItens]); - const handleAddItemWithPessoa = useCallback(async (selectedTPessoa: any) => { + const handleAddItemWithPessoa = useCallback(async (selectedTPessoa: TPessoaInterface) => { + // Habilita o loading setIsAdding(true) // Constroi um novo item - const newItem: TServicoSubviewInterface = await addTServicoItemPedido({ + const newItem: TServicoItemPedidoAddResponseInterface = await addTServicoItemPedido({ servico_tipo: selectedServicoTipo, - emolumento: selectedEmolumento + emolumento: selectedEmolumento, + pessoa: selectedTPessoa }); // Verifica se existe um novo item @@ -177,19 +193,8 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Obtem o indice atual const index = TServicoItemPedidoLocal.length; - // Guarda o ID da pessoa - newItem.pessoa_id = selectedTPessoa.pessoa_id; - - // Cria a subview se necessário - newItem.subview = ( - - ); + // Define a posição do item + newItem.index = index // Atualiza o estado localAddTServicoItemPedido(newItem); @@ -197,6 +202,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Atualiza os itens do formulário form.setValue(`itens.${index}`, newItem); + // Desabilita o loading setIsAdding(false) }, [ @@ -206,7 +212,6 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido TServicoItemPedidoLocal.length, localAddTServicoItemPedido, form, - TServicoPedidoParams ]); // Controla o formulário de cancelamento de pedido @@ -251,7 +256,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido setIsAdding(true) // Prepara e valida os dados de item do pedido - const payload = { + const payload: TServicoItemPedidoAddInterface = { servico_tipo: selectedServicoTipo, emolumento: selectedEmolumento } @@ -303,7 +308,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido } const total = TServicoItemPedidoLocal.reduce((acc, item) => { - const valor = Number(item.valor_total ?? item.valor ?? 0); + const valor = Number(item.valor ?? 0); return acc + valor; }, 0); @@ -522,7 +527,10 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido />
- +
diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts index 6a87816..1d4f135 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts @@ -1,7 +1,8 @@ 'use client'; import { useState } from 'react'; -import { UseFormSetValue } from 'react-hook-form'; +import { FieldValues, UseFormSetValue } from 'react-hook-form'; + import { GCalculoServicoService } from '@/packages/administrativo/services/GCalculo/GCalculoServicoService'; import PrepareTServicoItemPedidoPayload from '@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload'; @@ -9,8 +10,7 @@ import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServ import { default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; import { useResponse } from '@/shared/components/response/ResponseContext'; - -export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue) => { +export function useTServicoItemPedidoAddHook(setValue?: UseFormSetValue) { const { setResponse } = useResponse(); @@ -25,13 +25,23 @@ export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue) => { + +export function useTServicoItemPedidoLocalAddHook(setValue?: UseFormSetValue) { const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState([]); - const localAddTServicoItemPedido = (item: any) => { + const localAddTServicoItemPedido = (item: TServicoItemPedidoAddResponseInterface) => { setLocalTServicoItemPedido((prev) => { diff --git a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts index 94866e0..39edf22 100644 --- a/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts +++ b/src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoFormHook.ts @@ -19,6 +19,7 @@ export function useTServicoPedidoFormHook(defaults?: Partial { const { setResponse } = useResponse(); - const [TServicoPedido, setTServicoPedido] = useState(null); + const [TServicoPedido, setTServicoPedido] = useState(); const showTServicoPedido = async (data: TServicoPedidoInterface) => { diff --git a/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts b/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts new file mode 100644 index 0000000..da03c69 --- /dev/null +++ b/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts @@ -0,0 +1,7 @@ +import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface"; +import TPessoaInterface from "@/packages/administrativo/interfaces/TPessoa/TPessoaInterface"; + +export default interface TServicoItemPedidoSubviewInterface { + servico: TServicoTipoInterface, + pessoa: TPessoaInterface +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts b/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts deleted file mode 100644 index 3364882..0000000 --- a/src/packages/servicos/interfaces/TServico/TServicoSubviewInterface.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export default interface TServicoSubviewInterface { - pessoa_id: number; - subview: any; -} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts index 1269f8f..80fa589 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts @@ -4,7 +4,4 @@ import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumen export default interface TServicoItemPedidoAddInterface { emolumento: GEmolumentoInterface; servico_tipo: TServicoTipoInterface; - sistema_id?: number; - valor_documento?: number; - quantidade?: number; } \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts index e451438..e5c0d50 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts @@ -1,4 +1,7 @@ +import TServicoItemPedidoSubviewInterface from "../TServico/TServicoItemPedidoSubviewInterface"; + export default interface TServicoItemPedidoAddResponseInterface { + index?: number; emolumento_id: number; emolumento_item_id: number; servico_tipo_id: number; @@ -12,4 +15,6 @@ export default interface TServicoItemPedidoAddResponseInterface { fundesp: number; taxa_judiciaria: number; valor_iss: number; + pessoa_id?: number; + subview?: TServicoItemPedidoSubviewInterface; } diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormInterface.ts deleted file mode 100644 index e418f33..0000000 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormInterface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TServicoItemPedidoFormValues } from '../../schemas/TServicoItemPedido/TServicoItemPedidoSchema'; - -export interface TServicoItemPedidoFormInterface { - isOpen: boolean; - data: TServicoItemPedidoFormValues | null; - onClose: (item: null, isFormStatus: boolean) => void; - onSave: (data: TServicoItemPedidoFormValues) => void; - buttonIsLoading: boolean; -} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts new file mode 100644 index 0000000..a4f2203 --- /dev/null +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts @@ -0,0 +1,10 @@ +import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; +import { TServicoPedidoFormInterface } from "@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface"; +import GConfigInterface from "@/shared/interfaces/GConfigInterface"; + + +export default interface TServicoItemPedidoFormTableInterface { + data: TServicoItemPedidoAddResponseInterface; + form: TServicoPedidoFormInterface; + params: GConfigInterface; +} \ No newline at end of file diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts index e9e92c4..efd2a21 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts @@ -53,6 +53,10 @@ export const TServicoPedidoFormSchema = z.object({ tipo_pagamento: z .any() .refine((v) => !!v, "Selecione a forma de pagamento."), + + situacao: z + .string() + .min(1, "Campo situação deve ser informado"), }); export type TServicoPedidoFormValues = z.infer; \ No newline at end of file diff --git a/src/shared/actions/money/FormatMoney.ts b/src/shared/actions/money/FormatMoney.ts new file mode 100644 index 0000000..9ad7fce --- /dev/null +++ b/src/shared/actions/money/FormatMoney.ts @@ -0,0 +1,5 @@ +export default function FormatMoney(data) { + + return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(data || 0)); + +} \ No newline at end of file diff --git a/src/shared/components/dataTable/DataTable.tsx b/src/shared/components/dataTable/DataTable.tsx index 498ccbc..4d49119 100644 --- a/src/shared/components/dataTable/DataTable.tsx +++ b/src/shared/components/dataTable/DataTable.tsx @@ -34,6 +34,7 @@ import { } from '@/components/ui/table'; import DataTableInterface from './interfaces/DataTableInterface'; +import DataTableSubview from './interfaces/DataTableSubview'; /** * DataTable genérico com suporte a subvisões dinâmicas (subtabelas ou detalhes). @@ -210,7 +211,7 @@ export function DataTable Rea colSpan={row.getVisibleCells().length} className="p-0" > - {subview} + )} diff --git a/src/shared/components/dataTable/interfaces/DataTableSubview.tsx b/src/shared/components/dataTable/interfaces/DataTableSubview.tsx new file mode 100644 index 0000000..ae2efb4 --- /dev/null +++ b/src/shared/components/dataTable/interfaces/DataTableSubview.tsx @@ -0,0 +1,11 @@ +'use client'; + +export default function DataTableSubview({ subview }) { + if (!subview) return null; + if (!subview.component) return null; + + const Component = subview.component; + const props = subview.props || {}; + + return ; +} diff --git a/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts b/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts index acce843..2674bca 100644 --- a/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts +++ b/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts @@ -4,10 +4,9 @@ import { useState } from 'react'; import { FingerTechCaptureService } from '@/shared/services/FingerTech/FingerTechCaptureService'; - export const useFingerTechIndexHook = () => { - const [base64, setBase64] = useState(''); + const [base64, setBase64] = useState(''); const captureFingerTech = async () => { diff --git a/src/shared/hooks/sound/useSoundHook.ts b/src/shared/hooks/sound/useSoundHook.ts new file mode 100644 index 0000000..45a2c71 --- /dev/null +++ b/src/shared/hooks/sound/useSoundHook.ts @@ -0,0 +1,18 @@ +'use client' + +import { useCallback, useRef } from "react"; + +export function useSoundHook(soundPath: string) { + + const audioRef = useRef(null); + + if (!audioRef.current) { + audioRef.current = new Audio(soundPath); + } + + const play = useCallback(() => { + audioRef.current?.play().catch(() => { }); + }, []); + + return play; +} From fdd4cf7cfc667f3ce92623969709c3d546ee2000 Mon Sep 17 00:00:00 2001 From: Keven Date: Mon, 17 Nov 2025 17:39:04 -0300 Subject: [PATCH 11/14] [MVPTN-37] fix(Pedido): Ajustes diversos na tela de pedido --- .../TPessoa/TPessoaTableFormSubview.tsx | 9 +- .../TPessoaTableFormSubviewInterface.ts | 11 ++ .../GCalculo/GCalculoServicoService.ts | 39 ++++- .../PrepareTServicoItemPedidoResponseItem.ts | 38 ----- .../TServicoPedidoItemPreparePayload.ts} | 16 +- .../TServicoItemPedidoColumns.tsx | 78 --------- .../TServicoItemPedidoForm.tsx | 110 ------------ .../TServicoItemPedidoFormTable.tsx | 18 +- .../TServicoItemPedidoIndex.tsx | 161 ------------------ .../TServicoItemPedidoTable.tsx | 23 --- .../TServicoPedido/PessoaSubviewForm.tsx | 28 --- .../TServicoPedido/TServicoPedidoForm.tsx | 13 +- ...ts => useTServicoItemPedidoCalculoHook.ts} | 29 +++- .../useTServicoItemPedidoIndexHook.ts | 2 +- .../useTServicoItemPedidoLocalAddHook.ts | 2 +- .../TPessoaCartaoFormInterface.ts | 6 +- .../interfaces/TServico/TServicoInterface.ts | 2 +- .../TServicoItemPedidoSubviewInterface.ts | 6 +- .../TServicoItemPedidoAddInterface.ts | 23 ++- ...vicoItemPedidoCalculoResponseInterface.ts} | 11 +- .../TServicoItemPedidoFormTableInterface.ts | 13 +- .../TServicoItemPedidoIntefarce.ts | 154 ++++++++++------- .../TServicoItemPedidoListInterface.ts | 2 +- ...TServicoPedidoDetailsPagamentoInterface.ts | 2 +- .../TServicoPedidoFormInterface.ts | 9 +- .../TServicoPedido/TServicoPedidoInterface.ts | 50 +++++- .../TServicoPedidoFormSchema.ts | 5 + src/shared/components/dataTable/DataTable.tsx | 51 ++---- .../dataTable/interfaces/DataTableSubview.tsx | 11 -- 29 files changed, 298 insertions(+), 624 deletions(-) create mode 100644 src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormSubviewInterface.ts delete mode 100644 src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts rename src/packages/servicos/actions/{TServicoPedido/PrepareTServicoItemPedidoPayload.ts => TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts} (68%) delete mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoColumns.tsx delete mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoForm.tsx delete mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoIndex.tsx delete mode 100644 src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx delete mode 100644 src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx rename src/packages/servicos/hooks/TServicoItemPedido/{useTServicoItemPedidoAddHook.ts => useTServicoItemPedidoCalculoHook.ts} (65%) rename src/packages/servicos/interfaces/TServicoItemPedido/{TServicoItemPedidoAddResponseInterface.ts => TServicoItemPedidoCalculoResponseInterface.ts} (76%) delete mode 100644 src/shared/components/dataTable/interfaces/DataTableSubview.tsx diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx index 5206aab..95c06f1 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx @@ -21,21 +21,24 @@ import GetNameInitials from '@/shared/actions/text/GetNameInitials'; import WebCamDialog from '@/shared/components/webcam/WebCamDialog'; import { useFingerTechIndexHook } from '@/shared/hooks/FingerTech/useFingerTechIndexHook'; +import TPessoaTableFormSubviewInterface from '../../interfaces/TPessoa/TPessoaTableFormSubviewInterface'; + function TPessoaTableFormSubview({ item_index, data, params, form, -}: any) { +}: TPessoaTableFormSubviewInterface) { const [isWebCamOpenDialog, setIsWebCamOpenDialog] = useState(false) const { base64, captureFingerTech } = useFingerTechIndexHook(); + // Chama o leitor biométrico const handleBiometria = useCallback(() => { console.log(captureFingerTech()) - }) + }, []) // Define a classe do botão de biometria com base no status, sem estado extra const biometriaButtonClass = useMemo(() => { @@ -115,7 +118,7 @@ function TPessoaTableFormSubview({ isOpen={isWebCamOpenDialog} onClose={() => { setIsWebCamOpenDialog(false) }} onSave={() => { }} - key={1} + key={item_index} /> )}
diff --git a/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormSubviewInterface.ts b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormSubviewInterface.ts new file mode 100644 index 0000000..7c44173 --- /dev/null +++ b/src/packages/administrativo/interfaces/TPessoa/TPessoaTableFormSubviewInterface.ts @@ -0,0 +1,11 @@ +import { UseFormReturn } from "react-hook-form"; + +import { TServicoPedidoFormValues } from "@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema"; +import GConfigInterface from "@/shared/interfaces/GConfigInterface"; + +export default interface TPessoaTableFormSubviewInterface { + item_index: number, + data: any, + params: GConfigInterface[], + form: UseFormReturn; +} \ No newline at end of file diff --git a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts index dcba5f4..02d6395 100644 --- a/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts +++ b/src/packages/administrativo/services/GCalculo/GCalculoServicoService.ts @@ -1,12 +1,45 @@ import { GCalculoServico } from "@/packages/administrativo/data/GCalculo/GCalculoServicoData"; import GCalculoServicoInterface from "@/packages/administrativo/interfaces/GCalculo/GCalculoServicoInterface"; -import { PrepareTServicoItemPedidoCalculoResponse } from "@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem"; +import TServicoItemPedidoAddInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler"; -async function executeGCalculoServicoService(payload: GCalculoServicoInterface, data) { +async function executeGCalculoServicoService(payload: GCalculoServicoInterface, data: TServicoItemPedidoAddInterface) { + const response = await GCalculoServico(payload); - const item = PrepareTServicoItemPedidoCalculoResponse(response, data) + + 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: { + servico: data.servico_tipo, + pessoa: data.pessoa, + } + }; + return item; } diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts b/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts deleted file mode 100644 index 6e60e0e..0000000 --- a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoResponseItem.ts +++ /dev/null @@ -1,38 +0,0 @@ -import TServicoItemPedidoAddInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; -import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; - -export function PrepareTServicoItemPedidoCalculoResponse( - result: any, - data: TServicoItemPedidoAddInterface -) { - - if (result.status == 404 || result.status == 400) { - return { - 'status': result.status, - 'message': result.detail - }; - } - - const item: TServicoItemPedidoAddResponseInterface = { - emolumento_id: result.data.emolumento_id, - emolumento_item_id: result.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: result.data.valor_total ?? 0, - emolumento: result.data.valor_emolumento ?? 0, - fundesp: result.data.valor_fundos ?? 0, - taxa_judiciaria: result.data.taxa_judiciaria ?? 0, - valor_iss: result.data.valor_iss ?? 0, - pessoa_id: data.pessoa.pessoa_id ?? null, - subview: { - servico: data.servico_tipo, - pessoa: data.pessoa, - } - }; - - return item; -} diff --git a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts similarity index 68% rename from src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts rename to src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts index 524e76b..34d6361 100644 --- a/src/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload.ts +++ b/src/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload.ts @@ -1,8 +1,8 @@ import TServicoItemPedidoAddInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface"; -export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedidoAddInterface) { +export default function TServicoPedidoItemPreparePayload(data: TServicoItemPedidoAddInterface) { - data.quantidade = 1 + data.qtd = 1 data.valor_documento = 0 // Verifica dados obrigatórios de serviço e emolumento @@ -14,25 +14,17 @@ export default function PrepareTServicoItemPedidoPayload(data: TServicoItemPedid } // Valida sistema_id (padrão 2, mas ainda assim precisa ser válido) - if (!data?.emolumento.sistema_id || data.servico_tipo.sistema_id <= 0) { + if (!data?.emolumento.sistema_id || data.servico_tipo.servico_tipo_id <= 0) { return { status: 400, message: 'Sistema inválido ou não informado.' }; } - // Valida quantidade (padrão 1, precisa ser >= 1) - if (Number(data.quantidade) < 1) { - return { - status: 400, - message: 'Quantidade inválida.' - }; - } - return { sistema_id: data.emolumento.sistema_id, valor_documento: data.valor_documento, - quantidade: data.quantidade, + quantidade: data.qtd, emolumento_id: data.emolumento.emolumento_id, }; diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoColumns.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoColumns.tsx deleted file mode 100644 index b48305c..0000000 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoColumns.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { ColumnDef } from '@tanstack/react-table'; -import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react'; - -import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import GetCapitalize from '@/shared/actions/text/GetCapitalize'; -import { SortableHeader } from '@/shared/components/dataTable/SortableHeader'; - -import TServicoItemPedidoInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; - -export default function TServicoItemPedidoColumns( - onEdit: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, - onDelete: (item: TServicoItemPedidoInterface, isEditingFormStatus: boolean) => void, -): ColumnDef[] { - return [ - // ID - { - accessorKey: 'gramatica_id', - header: ({ column }) => SortableHeader('ID', column), - cell: ({ row }) => Number(row.getValue('gramatica_id')), - enableSorting: true, - }, - - // Descrição - { - accessorKey: 'palavra', - header: ({ column }) => SortableHeader('Palavra', column), - cell: ({ row }) => GetCapitalize(String(row.getValue('palavra') || '')), - }, - - // Ações - { - id: 'actions', - header: 'Ações', - cell: ({ row }) => { - const natureza = row.original; - - return ( - - - - - - - - onEdit(natureza, true)}> - - Editar - - - - - onDelete(natureza, true)} - > - - Remover - - - - - ); - }, - enableSorting: false, - enableHiding: false, - }, - ]; -} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoForm.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoForm.tsx deleted file mode 100644 index d943bbc..0000000 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoForm.tsx +++ /dev/null @@ -1,110 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; - -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData'; -import LoadingButton from '@/shared/components/loadingButton/LoadingButton'; - -import { useTServicoItemPedidoFormHook } from '../../hooks/TServicoItemPedido/useTServicoItemPedidoFormHook'; -import { TServicoItemPedidoFormInterface } from '../../interfaces/TServicoItemPedido/TServicoItemPedidoFormInterface'; - -/** - * Formulário de cadastro/edição de Natureza - * Baseado nos campos da tabela G_NATUREZA - */ -export default function TServicoItemPedidoForm({ - isOpen, - data, - onClose, - onSave, - buttonIsLoading, -}: TServicoItemPedidoFormInterface) { - const form = useTServicoItemPedidoFormHook({}); - - // Atualiza o formulário quando recebe dados para edição - useEffect(() => { - ResetFormIfData(form, data); - }, [data, form]); - - function onError(error: any) { - console.log('Erro no formulário:', error); - } - - return ( - { - if (!open) onClose(null, false); - }} - > - - - Formulário de Gramática - - Formulário de Gramática - - - {/* Formulário principal */} -
- - {/* GRID MOBILE FIRST */} -
- {/* Palavra */} -
- ( - - Palavra - - - - - - )} - /> -
-
- {/* Rodapé */} - - - - - - -
- -
-
- ); -} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx index 1f5f866..859465e 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoFormTable.tsx @@ -74,23 +74,11 @@ function TServicoItemPedidoFormTableComponent({ R$ {item.valor ?? '---'}
- - - -
@@ -101,7 +89,7 @@ function TServicoItemPedidoFormTableComponent({ (null); - const [isFormOpen, setIsFormOpen] = useState(false); - - // Estado para saber qual item será deletado - const [itemToDelete, setItemToDelete] = useState(null); - - /** - * Hook do modal de confirmação - */ - const { isOpen: isConfirmOpen, openDialog: openConfirmDialog, handleCancel } = useConfirmDialog(); - - /** - * Abre o formulário no modo de edição ou criação - */ - const handleOpenForm = useCallback((data: TServicoItemPedidoInterface | null) => { - setSelectedData(data); - setIsFormOpen(true); - }, []); - - /** - * Fecha o formulário e limpa o andamento selecionado - */ - const handleCloseForm = useCallback(() => { - setSelectedData(null); - setIsFormOpen(false); - }, []); - - /** - * Salva os dados do formulário - */ - const handleSave = useCallback( - async (formData: TServicoItemPedidoInterface) => { - // Coloca o botão em estado de loading - setButtonIsLoading(true); - - // Aguarda salvar o registro - await saveTServicoItemPedido(formData); - - // Remove o botão em estado de loading - setButtonIsLoading(false); - - // Atualiza a lista de dados - indexTServicoItemPedido(); - }, - [saveTServicoItemPedido, indexTServicoItemPedido, handleCloseForm], - ); - - /** - * Quando o usuário clica em "remover" na tabela - */ - const handleConfirmDelete = useCallback( - (item: TServicoItemPedidoInterface) => { - // Define o item atual para remoção - setItemToDelete(item); - // Abre o modal de confirmação - openConfirmDialog(); - }, - [openConfirmDialog], - ); - - /** - * Executa a exclusão de fato quando o usuário confirma - */ - const handleDelete = useCallback(async () => { - // Protege contra null - if (!itemToDelete) return; - - // Executa o Hook de remoção - await deleteTServicoItemPedido(itemToDelete); - - // Atualiza a lista - await indexTServicoItemPedido(); - - // Limpa o item selecionado - setItemToDelete(null); - - // Fecha o modal - handleCancel(); - }, [itemToDelete, indexTServicoItemPedido, handleCancel]); - - /** - * Busca inicial dos dados - */ - useEffect(() => { - indexTServicoItemPedido(); - }, []); - - /** - * Tela de loading enquanto carrega os dados - */ - if (TServicoItemPedido?.length == 0) { - return ; - } - - return ( -
- {/* Cabeçalho */} -
{ - handleOpenForm(null); - }} - /> - {/* Tabela de andamentos */} - - {/* Modal de confirmação */} - {isConfirmOpen && ( - - )} - {/* Formulário de criação/edição */} - {isFormOpen && ( - - )} -
- ); -} diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx deleted file mode 100644 index 6e87f4d..0000000 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoTable.tsx +++ /dev/null @@ -1,23 +0,0 @@ -'use client'; - -import { DataTable } from '@/shared/components/dataTable/DataTable'; - -import TServicoItemPedidoTableInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoTableInterface'; -import TServicoItemPedidoColumns from './TServicoItemPedidoColumns'; - -/** - * Componente principal da tabela de Naturezas - */ -export default function TServicoItemPedidoTable({ data, onEdit, onDelete }: TServicoItemPedidoTableInterface) { - const columns = TServicoItemPedidoColumns(onEdit, onDelete); - return ( -
- -
- ); -} diff --git a/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx b/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx deleted file mode 100644 index f713e80..0000000 --- a/src/packages/servicos/components/TServicoPedido/PessoaSubviewForm.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Input } from "@/components/ui/input"; - -export function PessoaSubviewForm({ form, index, pessoa }) { - return ( -
- -

- Dados da Pessoa Selecionada -

- -
- -
- - -
- -
- - -
- -
- -
- ); -} diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx index 6af3173..2cc3b78 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoForm.tsx @@ -28,13 +28,15 @@ import TPessoaTableFormDialog from '@/packages/administrativo/components/TPessoa 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 { useTServicoItemPedidoAddHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook'; +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 { TServicoPedidoFormInterface } from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface'; import TServicoPedidoInterface from '@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface'; import { FormatCPFCNPJForm } from '@/shared/actions/CPF/FormatCPFCNPJForm'; @@ -50,9 +52,6 @@ import { } from '@/shared/components/step/stepNavigator'; import TipoPagamentoSelect from '@/shared/components/tipoPagamento/TipoPagamentoSelect'; -import TServicoItemPedidoAddInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; -import TServicoItemPedidoAddResponseInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; -import { TServicoItemPedidoFormTable } from '../TServicoItemPedido/TServicoItemPedidoFormTable'; export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedidoFormInterface) { @@ -82,7 +81,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido const { saveTServicoPedido } = useTServicoPedidoSaveHook(); const { showTServicoPedido } = useTServicoPedidoShowHook(); const { TServicoItemPedidoLocal, localAddTServicoItemPedido, setLocalTServicoItemPedido } = useTServicoItemPedidoLocalAddHook(setValue); - const { addTServicoItemPedido } = useTServicoItemPedidoAddHook(setValue); + const { addTServicoItemPedido } = useTServicoItemPedidoCalculoHook(setValue); const { indexTServicoItemPedido } = useTServicoItemPedidoIndexHook(); const { TServicoPedidoParams, loadParamsTServicoPedido } = useTServicoPedidoLoadParamsHook(); @@ -181,7 +180,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido setIsAdding(true) // Constroi um novo item - const newItem: TServicoItemPedidoAddResponseInterface = await addTServicoItemPedido({ + const newItem = await addTServicoItemPedido({ servico_tipo: selectedServicoTipo, emolumento: selectedEmolumento, pessoa: selectedTPessoa @@ -324,6 +323,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Dispara a busca do pedido useEffect(() => { + // Se existir pedido_id, busca o pedido if (servico_pedido_id) fetchPedido(); }, [servico_pedido_id, fetchPedido]); @@ -331,6 +331,7 @@ export default function TServicoPedidoForm({ servico_pedido_id }: TServicoPedido // Dispara a busca de itens useEffect(() => { + // Dispara a busca dos itens setValue('itens', TServicoItemPedidoLocal, { shouldDirty: true }); }, [TServicoItemPedidoLocal, setValue]); diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts similarity index 65% rename from src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts rename to src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts index 1d4f135..7a1646b 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCalculoHook.ts @@ -5,21 +5,23 @@ import { FieldValues, UseFormSetValue } from 'react-hook-form'; import { GCalculoServicoService } from '@/packages/administrativo/services/GCalculo/GCalculoServicoService'; -import PrepareTServicoItemPedidoPayload from '@/packages/servicos/actions/TServicoPedido/PrepareTServicoItemPedidoPayload'; +import TServicoPedidoItemPreparePayload from '@/packages/servicos/actions/TServicoPedidoItem/TServicoPedidoItemPreparePayload'; import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface'; -import { default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; +import TServicoItemPedidoCalculoResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; import { useResponse } from '@/shared/components/response/ResponseContext'; -export function useTServicoItemPedidoAddHook(setValue?: UseFormSetValue) { + + +export function useTServicoItemPedidoCalculoHook(setValue?: UseFormSetValue) { const { setResponse } = useResponse(); - const [TServicoItemPedido, setTServicoItemPedido] = useState([]); + const [TServicoItemPedido, setTServicoItemPedido] = useState([]); const addTServicoItemPedido = async (data: TServicoItemPedidoAddInterface) => { // Trata os dados do payload - const payload = PrepareTServicoItemPedidoPayload(data); + const payload = TServicoPedidoItemPreparePayload(data); if (payload.status) { @@ -42,14 +44,23 @@ export function useTServicoItemPedidoAddHook(se } - // Obtem o resultado da busca - const item = response; + // Verifica se tem status de erro + if (response.status) { + + return { + + 'status': response.status, + 'message': 'Erro ao processar dados' + + }; + + } // Atualiza o estado com fallback seguro setTServicoItemPedido((prev) => { const safePrev = Array.isArray(prev) ? prev : []; - const novoArray = [...safePrev, item]; + const novoArray = [...safePrev, response]; if (setValue) { @@ -62,7 +73,7 @@ export function useTServicoItemPedidoAddHook(se }); - return item; + return response; }; diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts index ddfe105..79d74b6 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoIndexHook.ts @@ -2,7 +2,7 @@ import { useState } from 'react'; -import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; +import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import { TServicoItemPedidoIndexService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoIndexService'; import { useResponse } from '@/shared/components/response/ResponseContext'; diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts index 337073b..cad206b 100644 --- a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoLocalAddHook.ts @@ -3,7 +3,7 @@ import { useState } from 'react'; import { FieldValues, UseFormSetValue } from 'react-hook-form'; -import { default as TServicoItemPedidoAddResponseInterface, default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface'; +import { default as TServicoItemPedidoAddResponseInterface, default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface'; export function useTServicoItemPedidoLocalAddHook(setValue?: UseFormSetValue) { diff --git a/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts b/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts index 6b76cda..4fda09d 100644 --- a/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts +++ b/src/packages/servicos/interfaces/TPessoaCartao/TPessoaCartaoFormInterface.ts @@ -1,4 +1,8 @@ +import { UseFormReturn } from "react-hook-form"; + +import { TServicoPedidoFormValues } from "@/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema"; + export default interface TPessoaCartaoFormInterface { index: number; - form: any; + form: UseFormReturn; } \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServico/TServicoInterface.ts b/src/packages/servicos/interfaces/TServico/TServicoInterface.ts index 5771b3b..a52ef70 100644 --- a/src/packages/servicos/interfaces/TServico/TServicoInterface.ts +++ b/src/packages/servicos/interfaces/TServico/TServicoInterface.ts @@ -1,4 +1,4 @@ export default interface TServicoInterface { servico_pedido_id: number; escrevente_id: number; -} \ No newline at end of file +} diff --git a/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts b/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts index da03c69..bacbf7f 100644 --- a/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts +++ b/src/packages/servicos/interfaces/TServico/TServicoItemPedidoSubviewInterface.ts @@ -2,6 +2,6 @@ import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_inte import TPessoaInterface from "@/packages/administrativo/interfaces/TPessoa/TPessoaInterface"; export default interface TServicoItemPedidoSubviewInterface { - servico: TServicoTipoInterface, - pessoa: TPessoaInterface -} \ No newline at end of file + servico: TServicoTipoInterface; + pessoa: TPessoaInterface; +} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts index 80fa589..45367db 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface.ts @@ -1,7 +1,28 @@ import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface"; import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface"; +import TPessoaInterface from "@/packages/administrativo/interfaces/TPessoa/TPessoaInterface"; export default interface TServicoItemPedidoAddInterface { + /** + * Índice local na lista de itens (usado no front) + */ + index?: number; + + qtd?: number; + valor_documento?: number; + + /** + * Emolumento selecionado + */ emolumento: GEmolumentoInterface; + + /** + * Serviço/tipo de serviço selecionado + */ servico_tipo: TServicoTipoInterface; -} \ No newline at end of file + + /** + * Pessoa vinculada ao item (quando aplicável) + */ + pessoa?: TPessoaInterface; +} diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface.ts similarity index 76% rename from src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts rename to src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface.ts index e5c0d50..3ccd7b1 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface.ts @@ -1,20 +1,29 @@ import TServicoItemPedidoSubviewInterface from "../TServico/TServicoItemPedidoSubviewInterface"; -export default interface TServicoItemPedidoAddResponseInterface { +export default interface TServicoItemPedidoCalculoResponseInterface { index?: number; + emolumento_id: number; emolumento_item_id: number; servico_tipo_id: number; + tipo_item: string; descricao: string; tabela: string; situacao: string; + qtd: number; valor: number; + emolumento: number; fundesp: number; taxa_judiciaria: number; valor_iss: number; + pessoa_id?: number; + + /** + * Subview com serviço + pessoa (usado na linha expandida) + */ subview?: TServicoItemPedidoSubviewInterface; } diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts index a4f2203..a4d96f1 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoFormTableInterface.ts @@ -1,10 +1,13 @@ -import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; -import { TServicoPedidoFormInterface } from "@/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface"; +import { UseFormReturn } from "react-hook-form"; + +import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface"; import GConfigInterface from "@/shared/interfaces/GConfigInterface"; +import { TServicoPedidoFormValues } from "../../schemas/TServicoPedido/TServicoPedidoFormSchema"; + export default interface TServicoItemPedidoFormTableInterface { - data: TServicoItemPedidoAddResponseInterface; - form: TServicoPedidoFormInterface; - params: GConfigInterface; + data: TServicoItemPedidoAddResponseInterface[]; + form: UseFormReturn; + params: GConfigInterface[]; } \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce.ts index a8746c5..3622224 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce.ts @@ -1,67 +1,91 @@ +import TServicoItemPedidoSubviewInterface from "../TServico/TServicoItemPedidoSubviewInterface"; + export default interface TServicoItemPedidoInterface { - servico_itempedido_id?: number - servico_pedido_id?: number - servico_tipo_id?: number - valor?: number - qtd?: number - pessoa_id?: number - impressao_etiqueta?: string - situacao?: string - etiqueta_numero?: number - pessoa_auxiliar_id?: number - pessoa_sp_abono_rep?: string - tipo_item?: string - imprimir?: string - observacao?: string - impressao_direta?: string - selo_livro_id?: number - emolumento?: number - fundesp?: number - taxa_judiciaria?: number - desconto?: number - desc_complementar?: string - valor_manual?: string - valor_documento?: number - outra_taxa1?: number - emolumento_item_id?: number - certidao_impressa?: string - certidao_ato_id?: number - emolumento_id?: number - certidao_previsao?: string | Date - certidao_ato_antigo?: string - certidao_data_emissao?: string | Date - certidao_texto?: string | null - ato_antigo_tipo?: string - valor_iss?: number - id_ato_isentado?: number - motivo_isencao?: string - pessoas_etiquetas?: number - abonador?: string - servico_cartao?: string - valor_informacoes_centrais?: number - situacao_diferido?: string - sigla_numero?: string - motivo_diferido?: string - nome_juridico?: string - etiqueta_apenas_frente?: string - indexacao_id?: number - certidao_data_lavratura?: string | Date - nfse_id?: number - qtd_pagina_certidao?: number - placa?: string - dut?: string - etiqueta_unica?: string - fundo_abonador?: string - instrumento_publico?: string - data_lavratura_abono?: string | Date - valor_base_calculo?: number - valor_avaliacao?: number - ato_abonado?: number - transferencia_veiculo?: string - usar_a4?: string - cpf_abono_rep?: string - vrcext?: number - valor_fundo_selo?: number - averbacao?: string - descricao?: string + servico_itempedido_id?: number; + servico_pedido_id?: number; + servico_tipo_id?: number; + + valor?: number; + qtd?: number; + pessoa_id?: number; + + impressao_etiqueta?: string; + situacao?: string; + etiqueta_numero?: number; + pessoa_auxiliar_id?: number; + pessoa_sp_abono_rep?: string; + tipo_item?: string; + imprimir?: string; + observacao?: string; + impressao_direta?: string; + + // 🔹 Valores financeiros e emolumentos + selo_livro_id?: number; + emolumento?: number; + fundesp?: number; + taxa_judiciaria?: number; + desconto?: number; + desc_complementar?: string; + valor_manual?: string; + valor_documento?: number; + outra_taxa1?: number; + emolumento_item_id?: number; + emolumento_id?: number; + valor_iss?: number; + valor_informacoes_centrais?: number; + id_ato_isentado?: number; + motivo_isencao?: string; + valor_base_calculo?: number; + valor_avaliacao?: number; + vrcext?: number; + valor_fundo_selo?: number; + + // 🔹 Certidão + certidao_impressa?: string; + certidao_ato_id?: number; + certidao_previsao?: string | Date; + certidao_ato_antigo?: string; + certidao_data_emissao?: string | Date; + certidao_texto?: string | null; + ato_antigo_tipo?: string; + pessoas_etiquetas?: number; + qtd_pagina_certidao?: number; + + // 🔹 Situações / Diferimento / Notas + situacao_diferido?: string; + motivo_diferido?: string; + averbacao?: string; + + // 🔹 Dados auxiliares + abonador?: string; + servico_cartao?: string; + nome_juridico?: string; + etiqueta_apenas_frente?: string; + indexacao_id?: number; + certidao_data_lavratura?: string | Date; + nfse_id?: number; + placa?: string; + dut?: string; + etiqueta_unica?: string; + fundo_abonador?: string; + instrumento_publico?: string; + data_lavratura_abono?: string | Date; + ato_abonado?: number; + transferencia_veiculo?: string; + usar_a4?: string; + cpf_abono_rep?: string; + + // 🔹 Campos usados na UI + descricao?: string; + tabela?: string; + + /** + * Índice local na lista (usado no front para map e controle) + */ + index?: number; + + /** + * Subview: serviço + pessoa vinculada ao item + */ + subview?: TServicoItemPedidoSubviewInterface; } diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts index e21182d..7fd8f9f 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts @@ -1,4 +1,4 @@ -import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoAddResponseInterface"; +import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoCalculoResponseInterface"; export default interface TServicoItemPedidoListInterface { items: TServicoItemPedidoIndexResponseInterface[]; diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts index 0263e71..4630c0c 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoDetailsPagamentoInterface.ts @@ -1,4 +1,4 @@ -import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoAddResponseInterface"; +import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface"; export default interface TServicoPedidoDetailsPagamentoInterface { diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts index 97bdab4..eea5c91 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoFormInterface.ts @@ -1,4 +1,7 @@ - export interface TServicoPedidoFormInterface { - servico_pedido_id?: number -} + /** + * ID opcional do pedido, usado para modo edição + * - Se não informado, o formulário funciona em modo "novo pedido" + */ + servico_pedido_id?: number; +} \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts index 7111f28..ff67371 100644 --- a/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts +++ b/src/packages/servicos/interfaces/TServicoPedido/TServicoPedidoInterface.ts @@ -1,8 +1,22 @@ +import TServicoTipoInterface from "@/app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface"; +import GEmolumentoInterface from "@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface"; +import TServicoItemPedidoAddResponseInterface from "@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoCalculoResponseInterface"; export default interface TServicoPedidoInterface { + /** + * Chave primária do pedido + */ servico_pedido_id?: number; + + /** + * Valores financeiros do pedido + */ valor_pedido?: number; valor_pago?: number; + + /** + * Dados de controle + */ usuario_id?: number; data_pedido?: string; mensalista_livrocaixa_id?: number; @@ -11,16 +25,38 @@ export default interface TServicoPedidoInterface { situacao?: string; estornado?: string; nfse_id?: number; + + /** + * Apresentante / Pessoa selo + */ apresentante?: string; cpfcnpj_apresentante?: string; selo_pessoa_nome?: string; selo_pessoa_cpfcnpj?: string; + + /** + * Meta-informações de usuário/funcão (quando retornado pela API) + */ login?: string; funcao?: string; - itens?: []; - servico_tipo: object; - emolumento: object; - pagador_nome?: string, - pagador_cpfcnpj?: string, - tipo_pagamento?: number, -} \ No newline at end of file + + /** + * Serviço e emolumento selecionados no front. + * - No backend, provavelmente só IDs são gravados, + * mas no front trabalhamos com o objeto completo. + */ + servico_tipo: TServicoTipoInterface; + emolumento: GEmolumentoInterface; + + /** + * Itens do pedido (array tipado, e não mais []) + */ + itens?: TServicoItemPedidoAddResponseInterface[]; + + /** + * Dados de pagamento + */ + pagador_nome?: string; + pagador_cpfcnpj?: string; + tipo_pagamento?: number; +} diff --git a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts index efd2a21..3a9e490 100644 --- a/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts +++ b/src/packages/servicos/schemas/TServicoPedido/TServicoPedidoFormSchema.ts @@ -1,6 +1,11 @@ import z from "zod"; export const TServicoPedidoFormSchema = z.object({ + + servico_pedido_id: z + .number() + .optional(), + escrevente_id: z .number() .int() diff --git a/src/shared/components/dataTable/DataTable.tsx b/src/shared/components/dataTable/DataTable.tsx index 4d49119..68aaa5d 100644 --- a/src/shared/components/dataTable/DataTable.tsx +++ b/src/shared/components/dataTable/DataTable.tsx @@ -34,7 +34,6 @@ import { } from '@/components/ui/table'; import DataTableInterface from './interfaces/DataTableInterface'; -import DataTableSubview from './interfaces/DataTableSubview'; /** * DataTable genérico com suporte a subvisões dinâmicas (subtabelas ou detalhes). @@ -180,42 +179,22 @@ export function DataTable Rea {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => { - const subview = - typeof row.original.subview === 'function' - ? row.original.subview() - : row.original.subview; - return ( - - {/* Linha principal */} - onRowClick?.(row.original)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - - {/* Subview dinâmica (qualquer conteúdo) */} - {subview && ( - - - - - - )} - + onRowClick?.(row.original)} + > + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + ); }) ) : ( diff --git a/src/shared/components/dataTable/interfaces/DataTableSubview.tsx b/src/shared/components/dataTable/interfaces/DataTableSubview.tsx deleted file mode 100644 index ae2efb4..0000000 --- a/src/shared/components/dataTable/interfaces/DataTableSubview.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; - -export default function DataTableSubview({ subview }) { - if (!subview) return null; - if (!subview.component) return null; - - const Component = subview.component; - const props = subview.props || {}; - - return ; -} From 2d37d4d4212ca93f10350adaaaffe0ae6387d4da Mon Sep 17 00:00:00 2001 From: Keven Date: Tue, 2 Dec 2025 18:23:41 -0300 Subject: [PATCH 12/14] [MVPTN-37] feat(Pesquisa): Adiciona query params nas urls --- .../_data/GEmolumento/GEmolumentoIndexData.ts | 6 ++--- .../_interfaces/GEmolumentoReadInterface.ts | 1 + .../g_emolumento/GEmolumentoIndexService.ts | 1 + .../GEmolumento/GEmolumentoServicoSelect.tsx | 3 ++- .../components/GUsuario/GUsuarioSelect.tsx | 13 +++++++++- .../TPessoa/TPessoaTableFormSubview.tsx | 4 +-- .../TServicoTipo/TServicoTipoSelect.tsx | 11 +++++++- .../data/GUsuario/GUsuarioIndexData.ts | 8 ++++-- .../TServicoTipo/TServicoTipoIndexData.ts | 10 +++++--- .../hooks/GUsuario/useGUsuarioIndexHook.ts | 9 ++++--- .../TServicoTipo/useTServicoTipoReadHook.ts | 11 +++++--- .../GUsuario/GUsuarioIndexInterface.ts | 3 +++ .../TServicoTipo/TServicoTipoIndexInteface.ts | 3 +++ .../services/GUsuario/GUsuarioIndex.ts | 5 ++-- .../TServicoTipo/TServicoTipoIndexService.ts | 5 ++-- .../TServicoPedidoDetailsPagamento.tsx | 22 ---------------- src/shared/components/dataTable/DataTable.tsx | 6 ++--- .../data/fingertech/FingerTechCaptureData.ts | 8 ++++++ .../data/fingertech/FingerTechEnrollData.ts | 25 +++++++++++++++++++ .../data/fingertech/FingerTechMatchData.ts | 23 +++++++++++++++++ ...dexHook.ts => useFingerTechCaptureHook.ts} | 2 +- .../FingerTech/useFingerTechEnrollHook.ts | 25 +++++++++++++++++++ .../FingerTech/useFingerTechMatchHook.ts | 25 +++++++++++++++++++ .../FingerTech/FingerTechEnrollService.ts | 9 +++++++ .../FingerTech/FingerTechMatchService.ts | 9 +++++++ 25 files changed, 195 insertions(+), 52 deletions(-) create mode 100644 src/packages/administrativo/interfaces/GUsuario/GUsuarioIndexInterface.ts create mode 100644 src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoIndexInteface.ts create mode 100644 src/shared/data/fingertech/FingerTechEnrollData.ts create mode 100644 src/shared/data/fingertech/FingerTechMatchData.ts rename src/shared/hooks/FingerTech/{useFingerTechIndexHook.ts => useFingerTechCaptureHook.ts} (90%) create mode 100644 src/shared/hooks/FingerTech/useFingerTechEnrollHook.ts create mode 100644 src/shared/hooks/FingerTech/useFingerTechMatchHook.ts create mode 100644 src/shared/services/FingerTech/FingerTechEnrollService.ts create mode 100644 src/shared/services/FingerTech/FingerTechMatchService.ts diff --git a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts index e8e80b8..ceb79ab 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_data/GEmolumento/GEmolumentoIndexData.ts @@ -11,16 +11,14 @@ import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInter // Função assíncrona responsável por executar a requisição para listar os tipos de marcação async function executeGEmolumentoIndexData(data: GEmolumentoReadInterface) { + // Cria uma nova instância da classe API para enviar a requisição const api = new API(); - // Concatena o endpoint com a query string (caso existam parâmetros) - const endpoint = `administrativo/g_emolumento/sistema/${data.sistema_id}`; - // Envia uma requisição GET para o endpoint 'administrativo/g_marcacao_tipo/' return await api.send({ method: Methods.GET, - endpoint: endpoint, + endpoint: `administrativo/g_emolumento/sistema/${data.sistema_id}?${new URLSearchParams(data.urlParams).toString()}`, }); } diff --git a/src/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface.ts b/src/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface.ts index 111562f..b853138 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_interfaces/GEmolumentoReadInterface.ts @@ -1,3 +1,4 @@ export interface GEmolumentoReadInterface { sistema_id?: number; + urlParams?: object } diff --git a/src/app/(protected)/(cadastros)/cadastros/_services/g_emolumento/GEmolumentoIndexService.ts b/src/app/(protected)/(cadastros)/cadastros/_services/g_emolumento/GEmolumentoIndexService.ts index 4d46361..8d90167 100644 --- a/src/app/(protected)/(cadastros)/cadastros/_services/g_emolumento/GEmolumentoIndexService.ts +++ b/src/app/(protected)/(cadastros)/cadastros/_services/g_emolumento/GEmolumentoIndexService.ts @@ -7,6 +7,7 @@ import { GEmolumentoReadInterface } from '../../_interfaces/GEmolumentoReadInter // Função assíncrona responsável por executar o serviço de listagem de tipos de marcação async function executeGEmolumentoIndexService(data: GEmolumentoReadInterface) { + // Chama a função que realiza a requisição à API e aguarda a resposta const response = await GEmolumentoIndexData(data); diff --git a/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx index 487c82f..bf7a479 100644 --- a/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx +++ b/src/packages/administrativo/components/GEmolumento/GEmolumentoServicoSelect.tsx @@ -37,7 +37,8 @@ export default function GEmolumentoServicoSelect({ const [open, setOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); - const gEmolumentoReadParams: GEmolumentoReadInterface = { sistema_id }; + // Define parâmetros de leitura para o hook que busca os emolumentos + const gEmolumentoReadParams: GEmolumentoReadInterface = { sistema_id, urlParams: { situacao: 'A' } }; const { gEmolumento = [], fetchGEmolumento } = useGEmolumentoReadHook(); /** diff --git a/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx b/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx index 856e772..ee5d466 100644 --- a/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx +++ b/src/packages/administrativo/components/GUsuario/GUsuarioSelect.tsx @@ -19,6 +19,7 @@ import { useGUsuarioIndexHook } from '@/packages/administrativo/hooks/GUsuario/u import GUsuarioSelectInterface from '@/packages/administrativo/interfaces/GUsuario/GUsuarioSelectInterface'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; +import GUsuarioIndexInterface from '../../interfaces/GUsuario/GusuarioIndexInterface'; export default function GUsuarioSelect({ field }: GUsuarioSelectInterface) { const [open, setOpen] = useState(false); @@ -30,9 +31,19 @@ export default function GUsuarioSelect({ field }: GUsuarioSelectInterface) { * useCallback evita recriação desnecessária da função. */ const loadData = useCallback(async () => { + + const urlParams = { + assina: 'S', + situacao: 'A' + } + + const GUsuarioIndex: GUsuarioIndexInterface = { + urlParams: urlParams + } + if (usuarios?.length) return; setIsLoading(true); - await fetchUsuarios(); + await fetchUsuarios(GUsuarioIndex); setIsLoading(false); }, [usuarios?.length, fetchUsuarios]); diff --git a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx index 95c06f1..367b213 100644 --- a/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx +++ b/src/packages/administrativo/components/TPessoa/TPessoaTableFormSubview.tsx @@ -19,7 +19,7 @@ import { import TPessoaCartaoForm from '@/packages/servicos/components/TPessoaCartao/TPessoaCartaoForm'; import GetNameInitials from '@/shared/actions/text/GetNameInitials'; import WebCamDialog from '@/shared/components/webcam/WebCamDialog'; -import { useFingerTechIndexHook } from '@/shared/hooks/FingerTech/useFingerTechIndexHook'; +import { useFingerTechCaptureHook } from '@/shared/hooks/FingerTech/useFingerTechCaptureHook'; import TPessoaTableFormSubviewInterface from '../../interfaces/TPessoa/TPessoaTableFormSubviewInterface'; @@ -31,7 +31,7 @@ function TPessoaTableFormSubview({ }: TPessoaTableFormSubviewInterface) { const [isWebCamOpenDialog, setIsWebCamOpenDialog] = useState(false) - const { base64, captureFingerTech } = useFingerTechIndexHook(); + const { base64, captureFingerTech } = useFingerTechCaptureHook(); // Chama o leitor biométrico const handleBiometria = useCallback(() => { diff --git a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx index 1d9aeb3..232b308 100644 --- a/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx +++ b/src/packages/administrativo/components/TServicoTipo/TServicoTipoSelect.tsx @@ -19,6 +19,8 @@ import { useTServicoTipoReadHook } from '@/packages/administrativo/hooks/TServic import TServicoTipoSelectInterface from '@/packages/administrativo/interfaces/TServicoTipo/TServicoTipoSelectInterface'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; +import TServicoTipoIndexInteface from '../../interfaces/TServicoTipo/TServicoTipoIndexInteface'; + export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterface) { const [open, setOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -28,9 +30,16 @@ export default function TServicoTipoSelect({ field }: TServicoTipoSelectInterfac * Efeito para buscar os dados apenas uma vez. */ const loadData = useCallback(async () => { + + const TServicoTipoIndex: TServicoTipoIndexInteface = { + urlParams: { + situacao: 'A' + } + } + if (tServicoTipo.length) return; setIsLoading(true); - await fetchTServicoTipo(); + await fetchTServicoTipo(TServicoTipoIndex); setIsLoading(false); }, [tServicoTipo.length, fetchTServicoTipo]); diff --git a/src/packages/administrativo/data/GUsuario/GUsuarioIndexData.ts b/src/packages/administrativo/data/GUsuario/GUsuarioIndexData.ts index 1d0ceba..d5d1b99 100644 --- a/src/packages/administrativo/data/GUsuario/GUsuarioIndexData.ts +++ b/src/packages/administrativo/data/GUsuario/GUsuarioIndexData.ts @@ -3,13 +3,17 @@ import API from '@/shared/services/api/Api'; import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; -export default async function GUsuarioIndexData() { +import GUsuarioIndexInterface from '../../interfaces/GUsuario/GusuarioIndexInterface'; + +export default async function GUsuarioIndexData(data: GUsuarioIndexInterface) { const api = new API(); const response = await api.send({ method: Methods.GET, - endpoint: `administrativo/g_usuario/`, + endpoint: `administrativo/g_usuario?${new URLSearchParams(data.urlParams).toString()}`, }); + console.log(response) + return response; } diff --git a/src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts b/src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts index 7003e6e..3aadff7 100644 --- a/src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts +++ b/src/packages/administrativo/data/TServicoTipo/TServicoTipoIndexData.ts @@ -1,21 +1,23 @@ // Importa o serviço de API que será utilizado para realizar requisições HTTP -import API from '@/shared/services/api/Api'; // +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; // Importa o enum que contém os métodos HTTP disponíveis (GET, POST, PUT, DELETE) import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; // +import TServicoTipoIndexInteface from '../../interfaces/TServicoTipo/TServicoTipoIndexInteface'; + // Importa função que encapsula chamadas assíncronas e trata erros automaticamente -import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; // // Função assíncrona que implementa a lógica de buscar todos os tipos de serviço (GET) -async function executeTServicoTipoIndexData() { +async function executeTServicoTipoIndexData(data: TServicoTipoIndexInteface) { // Instancia o cliente da API para enviar a requisição const api = new API(); // // Executa a requisição para a API com o método apropriado e o endpoint da tabela t_servico_tipo return await api.send({ method: Methods.GET, // GET listar todos os itens - endpoint: `administrativo/t_servico_tipo/`, // Endpoint atualizado + endpoint: `administrativo/t_servico_tipo/?${new URLSearchParams(data.urlParams).toString()}`, }); } diff --git a/src/packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook.ts b/src/packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook.ts index f081677..1cb7b36 100644 --- a/src/packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook.ts +++ b/src/packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook.ts @@ -2,18 +2,21 @@ import { useState } from 'react'; +import Usuario from '@/packages/administrativo/interfaces/GUsuario/GUsuarioInterface'; import { useResponse } from '@/shared/components/response/ResponseContext'; -import Usuario from '../../interfaces/GUsuario/GUsuarioInterface'; +import GUsuarioIndexInterface from '../../interfaces/GUsuario/GusuarioIndexInterface'; import GUsuarioIndex from '../../services/GUsuario/GUsuarioIndex'; + export const useGUsuarioIndexHook = () => { const { setResponse } = useResponse(); const [usuarios, setUsuarios] = useState(null); - const fetchUsuarios = async () => { - const response = await GUsuarioIndex(); + const fetchUsuarios = async (data: GUsuarioIndexInterface) => { + + const response = await GUsuarioIndex(data); setUsuarios(response.data); diff --git a/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts index 8c3b1f5..a50af75 100644 --- a/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts +++ b/src/packages/administrativo/hooks/TServicoTipo/useTServicoTipoReadHook.ts @@ -1,14 +1,17 @@ -import { useResponse } from '@/shared/components/response/ResponseContext'; // Contexto global para gerenciar respostas da API import { useState } from 'react'; +import { useResponse } from '@/shared/components/response/ResponseContext'; // Contexto global para gerenciar respostas da API + // Serviço que busca a lista de tipos de serviço (TServicoTipoIndexService) +import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; +import TServicoTipoIndexInteface from '../../interfaces/TServicoTipo/TServicoTipoIndexInteface'; import { TServicoTipoIndexService } from '../../services/TServicoTipo/TServicoTipoIndexService'; // Interface tipada do tipo de serviço -import TServicoTipoInterface from '../../../../app/(protected)/(cadastros)/cadastros/_interfaces/TServicoTipoInterface'; // Hook customizado para leitura de dados de tipos de serviço export const useTServicoTipoReadHook = () => { + // Hook do contexto de resposta para feedback global (alertas, mensagens etc.) const { setResponse } = useResponse(); @@ -16,9 +19,9 @@ export const useTServicoTipoReadHook = () => { const [tServicoTipo, setTServicoTipo] = useState([]); // Função assíncrona que busca os dados dos tipos de serviço - const fetchTServicoTipo = async () => { + const fetchTServicoTipo = async (data: TServicoTipoIndexInteface) => { // Chama o serviço responsável por consultar a API - const response = await TServicoTipoIndexService(); + const response = await TServicoTipoIndexService(data); // Atualiza o estado local com os dados retornados setTServicoTipo(response.data); diff --git a/src/packages/administrativo/interfaces/GUsuario/GUsuarioIndexInterface.ts b/src/packages/administrativo/interfaces/GUsuario/GUsuarioIndexInterface.ts new file mode 100644 index 0000000..dabc97d --- /dev/null +++ b/src/packages/administrativo/interfaces/GUsuario/GUsuarioIndexInterface.ts @@ -0,0 +1,3 @@ +export default interface GUsuarioIndexInterface { + urlParams: object +} \ No newline at end of file diff --git a/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoIndexInteface.ts b/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoIndexInteface.ts new file mode 100644 index 0000000..ab18dc8 --- /dev/null +++ b/src/packages/administrativo/interfaces/TServicoTipo/TServicoTipoIndexInteface.ts @@ -0,0 +1,3 @@ +export default interface TServicoTipoIndexInteface { + urlParams: object +} \ No newline at end of file diff --git a/src/packages/administrativo/services/GUsuario/GUsuarioIndex.ts b/src/packages/administrativo/services/GUsuario/GUsuarioIndex.ts index ac2d962..bde697b 100644 --- a/src/packages/administrativo/services/GUsuario/GUsuarioIndex.ts +++ b/src/packages/administrativo/services/GUsuario/GUsuarioIndex.ts @@ -1,9 +1,10 @@ 'use server'; import GUsuarioIndexData from '../../data/GUsuario/GUsuarioIndexData'; +import GUsuarioIndexInterface from '../../interfaces/GUsuario/GusuarioIndexInterface'; -export default async function GUsuarioIndex() { - const response = await GUsuarioIndexData(); +export default async function GUsuarioIndex(data: GUsuarioIndexInterface) { + const response = await GUsuarioIndexData(data); return response; } diff --git a/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts b/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts index cb4860c..ed7dcc8 100644 --- a/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts +++ b/src/packages/administrativo/services/TServicoTipo/TServicoTipoIndexService.ts @@ -2,12 +2,13 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/ // Função que envolve qualquer ação assíncrona para capturar e tratar erros do cliente import { TServicoTipoIndexData } from '../../data/TServicoTipo/TServicoTipoIndexData'; +import TServicoTipoIndexInteface from '../../interfaces/TServicoTipo/TServicoTipoIndexInteface'; // Função que retorna os dados da lista de tipos de serviço (chamada à API ou mock) // Função assíncrona que executa a chamada para buscar os dados dos tipos de serviço -async function executeTServicoTipoIndexService() { +async function executeTServicoTipoIndexService(data: TServicoTipoIndexInteface) { // Chama a função que retorna os dados dos tipos de serviço - const response = await TServicoTipoIndexData(); + const response = await TServicoTipoIndexData(data); // Retorna a resposta para o chamador return response; diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx index 077e883..de7f70a 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetailsPagamento.tsx @@ -78,28 +78,6 @@ export default function TServicoPedidoDetailsPagamento({ {FormatMoney(total)}
- - -
- Emolumento - {FormatMoney(emolumento)} -
- -
- Tx. Judiciária - {FormatMoney(taxa_judiciaria)} -
- -
- ISS - {FormatMoney(valor_iss)} -
- -
- Fundesp - {FormatMoney(fundesp)} -
-
diff --git a/src/shared/components/dataTable/DataTable.tsx b/src/shared/components/dataTable/DataTable.tsx index 68aaa5d..6b82471 100644 --- a/src/shared/components/dataTable/DataTable.tsx +++ b/src/shared/components/dataTable/DataTable.tsx @@ -181,9 +181,8 @@ export function DataTable Rea table.getRowModel().rows.map((row) => { return ( onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( @@ -208,6 +207,7 @@ export function DataTable Rea )} +
diff --git a/src/shared/data/fingertech/FingerTechCaptureData.ts b/src/shared/data/fingertech/FingerTechCaptureData.ts index bea8dfd..cc50617 100644 --- a/src/shared/data/fingertech/FingerTechCaptureData.ts +++ b/src/shared/data/fingertech/FingerTechCaptureData.ts @@ -1,5 +1,13 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +/********************************************* +* Nome: Capture +* Descrição: Chama o método "Capture" da aplicação desktop, +* responsável por chamar a tela de captura de digital para apenas um único dedo. +* Este método é recomendável quando você deseja capturar a impressão digital de um único dedo e +* não existe a necessidade de identificar qual dedo da mão esta digital pertence. +* Retorno: Template (String) ou Null +*********************************************/ async function executeFingerTechCaptureData() { const response = await fetch(`http://localhost:9000/api/public/v1/captura/Capturar/1`, diff --git a/src/shared/data/fingertech/FingerTechEnrollData.ts b/src/shared/data/fingertech/FingerTechEnrollData.ts new file mode 100644 index 0000000..c54361d --- /dev/null +++ b/src/shared/data/fingertech/FingerTechEnrollData.ts @@ -0,0 +1,25 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +/********************************************* +* Nome: Enroll +* Descrição: Chama o método "Enroll" da aplicação desktop, +* responsável por chamar a tela de captura de impressão digital para mais de um dedo. +* Este método é recomendável quando você deseja capturar a impressão digital de mais de um dedo e +* quando é necessário identificar a qual dedo esta digital pertence. +* Quando houver a captura de mais de uma impressão digital, elas serão armazenadas de maneira +* codificada no mesmo "Template" (String), mas durante a comparação qualquer dedo poderá ser +* comparado. +* Retorno: Template (String) ou "" (Vazio) +*********************************************/ +async function executeFingerTechEnrollData() { + + const response = await fetch(`http://localhost:9000/api/public/v1/captura/Enroll/1`, + { + method: 'GET' + }, + ); + + return await response.text() +} + +export const FingerTechEnrollData = withClientErrorHandler(executeFingerTechEnrollData); diff --git a/src/shared/data/fingertech/FingerTechMatchData.ts b/src/shared/data/fingertech/FingerTechMatchData.ts new file mode 100644 index 0000000..0450850 --- /dev/null +++ b/src/shared/data/fingertech/FingerTechMatchData.ts @@ -0,0 +1,23 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +/********************************************* +* Nome: Match +* Descrição: Chama o método "VerifyMatch" da aplicação desktop, +* responsável por chamar a tela de captura de digital para apenas um único dedo e realizar a +* comparação com um outro template (impressão digital) já cadastrada. +* Este método é recomendável quando você deseja você comparação de 1:1 (Um para Um). +* Retorno: Template (String) ou Null +*********************************************/ +async function executeFingerTechMatchData() { + + // 'http://localhost:9000/api/public/v1/captura/Comparar?Digital=' + digital, + const response = await fetch(`http://localhost:9000/api/public/v1/captura/Capturar/1`, + { + method: 'GET' + }, + ); + + return await response.text() +} + +export const FingerTechMatchData = withClientErrorHandler(executeFingerTechMatchData); diff --git a/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts b/src/shared/hooks/FingerTech/useFingerTechCaptureHook.ts similarity index 90% rename from src/shared/hooks/FingerTech/useFingerTechIndexHook.ts rename to src/shared/hooks/FingerTech/useFingerTechCaptureHook.ts index 2674bca..956f504 100644 --- a/src/shared/hooks/FingerTech/useFingerTechIndexHook.ts +++ b/src/shared/hooks/FingerTech/useFingerTechCaptureHook.ts @@ -4,7 +4,7 @@ import { useState } from 'react'; import { FingerTechCaptureService } from '@/shared/services/FingerTech/FingerTechCaptureService'; -export const useFingerTechIndexHook = () => { +export const useFingerTechCaptureHook = () => { const [base64, setBase64] = useState(''); diff --git a/src/shared/hooks/FingerTech/useFingerTechEnrollHook.ts b/src/shared/hooks/FingerTech/useFingerTechEnrollHook.ts new file mode 100644 index 0000000..74afb2e --- /dev/null +++ b/src/shared/hooks/FingerTech/useFingerTechEnrollHook.ts @@ -0,0 +1,25 @@ +'use client'; + +import { useState } from 'react'; + +import { FingerTechEnrollService } from '@/shared/services/FingerTech/FingerTechEnrollService'; + +export const useFingerTechEnrollHook = () => { + + const [base64, setBase64] = useState(''); + + const enrollFingerTech = async () => { + + const response = await FingerTechEnrollService(); + + setBase64(response); + + return response + + }; + + return { + base64, + enrollFingerTech, + }; +}; diff --git a/src/shared/hooks/FingerTech/useFingerTechMatchHook.ts b/src/shared/hooks/FingerTech/useFingerTechMatchHook.ts new file mode 100644 index 0000000..85734e1 --- /dev/null +++ b/src/shared/hooks/FingerTech/useFingerTechMatchHook.ts @@ -0,0 +1,25 @@ +'use client'; + +import { useState } from 'react'; + +import { FingerTechMatchService } from '@/shared/services/FingerTech/FingerTechMatchService'; + +export const useFingerTechMatchHook = () => { + + const [base64, setBase64] = useState(''); + + const matchFingerTech = async () => { + + const response = await FingerTechMatchService(); + + setBase64(response); + + return response + + }; + + return { + base64, + matchFingerTech, + }; +}; diff --git a/src/shared/services/FingerTech/FingerTechEnrollService.ts b/src/shared/services/FingerTech/FingerTechEnrollService.ts new file mode 100644 index 0000000..7316166 --- /dev/null +++ b/src/shared/services/FingerTech/FingerTechEnrollService.ts @@ -0,0 +1,9 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import { FingerTechEnrollData } from '@/shared/data/fingertech/FingerTechEnrollData'; + +export default async function executeFingerTechCEnrollService() { + const response = await FingerTechEnrollData(); + return response; +} + +export const FingerTechEnrollService = withClientErrorHandler(executeFingerTechCEnrollService); diff --git a/src/shared/services/FingerTech/FingerTechMatchService.ts b/src/shared/services/FingerTech/FingerTechMatchService.ts new file mode 100644 index 0000000..b52ddc7 --- /dev/null +++ b/src/shared/services/FingerTech/FingerTechMatchService.ts @@ -0,0 +1,9 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import { FingerTechMatchData } from '@/shared/data/fingertech/FingerTechMatchData'; + +export default async function executeFingerTechMatchService() { + const response = await FingerTechMatchData(); + return response; +} + +export const FingerTechMatchService = withClientErrorHandler(executeFingerTechMatchService); From 6b6f2e4fc87ac51e219c34d7c446a261f861c01c Mon Sep 17 00:00:00 2001 From: Keven Date: Fri, 5 Dec 2025 11:14:03 -0300 Subject: [PATCH 13/14] [MVPTN-37] feat(Pedido): Cria endpoints e recursos para estornar e ativar um item do pedido --- .../TServicoItemPedidoList.tsx | 243 +++++++++++++----- .../TServicoPedido/TServicoPedidoDetails.tsx | 83 +++++- .../TServicoItemPedidoAtivarData.ts | 22 ++ .../TServicoItemPedidoCancelarData.ts | 22 ++ .../useTServicoItemPedidoAtivarHook.ts | 33 +++ .../useTServicoItemPedidoCancelarHook.ts | 32 +++ .../TServicoItemPedidoListInterface.ts | 1 + .../TServicoItemPedidoAtivarService.ts | 14 + .../TServicoItemPedidoCancelarService.ts | 14 + 9 files changed, 379 insertions(+), 85 deletions(-) create mode 100644 src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoAtivarData.ts create mode 100644 src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoCancelarData.ts create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook.ts create mode 100644 src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook.ts create mode 100644 src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoAtivarService.ts create mode 100644 src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoCancelarService.ts diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx index 4a32ea2..5f82cc7 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx @@ -1,107 +1,208 @@ 'use client'; -import { BookmarkX, IdCardIcon, MoreHorizontalIcon, TicketIcon } from 'lucide-react'; +import { BookmarkX, IdCardIcon, MoreHorizontalIcon, RotateCcwIcon, TicketIcon } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; import { ButtonGroup } from '@/components/ui/button-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; +import { useTServicoItemPedidoAtivarHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook'; +import { useTServicoItemPedidoCancelarHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import TServicoItemPedidoListInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface'; import FormatMoney from '@/shared/actions/money/FormatMoney'; +import { useResponse } from '@/shared/components/response/ResponseContext'; import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; -export default function TServicoItemPedidoList({ items }: TServicoItemPedidoListInterface) { + + +export default function TServicoItemPedidoList({ items, openConfirmDialog }: TServicoItemPedidoListInterface) { + + const { setResponse } = useResponse(); + const { cancelarTServicoItemPedido } = useTServicoItemPedidoCancelarHook() + const { ativarTServicoItemPedido } = useTServicoItemPedidoAtivarHook() + + const [localItems, setLocalItems] = useState(items || []) + + useEffect(() => { + + setLocalItems(items || []) + + }, [items]) + + const handleSituacaoTServicoItemPedido = useCallback(async (item: any) => { + + const servicoItemPedido: TServicoItemPedidoInterface = { + + servico_itempedido_id: item.servico_itempedido_id + + } + + let response: any = null + + switch (item.situacao) { + + case 'C': + + response = await ativarTServicoItemPedido(servicoItemPedido) + break + + case 'F': + + response = await cancelarTServicoItemPedido(servicoItemPedido) + break + + default: + + setResponse({ + status: 422, + error: 'Situação', + detail: 'Situação não tratada' + }) + break + + } + + if (response) { + + setLocalItems((prev) => + + prev.map((i) => + + i.servico_itempedido_id === item.servico_itempedido_id ? { ...i, situacao: response.situacao } : i + + ) + ) + } + + }, [cancelarTServicoItemPedido, setLocalItems]) + return ( - Itens: {items?.length} + Itens: {localItems?.length} + {/* Altura máxima + scroll vertical */}
- {items?.map((item) => ( -
- {/* Descrição */} -
-

- {item.descricao} de {item.nome} - -

-
- # {item.servico_itempedido_id} -
-
- {/* Valores (grid compacto) */} -
+ {localItems?.map((item) => { + + const isCancelado = item.situacao === 'C' + const actionLabel = isCancelado ? 'Ativar Item' : 'Estornar Item' + const confirmTitle = isCancelado ? 'Ativação de Item' : 'Estorno de Item' + const confirmMessage = isCancelado ? `Deseja realmente ativar o item #${item.servico_itempedido_id}?` : `Deseja realmente estornar o item #${item.servico_itempedido_id}?` + const confirmButton = isCancelado ? 'Sim, ativar item' : 'Sim, estornar item' + const actionIcon = isCancelado ? : + + return ( +
+ + {/* Descrição */} +
+ +

+ + {item.descricao} de {item.nome} - + +

+ +
+ + # {item.servico_itempedido_id} + +
-
-
Emolumento
-
{FormatMoney(item.emolumento)}
-
-
Tx. Judiciária
-
{FormatMoney(item.taxa_judiciaria)}
-
+ {/* Valores (grid compacto) */} +
-
-
ISS
-
{FormatMoney(item.valor_iss)}
-
+
+
Emolumento
+
{FormatMoney(item.emolumento)}
+
-
-
Fundesp
-
{FormatMoney(item.fundesp)}
-
+
+
Tx. Judiciária
+
{FormatMoney(item.taxa_judiciaria)}
+
-
-
Total
-
{FormatMoney(item.valor)}
-
+
+
ISS
+
{FormatMoney(item.valor_iss)}
+
-
- +
+
Fundesp
+
{FormatMoney(item.fundesp)}
+
+ +
+
Total
+
{FormatMoney(item.valor)}
+
+ +
- - - - - - - - - - Imprimir Etiqueta - - - - - - Imprimir Cartão - - - - - - Estornar Item - - - - + + + + + + + + + + + Imprimir Etiqueta + + + + + + Imprimir Cartão + + + + + + openConfirmDialog({ + title: confirmTitle, + description: 'Confirmação necessária', + message: confirmMessage, + confirmText: confirmButton, + cancelText: 'Cancelar', + onConfirm: () => { + handleSituacaoTServicoItemPedido(item) + }, + }) + } + > + {actionIcon} {actionLabel} + + + + + - +
-
-
- ))} +
+ ) + })}
diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx index ac68696..f401f60 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx @@ -24,6 +24,46 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed const { TServicoPedido, showTServicoPedido } = useTServicoPedidoShowHook() const [isCancelServicoPedidoDialogOpen, setIsCancelServicoPedidoDialogOpen] = useState(false) + // 🔹 Estado genérico e dinâmico do ConfirmDialog + const [confirmDialog, setConfirmDialog] = useState({ + isOpen: false, + title: '', + description: '', + message: '', + confirmText: 'Confirmar', + cancelText: 'Cancelar', + onConfirm: () => { }, + onCancel: () => { }, + }) + + // 🔹 Função utilitária para abrir o dialog dinamicamente + const openConfirmDialog = ({ + title, + description, + message, + confirmText = 'Confirmar', + cancelText = 'Cancelar', + onConfirm, + onCancel, + }) => { + setConfirmDialog({ + isOpen: true, + title, + description, + message, + confirmText, + cancelText, + onConfirm: () => { + onConfirm?.() + setConfirmDialog((prev) => ({ ...prev, isOpen: false })) + }, + onCancel: () => { + onCancel?.() + setConfirmDialog((prev) => ({ ...prev, isOpen: false })) + }, + }) + } + const TServicoPedidoShowData = useCallback(async () => { const servicoPedido: TServicoPedidoInterface = { servico_pedido_id: servico_pedido_id @@ -61,6 +101,7 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed
-
{/* Confirma o cancelamento do pedido */} - {isCancelServicoPedidoDialogOpen && ( - { handleCancelServicoPedidoOpenDialog(false) }} - onCancel={() => { handleCancelServicoPedidoOpenDialog(false) }} - /> - )} -
+ +
) } \ No newline at end of file diff --git a/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoAtivarData.ts b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoAtivarData.ts new file mode 100644 index 0000000..b451321 --- /dev/null +++ b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoAtivarData.ts @@ -0,0 +1,22 @@ +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; +import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; +import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; + + +async function executeTServicoItemPedidoAtivarData(data: TServicoItemPedidoInterface): Promise { + + const api = new API(); + + return api.send({ + + method: Methods.PUT, + endpoint: `servicos/balcao/t_servico_itempedido/${data.servico_itempedido_id}/ativar`, + body: data, + + }); + +} + +export const TServicoItemPedidoAtivarData = withClientErrorHandler(executeTServicoItemPedidoAtivarData); diff --git a/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoCancelarData.ts b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoCancelarData.ts new file mode 100644 index 0000000..51cdda2 --- /dev/null +++ b/src/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoCancelarData.ts @@ -0,0 +1,22 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; +import API from '@/shared/services/api/Api'; +import { Methods } from '@/shared/services/api/enums/ApiMethodEnum'; +import ApiResponseInterface from '@/shared/services/api/interfaces/ApiResponseInterface'; + +import TServicoItemPedidoInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; + +async function executeTServicoItemPedidoCancelarData(data: TServicoItemPedidoInterface): Promise { + + const api = new API(); + + return api.send({ + + method: Methods.PUT, + endpoint: `servicos/balcao/t_servico_itempedido/${data.servico_itempedido_id}/cancelar`, + body: data, + + }); + +} + +export const TServicoItemPedidoCancelarData = withClientErrorHandler(executeTServicoItemPedidoCancelarData); diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook.ts new file mode 100644 index 0000000..5b1b05a --- /dev/null +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook.ts @@ -0,0 +1,33 @@ +'use client'; + +import { useState } from 'react'; + +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { TServicoItemPedidoAtivarService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoAtivarService'; +import { useResponse } from '@/shared/components/response/ResponseContext'; + + +export const useTServicoItemPedidoAtivarHook = () => { + + const { setResponse } = useResponse(); + + const [TServicoItemPedido, setTServicoItemPedido] = useState(null); + + const [isOpen, setIsOpen] = useState(false); + + const ativarTServicoItemPedido = async (data: TServicoItemPedidoInterface) => { + + const response = await TServicoItemPedidoAtivarService(data); + + setTServicoItemPedido(response.data); + + setResponse(response); + + setIsOpen(false); + + return response.data; + + }; + + return { TServicoItemPedido, ativarTServicoItemPedido, isOpen, setIsOpen }; +}; \ No newline at end of file diff --git a/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook.ts b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook.ts new file mode 100644 index 0000000..e0f3f64 --- /dev/null +++ b/src/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook.ts @@ -0,0 +1,32 @@ +'use client'; + +import { useState } from 'react'; + +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { TServicoItemPedidoCancelarService } from '@/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoCancelarService'; +import { useResponse } from '@/shared/components/response/ResponseContext'; + +export const useTServicoItemPedidoCancelarHook = () => { + + const { setResponse } = useResponse(); + + const [TServicoItemPedido, setTServicoItemPedido] = useState(null); + + const [isOpen, setIsOpen] = useState(false); + + const cancelarTServicoItemPedido = async (data: TServicoItemPedidoInterface) => { + + const response = await TServicoItemPedidoCancelarService(data); + + setTServicoItemPedido(response.data); + + setResponse(response); + + setIsOpen(false); + + return response.data; + + }; + + return { TServicoItemPedido, cancelarTServicoItemPedido, isOpen, setIsOpen }; +}; \ No newline at end of file diff --git a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts index 7fd8f9f..ae1fbab 100644 --- a/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts +++ b/src/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface.ts @@ -2,4 +2,5 @@ import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoCalcul export default interface TServicoItemPedidoListInterface { items: TServicoItemPedidoIndexResponseInterface[]; + openConfirmDialog: any } \ No newline at end of file diff --git a/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoAtivarService.ts b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoAtivarService.ts new file mode 100644 index 0000000..c5f0775 --- /dev/null +++ b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoAtivarService.ts @@ -0,0 +1,14 @@ +import { TServicoItemPedidoAtivarData } from '@/packages/servicos/data/TServicoItemPedido/TServicoItemPedidoAtivarData'; +import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + + +async function executeTServicoItemPedidoAtivarService(data: TServicoItemPedidoInterface) { + + const response = await TServicoItemPedidoAtivarData(data); + + return response; + +} + +export const TServicoItemPedidoAtivarService = withClientErrorHandler(executeTServicoItemPedidoAtivarService); diff --git a/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoCancelarService.ts b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoCancelarService.ts new file mode 100644 index 0000000..687831b --- /dev/null +++ b/src/packages/servicos/services/TServicoItemPedido/TServicoItemPedidoCancelarService.ts @@ -0,0 +1,14 @@ +import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler'; + +import { TServicoItemPedidoCancelarData } from '../../data/TServicoItemPedido/TServicoItemPedidoCancelarData'; +import TServicoItemPedidoInterface from '../../interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; + +async function executeTServicoItemPedidoCancelarService(data: TServicoItemPedidoInterface) { + + const response = await TServicoItemPedidoCancelarData(data); + + return response; + +} + +export const TServicoItemPedidoCancelarService = withClientErrorHandler(executeTServicoItemPedidoCancelarService); From 58630f8602bbb266c486f9381f57fd26742683fd Mon Sep 17 00:00:00 2001 From: Keven Date: Fri, 5 Dec 2025 16:03:26 -0300 Subject: [PATCH 14/14] [MVPTN-37] feat(Pedido): Cria endpoints e recursos para estornar e ativar um item do pedido --- .../(g_usuario)/usuarios/formulario/page.tsx | 7 +- .../(g_usuario)/usuarios/page.tsx | 17 ++-- .../TServicoItemPedidoList.tsx | 3 +- .../TServicoPedido/TServicoPedidoDetails.tsx | 84 +++++++++++++++---- .../TServicoPedidoAtivarData.ts | 19 +++++ .../TServicoPedidoCancelarData.ts | 19 +++++ .../useTServicoPedidoAtivarHook.ts | 37 ++++++++ .../useTServicoPedidoCancelarHook.ts | 37 ++++++++ .../useTServicoPedidoShowHook.ts | 2 +- .../TServicoPedidoAtivarService.ts | 11 +++ .../TServicoPedidoCancelarService.ts | 10 +++ 11 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 src/packages/servicos/data/TServicoPedido/TServicoPedidoAtivarData.ts create mode 100644 src/packages/servicos/data/TServicoPedido/TServicoPedidoCancelarData.ts create mode 100644 src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoAtivarHook.ts create mode 100644 src/packages/servicos/hooks/TServicoPedido/useTServicoPedidoCancelarHook.ts create mode 100644 src/packages/servicos/services/TServicoPedido/TServicoPedidoAtivarService.ts create mode 100644 src/packages/servicos/services/TServicoPedido/TServicoPedidoCancelarService.ts diff --git a/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/formulario/page.tsx b/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/formulario/page.tsx index 5c54cba..2721d7a 100644 --- a/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/formulario/page.tsx +++ b/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/formulario/page.tsx @@ -3,13 +3,10 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; -import { Input } from '@/components/ui/input'; -import { GUsuarioSchema } from '../../../../../../packages/administrativo/schemas/GUsuario/GUsuarioSchema'; + import { Button } from '@/components/ui/button'; - import { Card, CardContent } from '@/components/ui/card'; - import { Form, FormControl, @@ -18,8 +15,10 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; import { useGUsuarioSaveHook } from '../../../../../../packages/administrativo/hooks/GUsuario/useGUsuarioSaveHook'; +import { GUsuarioSchema } from '../../../../../../packages/administrativo/schemas/GUsuario/GUsuarioSchema'; type FormValues = z.infer; diff --git a/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/page.tsx b/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/page.tsx index 3f1d234..07463dd 100644 --- a/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/page.tsx +++ b/src/app/(protected)/(administrativo)/(g_usuario)/usuarios/page.tsx @@ -1,7 +1,10 @@ 'use client'; -import { Card, CardContent } from '@/components/ui/card'; +import Link from 'next/link'; +import { useEffect } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; import { Table, TableBody, @@ -10,14 +13,14 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; - -import Usuario from '../../../../../packages/administrativo/interfaces/GUsuario/GUsuarioInterface'; -import { Button } from '@/components/ui/button'; -import Link from 'next/link'; -import { useGUsuarioIndexHook } from '../../../../../packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook'; -import { useEffect } from 'react'; import Loading from '@/shared/components/loading/loading'; +import { useGUsuarioIndexHook } from '../../../../../packages/administrativo/hooks/GUsuario/useGUsuarioIndexHook'; +import Usuario from '../../../../../packages/administrativo/interfaces/GUsuario/GUsuarioInterface'; + + + + export default function UsuarioPage() { const { usuarios, fetchUsuarios } = useGUsuarioIndexHook(); diff --git a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx index 5f82cc7..ea8ed69 100644 --- a/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx +++ b/src/packages/servicos/components/TServicoItemPedido/TServicoItemPedidoList.tsx @@ -8,7 +8,6 @@ import { Button } from '@/components/ui/button'; import { ButtonGroup } from '@/components/ui/button-group'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; -import { useTServicoItemPedidoAtivarHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook'; import { useTServicoItemPedidoCancelarHook } from '@/packages/servicos/hooks/TServicoItemPedido/useTServicoItemPedidoCancelarHook'; import TServicoItemPedidoInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoIntefarce'; import TServicoItemPedidoListInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoListInterface'; @@ -16,7 +15,7 @@ import FormatMoney from '@/shared/actions/money/FormatMoney'; import { useResponse } from '@/shared/components/response/ResponseContext'; import { ServicosPedidosSituacoesBadge } from '@/shared/components/servicosPedidosSituacoes/ServicosPedidosSituacoesBadge'; - +import { useTServicoItemPedidoAtivarHook } from '../../hooks/TServicoItemPedido/useTServicoItemPedidoAtivarHook'; export default function TServicoItemPedidoList({ items, openConfirmDialog }: TServicoItemPedidoListInterface) { diff --git a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx index f401f60..792308d 100644 --- a/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx +++ b/src/packages/servicos/components/TServicoPedido/TServicoPedidoDetails.tsx @@ -1,7 +1,7 @@ 'use client'; -import { BookmarkX, CalendarIcon, ReceiptText } from 'lucide-react'; +import { BookmarkX, CalendarIcon, ReceiptText, RotateCcwIcon } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; @@ -17,14 +17,63 @@ import { FormatDateTime } from '@/shared/actions/dateTime/FormatDateTime'; import GetCapitalize from '@/shared/actions/text/GetCapitalize'; import GetNameInitials from '@/shared/actions/text/GetNameInitials'; import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog'; +import { useResponse } from '@/shared/components/response/ResponseContext'; + +import { useTServicoPedidoAtivarHook } from '../../hooks/TServicoPedido/useTServicoPedidoAtivarHook'; +import { useTServicoPedidoCancelarHook } from '../../hooks/TServicoPedido/useTServicoPedidoCancelarHook'; export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPedidoInterface) { - const { TServicoItemPedido, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() - const { TServicoPedido, showTServicoPedido } = useTServicoPedidoShowHook() - const [isCancelServicoPedidoDialogOpen, setIsCancelServicoPedidoDialogOpen] = useState(false) + const { setResponse } = useResponse(); + const { ativarTServicoPedido } = useTServicoPedidoAtivarHook() + const { cancelarTServicoPedido } = useTServicoPedidoCancelarHook() + + const { TServicoItemPedido, indexTServicoItemPedido } = useTServicoItemPedidoIndexHook() + const { TServicoPedido, setTServicoPedido, showTServicoPedido } = useTServicoPedidoShowHook() + + const handleSituacaoTServicoPedido = useCallback(async (pedido: any) => { + + const servicoPedido: TServicoPedidoInterface = { + + servico_pedido_id: pedido.servico_pedido_id + + } + + let response: any = null + + switch (pedido.situacao) { + + case 'C': + + response = await ativarTServicoPedido(servicoPedido) + break + + case 'F': + + response = await cancelarTServicoPedido(servicoPedido) + break + + default: + + setResponse({ + status: 422, + error: 'Situação', + detail: 'Situação não tratada' + }) + break + + } + + if (response) { + + pedido.situacao = response.situacao + + setTServicoPedido(pedido) + + } + + }, [cancelarTServicoPedido]) - // 🔹 Estado genérico e dinâmico do ConfirmDialog const [confirmDialog, setConfirmDialog] = useState({ isOpen: false, title: '', @@ -89,6 +138,10 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed TServicoPedidoShowData() }, []) + const isCancelado = TServicoPedido?.situacao === 'C' + const actionLabel = isCancelado ? 'Ativar Pedido' : 'Estornar Pedido' + const actionIcon = isCancelado ? : + return (

@@ -101,7 +154,6 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed