backup: Backup de código

This commit is contained in:
Keven Willian Pereira de Souza 2025-11-04 08:16:28 -03:00
parent 79f2092882
commit e50818e52a
45 changed files with 1518 additions and 443 deletions

33
package-lock.json generated
View file

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

View file

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

View file

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

View file

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

View file

@ -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 (
<TServicoPedidoDetails
servico_pedido_id={Number(params.servicoPedidoId)}
/>
)
}

View file

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

View file

@ -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 (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button
variant="outline"
role="combobox"
aria-expanded={open}
disabled={isLoading}
className="justify-between"
>
{isLoading
? 'Carregando...'
: selected
? GetCapitalize(selected.descricao)
: 'Selecione...'}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Buscar bairro..." disabled={isLoading} />
<CommandList>
<CommandEmpty>
{isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'}
</CommandEmpty>
<CommandGroup>
{tServicoTipo.map((item) => (
<CommandItem
key={item.servico_tipo_id}
value={item.descricao?.toLowerCase() ?? ''}
onSelect={() => handleSelect(Number(item.servico_tipo_id))}
>
<CheckIcon
className={cn(
'mr-2 h-4 w-4',
String(field?.value ?? '') === String(item.servico_tipo_id)
? 'opacity-100'
: 'opacity-0',
)}
/>
{GetCapitalize(item.descricao ?? '')}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

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

View file

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

View file

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

View file

@ -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 = () => {

View file

@ -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 = () => {

View file

@ -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 = () => {

View file

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

View file

@ -0,0 +1,6 @@
export default interface TServicoTipoSelectInterface {
field?: {
value?: number | string;
onChange?: (value: string | number) => void;
};
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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<TServicoItemPedidoInterface>[] {
return [
// servico
{
accessorKey: 'servico',
header: ({ column }) => SortableHeader('Serviço / Tabela', column),
cell: ({ row }) => {
const data = row.original;
return (
<div className="flex items-center gap-3">
<div>
<div className="font-semibold text-gray-900 capitalize">
{GetCapitalize(data.servico)}
</div>
<div className="text-sm text-gray-500">
{GetCapitalize(data.tabela)}
</div>
</div>
</div>
);
},
sortingFn: (a, b) =>
(a.original.servico?.toLowerCase() || '').localeCompare(
b.original.servico?.toLowerCase() || '',
),
},
// emolumento
{
accessorKey: 'emolumento',
header: ({ column }) => SortableHeader('Emolumento', column),
cell: ({ row }) => <div>R$ {row.getValue('emolumento') || '---'}</div>,
},
// taxa_judiciaria
{
accessorKey: 'taxa_judiciaria',
header: ({ column }) => SortableHeader('Tx. Judiciária', column),
cell: ({ row }) => <div>R$ {row.getValue('taxa_judiciaria') || '---'}</div>,
},
// fundesp
{
accessorKey: 'fundesp',
header: ({ column }) => SortableHeader('Fundesp 21%', column),
cell: ({ row }) => <div>R$ {row.getValue('fundesp') || '---'}</div>,
},
// valor_iss
{
accessorKey: 'valor_iss',
header: ({ column }) => SortableHeader('ISS 5%', column),
cell: ({ row }) => <div>R$ {row.getValue('valor_iss') || '---'}</div>,
},
// total
{
accessorKey: 'valor',
header: ({ column }) => SortableHeader('Total', column),
cell: ({ row }) => <div>R$ {row.getValue('valor') || '---'}</div>,
},
// 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 (
<div className="flex items-center">
<Button
type="button"
size="icon"
variant="outline"
onClick={dec}
disabled={value <= min}
aria-label="Diminuir quantidade"
className="bg-white border border-gray-300 dark:bg-gray-700 dark:border-gray-700 ring-primary dark:ring-white hover:border-primary dark:hover:border-white hover:ring-1 hover:text-primary dark:hover:text-white dark:hover:bg-transparent text-gray-600 dark:text-gray-100 h-8 rounded-lg w-8 inline-flex items-center justify-center text-base button-press-feedback"
>
<Minus className="h-4 w-4" />
</Button>
<div className="w-10 text-center">
<span>{value}</span>
</div>
<Button
type="button"
size="icon"
variant="outline"
onClick={inc}
disabled={value >= max}
aria-label="Aumentar quantidade"
className="bg-white border border-gray-300 dark:bg-gray-700 dark:border-gray-700 ring-primary dark:ring-white hover:border-primary dark:hover:border-white hover:ring-1 hover:text-primary dark:hover:text-white dark:hover:bg-transparent text-gray-600 dark:text-gray-100 h-8 rounded-lg w-8 inline-flex items-center justify-center text-base button-press-feedback"
>
<Plus className="h-4 w-4" />
</Button>
</div>
);
},
},
];
}

View file

@ -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 (
<div>
<DataTable
data={data}
columns={columns}
filterColumn="servico"
filterPlaceholder="Buscar por serviço..."
/>
</div>
);
}

View file

@ -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 (
<Card className="card-border">
<CardHeader>
<CardTitle className="text-2xl font-semibold">Itens: {items?.length}</CardTitle>
</CardHeader>
<CardContent>
{/* Altura máxima + scroll vertical */}
<div className="space-y-4 max-h-[60vh] overflow-y-auto pr-1">
{items.map((item) => (
<div
key={item.servico_itempedido_id}
className="bg-cart-item border-cart-border flex items-start gap-4 rounded-lg border p-4"
>
{/* Descrição */}
<div className="min-w-0 flex-1">
<h3 className="text-foreground line-clamp-2 text-sm lg:text-base font-bold">
{item.descricao}
</h3>
<h6 className="text-foreground line-clamp-2 text-sm lg:text-base">
# {item.servico_itempedido_id}
</h6>
</div>
{/* Valores (grid compacto) */}
<div className="grid grid-cols-5 gap-3 min-w-[520px] text-right">
<div>
<div className="text-xs text-muted-foreground">Emolumento</div>
<div className="font-semibold">{money(item.emolumento)}</div>
</div>
<div>
<div className="text-xs text-muted-foreground">Tx. Judiciária</div>
<div className="font-semibold">{money(item.taxa_judiciaria)}</div>
</div>
<div>
<div className="text-xs text-muted-foreground">ISS</div>
<div className="font-semibold">{money(item.valor_iss)}</div>
</div>
<div>
<div className="text-xs text-muted-foreground">Fundesp</div>
<div className="font-semibold">{money(item.fundesp)}</div>
</div>
<div>
<div className="text-xs text-muted-foreground">Total</div>
<div className="font-semibold">{money(item.valor)}</div>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
);
}

View file

@ -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 (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -113,15 +114,19 @@ export default function TServicoPedidoColumns(
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onEdit(natureza, true)}>
<DropdownMenuItem asChild>
<Link href={`/servicos/balcao/detalhes/${row.getValue('servico_pedido_id')}`}>
<EyeIcon className="mr-2 h-4 w-4" />
Detalhes
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => onEdit(servicoPedido, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onSelect={() => onDelete(natureza, true)}
>
<DropdownMenuItem onSelect={() => onDelete(servicoPedido, true)}>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>

View file

@ -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 (
<div className="relative h-full flex flex-col px-2 sm:px-4 py-2 sm:py-4 md:px-6 container mx-auto">
<div className="container mx-auto flex items-center justify-between mb-4">
<h3 className="text-2xl font-semibold">
Pedido: #{TServicoPedido?.servico_pedido_id}
</h3>
<div className="flex items-center gap-2 print:hidden">
<Button>
<Pencil className="mr-2 h-4 w-4" />
Edit
</Button>
</div>
</div>
{/* Main */}
<div className="container mx-auto h-full">
<div className="flex flex-col lg:flex-row gap-4">
{/* Left column */}
<div className="flex flex-col flex-auto gap-4">
<TServicoItemPedidoList
items={TServicoItemPedido}
/>
<TServicoPedidoDetailsPagamento
situacao={TServicoPedido?.situacao}
items={TServicoItemPedido}
/>
</div>
{/* Right column (sidebar) */}
<div className="lg:w-[320px] xl:w-[420px] flex flex-col gap-4">
<Card role="presentation" className="card-border">
<CardHeader>
<CardTitle className="text-2xl font-semibold">
Apresentante
</CardTitle>
</CardHeader>
<CardContent>
{/* Header com avatar e link */}
<div className="flex items-center gap-2">
{/* Foto ou Iniciais */}
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-200">
<span className="text-sm font-medium text-gray-700">
{GetNameInitials(TServicoPedido?.apresentante)}
</span>
</div>
<div className="min-w-0">
<div className="font-bold truncate">
{GetCapitalize(TServicoPedido?.apresentante)}
</div>
<div className="font-light truncate">
{FormatCPF(String(TServicoPedido?.cpfcnpj_apresentante))}
</div>
</div>
</div>
<Separator className="my-5" />
<div className="mb-4 flex items-center gap-2">
<CalendarIcon className="opacity-70" />
<span className="truncate">
{FormatDateTime(TServicoPedido?.data_pedido)}
</span>
</div>
<div className="flex items-center gap-2">
<ClockIcon className="opacity-70" />
<span className="truncate">
14:50:31
</span>
</div>
</CardContent>
</Card>
<Card role="presentation" className="card-border">
<CardHeader>
<CardTitle className="text-2xl font-semibold">
Operador
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
{/* Foto ou Iniciais */}
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-200">
<span className="text-sm font-medium text-gray-700">
{GetNameInitials(TServicoPedido?.login)}
</span>
</div>
<div className="min-w-0">
<div className="font-bold truncate">
{GetCapitalize(TServicoPedido?.login)}
</div>
<div className="font-light truncate">
{GetCapitalize(TServicoPedido?.funcao)}
</div>
</div>
</div>
</CardContent>
</Card>
<Card className="card-border">
<CardHeader>
<CardTitle className="text-2xl font-semibold">
Impressões
</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-2">
<Button className="w-full" variant="outline">Recibo</Button>
<Button className="w-full">Etiqueta</Button>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
)
}

View file

@ -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 (
<Card className="card-border">
<CardHeader>
<CardTitle className="text-2xl font-semibold">
Pagamento <ServicosPedidosSituacoesBadge situacao={situacao} />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">Emolumento</span>
<span className="font-medium">{fmt.format(emolumento)}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">Tx. Judiciária</span>
<span className="font-medium">{fmt.format(taxa_judiciaria)}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">ISS</span>
<span className="font-medium">{fmt.format(valor_iss)}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">Fundesp</span>
<span className="font-medium">{fmt.format(fundesp)}</span>
</div>
<Separator className="border-cart-border" />
<div className="flex items-center justify-between">
<span className="text-muted-foreground font-semibold">Total</span>
<span className="font-semibold">{fmt.format(total)}</span>
</div>
</div>
</CardContent>
</Card>
);
}

View file

@ -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 (
<Dialog
open={isOpen}
onOpenChange={(open) => {
if (!open) onClose(null, false);
}}
>
<DialogContent className="w-full max-w-full p-6 sm:max-w-3xl md:max-w-2xl lg:max-w-2xl">
<DialogHeader>
<DialogTitle className="text-lg sm:text-xl">Formulário de Gramática</DialogTitle>
<DialogDescription className="text-muted-foreground text-sm">
Formulário de Gramática
</DialogDescription>
</DialogHeader>
{/* Formulário principal */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-6">
{/* GRID MOBILE FIRST */}
<div className="grid w-full grid-cols-12 gap-4">
{/* Palavra */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Palavra</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
{/* Rodapé */}
<DialogFooter className="mt-6 flex flex-col justify-end gap-2 sm:flex-row">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancelar
</Button>
</DialogClose>
<LoadingButton
text="Salvar"
textLoading="Salvando..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -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 (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit, onError)}>
<div className="flex gap-4">
{/* Sidebar - Sticky */}
<aside className="hidden w-[360px] lg:block">
<div className="sticky top-4 z-10 max-h-[calc(100vh-2rem)] overflow-auto">
<Card role="presentation" className="card-border">
<CardContent>
<nav className="flex flex-col gap-2">
<StepLink
active={active === 'pedido'}
onClick={() => {
setActive('pedido');
scrollToSection('selectPedido');
}}
icon={<Package className="h-4 w-4" />}
title="Pedido"
description="Dados gerais do pedido."
/>
<StepLink
active={active === 'servicoPedidoItem'}
onClick={() => {
setActive('servicoPedidoItem');
scrollToSection('selectServicoPedidoItem');
}}
icon={<UserSquare2 className="h-4 w-4" />}
title="Itens"
description="Itens/serviços do pedido."
/>
<StepLink
active={active === 'payment'}
onClick={() => {
setActive('payment');
scrollToSection('selectPayment');
}}
icon={<CreditCard className="h-4 w-4" />}
title="Pagamento"
description="Forma e dados de pagamento."
/>
</nav>
</CardContent>
</Card>
</div>
</aside>
{/* Conteúdo */}
<main className="flex-1">
<div className="flex flex-col gap-4">
{/* Seção: Pedido */}
<Card role="presentation" id="selectPedido" className="scroll-mt-6">
<CardHeader>
<CardTitle className="mb-2">Pedido</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Escrevente/Tabelião</FormLabel>
<GUsuarioSelect field={field} />
<FormMessage />
</FormItem>
)}
<div>
<h3 className='text-4xl font-bold mb-4'>
Pedido
</h3>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit, onError)}>
<div className="flex gap-4">
{/* Sidebar - Sticky */}
<aside className="hidden w-[360px] lg:block">
<div className="sticky top-4 z-10 max-h-[calc(100vh-2rem)] overflow-auto">
<Card role="presentation" className="card-border">
<CardContent>
<nav className="flex flex-col gap-2">
<StepLink
active={active === 'pedido'}
onClick={() => {
setActive('pedido');
scrollToSection('selectPedido');
}}
icon={<Package className="h-4 w-4" />}
title="Pedido"
description="Dados gerais do pedido."
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-8">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Apresentante</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
<StepLink
active={active === 'servicoPedidoItem'}
onClick={() => {
setActive('servicoPedidoItem');
scrollToSection('selectServicoPedidoItem');
}}
icon={<UserSquare2 className="h-4 w-4" />}
title="Itens"
description="Itens/serviços do pedido."
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="cpfcnpj_apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>CPF</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
<StepLink
active={active === 'payment'}
onClick={() => {
setActive('payment');
scrollToSection('selectPayment');
}}
icon={<CreditCard className="h-4 w-4" />}
title="Pagamento"
description="Forma e dados de pagamento."
/>
</nav>
</CardContent>
</Card>
</div>
</aside>
{/* Conteúdo */}
<main className="flex-1">
<div className="flex flex-col gap-4">
{/* Seção: Pedido */}
<Card role="presentation" id="selectPedido" className="scroll-mt-6">
<CardHeader>
<CardTitle className="mb-2">
<h4 className='text-3xl'>
Pedido
</h4>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel className='font-semibold'>Escrevente/Tabelião</FormLabel>
<GUsuarioSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-8">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Apresentante</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="cpfcnpj_apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>CPF</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-8">
<FormField
control={form.control}
name="selo_pessoa_nome"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Pessoa presente no selo</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="selo_pessoa_cpfcnpj"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>CPF</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-8">
<FormField
control={form.control}
name="selo_pessoa_nome"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Pessoa presente no selo</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* Seção: Itens */}
<Card role="presentation" id="selectServicoPedidoItem" className="scroll-mt-6">
<CardHeader>
<CardTitle>
<h4 className='text-3xl'>
Itens
</h4>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Serviços</FormLabel>
<TServicoTipoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Emolumentos</FormLabel>
<TServicoTipoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<TServicoItemPedidoFormTable
data={dataItens}
onEdit={() => { }}
onDelete={() => { }}
/>
</div>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="selo_pessoa_cpfcnpj"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>CPF</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* Seção: Pagamento */}
<Card role="presentation" id="selectPayment" className="scroll-mt-6">
<CardHeader>
<CardTitle className="mb-2">
<h4 className='text-3xl'>
Pagamento
</h4>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">
<div className="col-span-12 sm:col-span-12 md:col-span-8">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Requerente</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-12 md:col-span-4">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>CPF/CNPJ Requerente</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Forma Pagamento</FormLabel>
<TipoPagamentoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-12 md:col-span-12 flex justify-end items-center gap-4">
<Button type="button" variant="outline">
Cancelar
</Button>
<Button type="submit">
Confirmar
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Seção: Itens */}
<Card role="presentation" id="selectServicoPedidoItem" className="scroll-mt-6">
<CardHeader>
<CardTitle className="mb-2">Itens</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="apresentante"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Escrevente/Tabelião</FormLabel>
<GUsuarioSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</CardContent>
</Card>
{/* Seção: Pagamento */}
<Card role="presentation" id="selectPayment" className="scroll-mt-6">
<CardHeader>
<CardTitle className="mb-2">Pagamento</CardTitle>
</CardHeader>
<CardContent className="space-y-4">...</CardContent>
</Card>
</div>
</main>
</div>
</form>
</Form>
</CardContent>
</Card>
</div>
</main>
</div>
</form>
</Form>
</div>
);
}

View file

@ -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<ApiResponseInterface> {
const api = new API();
async function executeTServicoItemPedidoIndexData(data: TServicoItemPedidoInterface): Promise<ApiResponseInterface> {
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);

View file

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

View file

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

View file

@ -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<TServicoItemPedidoInterface[]>([]);
const [TServicoItemPedido, setTServicoItemPedido] = useState<TServicoItemPedidoIndexResponseInterface[]>([]);
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 {

View file

@ -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<TServicoItemPedidoInterface>();
const showTServicoItemPedido = async (data: TServicoItemPedidoInterface) => {
const response = await TServicoItemPedidoShowService(data);
setTServicoItemPedido(response.data);
setResponse(response);
return response.data
};
return { TServicoItemPedido, showTServicoItemPedido };
};

View file

@ -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<TServicoPedidoInterface>(null);
const showTServicoPedido = async (data: TServicoPedidoInterface) => {
const response = await TServicoPedidoShowService(data);
setTServicoPedido(response.data);
setResponse(response);
return response.data
};
return { TServicoPedido, showTServicoPedido };
};

View file

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

View file

@ -0,0 +1,5 @@
import TServicoItemPedidoIndexResponseInterface from "./TServicoItemPedidoIndexResponseInterface";
export default interface TServicoItemPedidoListInterface {
items: TServicoItemPedidoIndexResponseInterface[];
}

View file

@ -0,0 +1,3 @@
export default interface TServicoPedidoDetailsInterface {
servico_pedido_id: number
}

View file

@ -0,0 +1,8 @@
import TServicoItemPedidoIndexResponseInterface from "../TServicoItemPedido/TServicoItemPedidoIndexResponseInterface";
export default interface TServicoPedidoDetailsPagamentoInterface {
situacao: string,
items: TServicoItemPedidoIndexResponseInterface[]
}

View file

@ -14,4 +14,6 @@ export default interface TServicoPedidoInterface {
cpfcnpj_apresentante?: string;
selo_pessoa_nome?: string;
selo_pessoa_cpfcnpj?: string;
login?: string;
funcao?: string;
}

View file

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

View file

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

View file

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

View file

@ -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 (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="justify-between"
>
{selectedLabel}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Buscar tipo de parte..." />
<CommandList>
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
<CommandGroup>
{options.map((item) => (
<CommandItem
key={item.value}
value={item.label.toLowerCase()}
onSelect={() => {
field.onChange(item.value); // envia número
setOpen(false);
}}
>
<CheckIcon
className={cn(
'mr-2 h-4 w-4',
Number(field.value) === item.value ? 'opacity-100' : 'opacity-0',
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

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

View file

@ -0,0 +1,18 @@
import { useCallback, useRef } from "react";
export default function useRunOnceByKey() {
const done = useRef(new Set<string>());
const run = useCallback(async (key: string, tasks: Array<() => Promise<unknown>>, 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 };
}