[MVPTN-37] feat(Subview): Ajusta diversos pontos da aplicação para trabalhar com subviews sem sobrecarga
This commit is contained in:
parent
b2e0d50dd6
commit
bc2c2ef3dd
32 changed files with 728 additions and 396 deletions
166
package-lock.json
generated
166
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
BIN
public/sounds/success.mp3
Normal file
BIN
public/sounds/success.mp3
Normal file
Binary file not shown.
83
src/components/ui/button-group.tsx
Normal file
83
src/components/ui/button-group.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
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"
|
||||
|
||||
const buttonGroupVariants = cva(
|
||||
"flex w-fit items-stretch [&>*]: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<typeof buttonGroupVariants>) {
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
data-slot="button-group"
|
||||
data-orientation={orientation}
|
||||
className={cn(buttonGroupVariants({ orientation }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ButtonGroupText({
|
||||
className,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "div"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={cn(
|
||||
"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ButtonGroupSeparator({
|
||||
className,
|
||||
orientation = "vertical",
|
||||
...props
|
||||
}: React.ComponentProps<typeof Separator>) {
|
||||
return (
|
||||
<Separator
|
||||
data-slot="button-group-separator"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
ButtonGroup,
|
||||
ButtonGroupSeparator,
|
||||
ButtonGroupText,
|
||||
buttonGroupVariants,
|
||||
}
|
||||
|
|
@ -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 (
|
||||
|
||||
<div className="p-5" key={index}>
|
||||
<div>
|
||||
<Item variant="outline">
|
||||
<ItemMedia>
|
||||
<Avatar className="size-10">
|
||||
<AvatarImage src={`data:image/png;base64,${base64}`} />
|
||||
<AvatarImage src={``} />
|
||||
<AvatarFallback>
|
||||
{GetNameInitials(selectedTPessoa.nome)}
|
||||
{GetNameInitials(data.pessoa?.nome)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</ItemMedia>
|
||||
<ItemContent>
|
||||
<ItemTitle>
|
||||
{selectedTPessoa.cpf_cnpj} - {selectedTPessoa.nome}
|
||||
{data.pessoa?.cpf_cnpj} - {data.pessoa?.nome}
|
||||
</ItemTitle>
|
||||
<ItemDescription>
|
||||
{selectedTPessoa.email ? selectedTPessoa.email : 'Email não informado'}
|
||||
{data.pessoa?.email || 'Email não informado'}
|
||||
</ItemDescription>
|
||||
{/* 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) => (
|
||||
<TPessoaCartaoForm
|
||||
index={index}
|
||||
form={form}
|
||||
index={item_index}
|
||||
key={param.config_id}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
))}
|
||||
|
||||
</ItemContent>
|
||||
<ItemActions>
|
||||
{servico.requer_biometria === 'S' && (
|
||||
{data.servico.requer_biometria === 'S' && (
|
||||
<Button
|
||||
type="button"
|
||||
size="icon-lg"
|
||||
variant="outline"
|
||||
className={`rounded-full cursor-pointer ` + classBiometriaButton}
|
||||
aria-label="Invite"
|
||||
onClick={handleCaptureBiometria}
|
||||
className={`rounded-full cursor-pointer ${biometriaButtonClass}`}
|
||||
aria-label="Capturar biometria"
|
||||
onClick={() => { handleBiometria() }}
|
||||
>
|
||||
<FingerprintIcon />
|
||||
</Button>
|
||||
)}
|
||||
{servico.requer_biometria === 'S' && (
|
||||
{data.servico.requer_biometria && (
|
||||
<Button
|
||||
type="button"
|
||||
size="icon-lg"
|
||||
variant="outline"
|
||||
className="rounded-full cursor-pointer"
|
||||
aria-label="Invite"
|
||||
onClick={() => { setIsOpenWebcamDialog(true) }}
|
||||
aria-label="Capturar imagem"
|
||||
onClick={() => { setIsWebCamOpenDialog(true) }}
|
||||
>
|
||||
<WebcamIcon />
|
||||
</Button>
|
||||
)}
|
||||
</ItemActions>
|
||||
</Item>
|
||||
{isOpenWebcamDialog && (
|
||||
{isWebCamOpenDialog && (
|
||||
<WebCamDialog
|
||||
isOpen={isOpenWebcamDialog}
|
||||
onClose={() => { setIsOpenWebcamDialog(false) }}
|
||||
onSave={handleCapture}
|
||||
isOpen={isWebCamOpenDialog}
|
||||
onClose={() => { setIsWebCamOpenDialog(false) }}
|
||||
onSave={() => { }}
|
||||
key={1}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Memo para evitar re-renderizações desnecessárias da subview
|
||||
export default memo(TPessoaTableFormSubview);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ export default function TPessoaCartaoForm({ index, form }: TPessoaCartaoFormInte
|
|||
<FormControl>
|
||||
<Input {...field}
|
||||
type="number"
|
||||
onChange={(e) => field.onChange(parseNumberInput(e))} />
|
||||
onChange={(e) => field.onChange(parseNumberInput(e))}
|
||||
defaultValue={field.value ?? ""}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
@ -69,7 +71,10 @@ export default function TPessoaCartaoForm({ index, form }: TPessoaCartaoFormInte
|
|||
<FormItem>
|
||||
<FormLabel>Data de Abertura</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type="date" />
|
||||
<Input {...field}
|
||||
type="date"
|
||||
defaultValue={field.value ?? ""}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -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<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.descricao)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{GetCapitalize(data.tabela)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
sortingFn: (a, b) =>
|
||||
(a.original.descricao?.toLowerCase() || '').localeCompare(
|
||||
b.original.descricao?.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>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: 'quantidade',
|
||||
header: ({ column }) => SortableHeader('Qtd.', column),
|
||||
enableSorting: true,
|
||||
cell: ({ row, table }) => {
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Botão diminuir */}
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
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>
|
||||
|
||||
{/* Campo de entrada */}
|
||||
<div className="w-12">
|
||||
<Input
|
||||
type="number"
|
||||
className="h-8 text-center px-1 bg-white dark:bg-gray-700
|
||||
border-gray-300 dark:border-gray-700
|
||||
text-gray-600 dark:text-gray-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Botão aumentar */}
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
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>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<div>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
filterColumn="servico"
|
||||
filterPlaceholder="Buscar por serviço..."
|
||||
/>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>
|
||||
Serviço / Tabela
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Emolumento
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Tx. Judiciária
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Fundesp 21%
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
ISS 5%
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
Total
|
||||
</TableHead>
|
||||
<TableHead className="text-center">Qtd.</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data?.length ? (
|
||||
data.map((item, index) => {
|
||||
return (
|
||||
<React.Fragment key={`fragment-${index}`}>
|
||||
{/* Linha principal */}
|
||||
<TableRow key={`row-${index}`} className="cursor-pointer hover:bg-gray-50">
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-3">
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900 capitalize">
|
||||
{GetCapitalize(item.descricao)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{GetCapitalize(item.tabela)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>R$ {item.emolumento ?? '---'}</TableCell>
|
||||
<TableCell>R$ {item.taxa_judiciaria ?? '---'}</TableCell>
|
||||
<TableCell>R$ {item.fundesp ?? '---'}</TableCell>
|
||||
<TableCell>R$ {item.valor_iss ?? '---'}</TableCell>
|
||||
<TableCell>R$ {item.valor ?? '---'}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-1 justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="bg-white border h-8 w-8 rounded-lg"
|
||||
>
|
||||
<Minus className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<Input type="number" className="h-8 text-center px-1 w-12" />
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="bg-white border h-8 w-8 rounded-lg"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* SubView */}
|
||||
{item.subview && (
|
||||
<TableRow className="bg-gray-50">
|
||||
<TableCell colSpan={7} className="p-4">
|
||||
<TPessoaTableFormSubview
|
||||
item_index={item.index}
|
||||
data={item.subview}
|
||||
params={params}
|
||||
form={form}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center py-4">
|
||||
Nenhum item encontrado.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const TServicoItemPedidoFormTable = memo(TServicoItemPedidoFormTableComponent);
|
||||
|
|
@ -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 (
|
||||
<Card className="card-border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl font-semibold">Itens: {items?.length}</CardTitle>
|
||||
<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"
|
||||
>
|
||||
<div key={item.servico_itempedido_id} className="bg-cart-item border-cart-border rounded-lg border p-4">
|
||||
{/* Descrição */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-foreground line-clamp-2 text-sm lg:text-base font-bold">
|
||||
{item.descricao}
|
||||
{item.descricao} de {item.nome} - <ServicosPedidosSituacoesBadge situacao={item.situacao} />
|
||||
</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 className="grid grid-cols-6 gap-3 mt-4">
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground">Emolumento</div>
|
||||
<div className="font-semibold">{money(item.emolumento)}</div>
|
||||
<div className="font-semibold">{FormatMoney(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 className="font-semibold">{FormatMoney(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 className="font-semibold">{FormatMoney(item.valor_iss)}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground">Fundesp</div>
|
||||
<div className="font-semibold">{money(item.fundesp)}</div>
|
||||
<div className="font-semibold">{FormatMoney(item.fundesp)}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground">Total</div>
|
||||
<div className="font-semibold">{money(item.valor)}</div>
|
||||
<div className="font-semibold">{FormatMoney(item.valor)}</div>
|
||||
</div>
|
||||
|
||||
<div className="text-end">
|
||||
<ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">
|
||||
Ações
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" aria-label="More Options">
|
||||
<MoreHorizontalIcon />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem className='cursor-pointer'>
|
||||
<TicketIcon />
|
||||
Imprimir Etiqueta
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem className='cursor-pointer'>
|
||||
<IdCardIcon />
|
||||
Imprimir Cartão
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem className='cursor-pointer' variant="destructive">
|
||||
<BookmarkX /> Estornar Item
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { Input } from "@/components/ui/input";
|
||||
|
||||
export function PessoaSubviewForm({ form, index, pessoa }) {
|
||||
return (
|
||||
<div className="space-y-3 p-4 border rounded-md bg-gray-50">
|
||||
|
||||
<h3 className="font-semibold text-gray-700">
|
||||
Dados da Pessoa Selecionada
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Nome</label>
|
||||
<Input {...form.register(`itens.${index}.pessoa_nome`)} defaultValue={pessoa?.nome} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">CPF/CNPJ</label>
|
||||
<Input {...form.register(`itens.${index}.pessoa_documento`)} defaultValue={pessoa?.cpfcnpj}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<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>
|
||||
<h3 className='text-4xl font-bold mb-4'>
|
||||
Pedido: #{TServicoPedido?.servico_pedido_id}
|
||||
</h3>
|
||||
{/* Main */}
|
||||
<div className="container mx-auto h-full">
|
||||
<div className="flex flex-col lg:flex-row gap-4">
|
||||
|
|
@ -99,18 +94,12 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed
|
|||
</div>
|
||||
</div>
|
||||
<Separator className="my-5" />
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<CalendarIcon className="opacity-70" />
|
||||
<div className="flex items-center gap-2">
|
||||
<CalendarIcon className="opacity-70 size-5" />
|
||||
<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">
|
||||
|
|
@ -141,17 +130,34 @@ export default function TServicoPedidoDetails({ servico_pedido_id }: TServicoPed
|
|||
<Card className="card-border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl font-semibold">
|
||||
Impressões
|
||||
Controles
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2">
|
||||
<Button className="w-full" variant="outline">Recibo</Button>
|
||||
<Button className="w-full">Etiqueta</Button>
|
||||
<Button className="w-full cursor-pointer" variant={`outline`} onClick={() => { handleCancelServicoPedidoOpenDialog(true) }}>
|
||||
<BookmarkX /> Estornar Pedido
|
||||
</Button>
|
||||
<Button className="w-full cursor-pointer">
|
||||
<ReceiptText /> Imprimir Recibo
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Confirma o cancelamento do pedido */}
|
||||
{isCancelServicoPedidoDialogOpen && (
|
||||
<ConfirmDialog
|
||||
isOpen={isCancelServicoPedidoDialogOpen}
|
||||
title="Estorno do pedido"
|
||||
description="Atenção"
|
||||
message="Deseja estornar o pedido?"
|
||||
confirmText="Sim, estornar"
|
||||
cancelText="Fechar"
|
||||
onConfirm={() => { handleCancelServicoPedidoOpenDialog(false) }}
|
||||
onCancel={() => { handleCancelServicoPedidoOpenDialog(false) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -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 <ServicosPedidosSituacoesBadge situacao={situacao} />
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground">Emolumento</span>
|
||||
<span className="font-medium">{fmt.format(emolumento)}</span>
|
||||
<span className="font-medium">{FormatMoney(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>
|
||||
<span className="font-medium">{FormatMoney(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>
|
||||
<span className="font-medium">{FormatMoney(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>
|
||||
<span className="font-medium">{FormatMoney(fundesp)}</span>
|
||||
</div>
|
||||
|
||||
<Separator className="border-cart-border" />
|
||||
|
||||
<div className="flex items-center justify-between text-xl">
|
||||
<span className="text-muted-foreground font-semibold">Total</span>
|
||||
<span className="font-semibold">{FormatMoney(total)}</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>
|
||||
<span className="text-muted-foreground">Emolumento</span>
|
||||
<span className="font-medium">{FormatMoney(emolumento)}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground">Tx. Judiciária</span>
|
||||
<span className="font-medium">{FormatMoney(taxa_judiciaria)}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground">ISS</span>
|
||||
<span className="font-medium">{FormatMoney(valor_iss)}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground">Fundesp</span>
|
||||
<span className="font-medium">{FormatMoney(fundesp)}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<TPessoaTableFormSubview
|
||||
form={form}
|
||||
servico={selectedServicoTipo}
|
||||
params={TServicoPedidoParams}
|
||||
selectedTPessoa={selectedTPessoa}
|
||||
index={index}
|
||||
/>
|
||||
);
|
||||
// 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
|
|||
/>
|
||||
</div>
|
||||
<div className="col-span-12">
|
||||
<TServicoItemPedidoFormTable data={TServicoItemPedidoLocal} />
|
||||
<TServicoItemPedidoFormTable
|
||||
form={form}
|
||||
params={TServicoPedidoParams}
|
||||
data={TServicoItemPedidoLocal} />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
|
|||
|
|
@ -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<TServicoItemPedidoAddInterface>) => {
|
||||
export function useTServicoItemPedidoAddHook<TFormValues extends FieldValues>(setValue?: UseFormSetValue<TFormValues>) {
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
|
|
@ -25,13 +25,23 @@ export const useTServicoItemPedidoAddHook = (setValue?: UseFormSetValue<TServico
|
|||
|
||||
setResponse(payload)
|
||||
|
||||
return null;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Realiza a busca do item
|
||||
const response = await GCalculoServicoService(payload, data);
|
||||
|
||||
// Verifico se tenho código de resposta
|
||||
if (response.status) {
|
||||
|
||||
// Exibo a resposta em tela
|
||||
setResponse(response)
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Obtem o resultado da busca
|
||||
const item = response;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { UseFormSetValue } from 'react-hook-form';
|
||||
import { FieldValues, UseFormSetValue } from 'react-hook-form';
|
||||
|
||||
import TServicoItemPedidoAddInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddInterface';
|
||||
import TServicoItemPedidoIndexResponseInterface from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface';
|
||||
import { default as TServicoItemPedidoAddResponseInterface, default as TServicoItemPedidoIndexResponseInterface } from '@/packages/servicos/interfaces/TServicoItemPedido/TServicoItemPedidoAddResponseInterface';
|
||||
|
||||
export const useTServicoItemPedidoLocalAddHook = (setValue?: UseFormSetValue<TServicoItemPedidoAddInterface>) => {
|
||||
|
||||
export function useTServicoItemPedidoLocalAddHook<TFormValues extends FieldValues>(setValue?: UseFormSetValue<TFormValues>) {
|
||||
|
||||
const [TServicoItemPedidoLocal, setLocalTServicoItemPedido] = useState<TServicoItemPedidoIndexResponseInterface[]>([]);
|
||||
|
||||
const localAddTServicoItemPedido = (item: any) => {
|
||||
const localAddTServicoItemPedido = (item: TServicoItemPedidoAddResponseInterface) => {
|
||||
|
||||
setLocalTServicoItemPedido((prev) => {
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export function useTServicoPedidoFormHook(defaults?: Partial<TServicoPedidoFormV
|
|||
pagador_cpfcnpj: "",
|
||||
valor_pedido: 0,
|
||||
valor_pago: 0,
|
||||
situacao: "F",
|
||||
tipo_pagamento: null,
|
||||
...defaults,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const useTServicoPedidoShowHook = () => {
|
|||
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [TServicoPedido, setTServicoPedido] = useState<TServicoPedidoInterface>(null);
|
||||
const [TServicoPedido, setTServicoPedido] = useState<TServicoPedidoInterface>();
|
||||
|
||||
const showTServicoPedido = async (data: TServicoPedidoInterface) => {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
export default interface TServicoSubviewInterface {
|
||||
pessoa_id: number;
|
||||
subview: any;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<typeof TServicoPedidoFormSchema>;
|
||||
5
src/shared/actions/money/FormatMoney.ts
Normal file
5
src/shared/actions/money/FormatMoney.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export default function FormatMoney(data) {
|
||||
|
||||
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(Number(data || 0));
|
||||
|
||||
}
|
||||
|
|
@ -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<TData extends { subview?: React.ReactNode | (() => Rea
|
|||
colSpan={row.getVisibleCells().length}
|
||||
className="p-0"
|
||||
>
|
||||
{subview}
|
||||
<DataTableSubview subview={subview} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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 <Component {...props} />;
|
||||
}
|
||||
|
|
@ -4,10 +4,9 @@ import { useState } from 'react';
|
|||
|
||||
import { FingerTechCaptureService } from '@/shared/services/FingerTech/FingerTechCaptureService';
|
||||
|
||||
|
||||
export const useFingerTechIndexHook = () => {
|
||||
|
||||
const [base64, setBase64] = useState<any>('');
|
||||
const [base64, setBase64] = useState<string>('');
|
||||
|
||||
const captureFingerTech = async () => {
|
||||
|
||||
|
|
|
|||
18
src/shared/hooks/sound/useSoundHook.ts
Normal file
18
src/shared/hooks/sound/useSoundHook.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
'use client'
|
||||
|
||||
import { useCallback, useRef } from "react";
|
||||
|
||||
export function useSoundHook(soundPath: string) {
|
||||
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||
|
||||
if (!audioRef.current) {
|
||||
audioRef.current = new Audio(soundPath);
|
||||
}
|
||||
|
||||
const play = useCallback(() => {
|
||||
audioRef.current?.play().catch(() => { });
|
||||
}, []);
|
||||
|
||||
return play;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue