[MVPTN-90] refactor(Correção): corrigindo observações passadas relacionadas a utilizar Enum de situações, retirar trycatch, arrumando coluna de situação com badges
This commit is contained in:
parent
727e665db4
commit
66f285ba74
11 changed files with 331 additions and 34 deletions
11
package-lock.json
generated
11
package-lock.json
generated
|
|
@ -27,6 +27,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"cookies-next": "^6.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
|
|
@ -2238,6 +2239,16 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/input-otp": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz",
|
||||
"integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"cookies-next": "^6.1.0",
|
||||
"faker-js": "^1.0.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import { useTTBReconhecimentoTipoReadHook } from "../../_hooks/t_tb_reconhecimen
|
|||
import { useTTBReconhecimentoTipoSaveHook } from "../../_hooks/t_tb_reconhecimentotipo/useTTBReconhecimentoTipoSaveHook";
|
||||
import { useTTBReconhecimentoTipoDeleteHook } from "../../_hooks/t_tb_reconhecimentotipo/useTTBReconhecimentoTipoDeleteHook";
|
||||
|
||||
import ConfirmDialog from "@/app/_components/confirm_dialog/ConfirmDialog";
|
||||
import { useConfirmDialog } from "@/app/_components/confirm_dialog/useConfirmDialog";
|
||||
import ConfirmExclusion from "@/app/_components/confirm_exclusion/ConfirmExclusion";
|
||||
import { useConfirmExclusion } from "@/app/_components/confirm_exclusion/useConfirmExclusion";
|
||||
|
||||
import TTBReconhecimentoTipoInterface from "../../_interfaces/TTBReconhecimentoTipoInterface";
|
||||
|
||||
|
|
@ -32,14 +32,26 @@ export default function TTBAndamentoServico() {
|
|||
const [itemToDelete, setItemToDelete] = useState<TTBReconhecimentoTipoInterface | null>(null);
|
||||
|
||||
/**
|
||||
* Hook do modal de confirmação
|
||||
* Hook do modal de exclusão com OTP
|
||||
*/
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
code,
|
||||
isValid,
|
||||
handleChange,
|
||||
} = useConfirmExclusion({
|
||||
expectedCode: "123456", // 🔑 aqui você define o código esperado
|
||||
onConfirm: () => {
|
||||
console.log("Confirmação aceita com código OTP válido");
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log("Ação cancelada");
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Abre o formulário no modo de edição ou criação
|
||||
|
|
@ -78,7 +90,7 @@ export default function TTBAndamentoServico() {
|
|||
// Define o item atual para remoção
|
||||
setItemToDelete(item);
|
||||
|
||||
// Abre o modal de confirmação
|
||||
// Abre o modal de exclusão
|
||||
openConfirmDialog();
|
||||
|
||||
}, [openConfirmDialog]);
|
||||
|
|
@ -105,6 +117,10 @@ export default function TTBAndamentoServico() {
|
|||
|
||||
}, [itemToDelete, fetchTTBReconhecimentosTipos, handleCancel]);
|
||||
|
||||
const handleResendCode = (async () => {
|
||||
return alert("Reenviando código de exclusão")
|
||||
})
|
||||
|
||||
/**
|
||||
* Busca inicial dos dados
|
||||
*/
|
||||
|
|
@ -140,18 +156,20 @@ export default function TTBAndamentoServico() {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
{/* Modal de confirmação de exclusão com OTP */}
|
||||
<ConfirmExclusion
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir o andamento "${itemToDelete?.descricao}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
description="Digite o código de exclusão para confirmar:"
|
||||
confirmText="Continuar"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
expectedCode={"123456"}
|
||||
onResendCode={handleResendCode}
|
||||
/>
|
||||
|
||||
|
||||
{/* Formulário de criação/edição */}
|
||||
<TTBReconhecimentoTipoForm
|
||||
isOpen={isFormOpen}
|
||||
|
|
@ -161,4 +179,4 @@ export default function TTBAndamentoServico() {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { Label } from "@/components/ui/label";
|
|||
import { TCensecTipoAtoSchema } from "../../_schemas/TCensecTipoAtoSchema";
|
||||
import { TCensecTipoAtoInterface } from "../../_interfaces/TCensecTipoAtoInterface";
|
||||
import TCensecInterface from "../../_interfaces/TCensecInterface";
|
||||
import { SituacoesEnum } from "@/enums/SituacoesEnum";
|
||||
|
||||
type FormValues = z.infer<typeof TCensecTipoAtoSchema>;
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ export default function TCensecTipoAtoForm({
|
|||
censec_id: 0,
|
||||
codigo: null,
|
||||
descricao: "",
|
||||
situacao: "A",
|
||||
situacao: SituacoesEnum.A,
|
||||
tipo_separacao: null,
|
||||
tipo_revogacao: null,
|
||||
},
|
||||
|
|
@ -105,17 +106,17 @@ export default function TCensecTipoAtoForm({
|
|||
name="censec_id"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Censec</FormLabel>
|
||||
<FormLabel>Central do Censec</FormLabel>
|
||||
<Select
|
||||
value={field.value?.toString()}
|
||||
onValueChange={(val) => field.onChange(Number(val))}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Selecione um Censec" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectContent className="w-full">
|
||||
{tCensec.map((c) => (
|
||||
<SelectItem key={c.censec_id} value={String(c.censec_id)}>
|
||||
{c.descricao}
|
||||
|
|
@ -128,6 +129,7 @@ export default function TCensecTipoAtoForm({
|
|||
)}
|
||||
/>
|
||||
|
||||
|
||||
{/* Situação */}
|
||||
<Controller
|
||||
name="situacao"
|
||||
|
|
@ -155,11 +157,11 @@ export default function TCensecTipoAtoForm({
|
|||
value={field.value ?? ""}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Selecione" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectContent className="w-full">
|
||||
<SelectItem value="S">Sim</SelectItem>
|
||||
<SelectItem value="N">Não</SelectItem>
|
||||
</SelectContent>
|
||||
|
|
@ -181,11 +183,11 @@ export default function TCensecTipoAtoForm({
|
|||
value={field.value ?? ""}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Selecione" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectContent className="w-full">
|
||||
<SelectItem value="S">Sim</SelectItem>
|
||||
<SelectItem value="N">Não</SelectItem>
|
||||
</SelectContent>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,25 @@ interface TCensecTipoAtoTableProps {
|
|||
onDelete: (item: TCensecTipoAtoInterface, isEditingFormStatus: boolean) => void;
|
||||
}
|
||||
|
||||
function StatusBadge({ situacao }: { situacao: string }) {
|
||||
const isActive = situacao === "A";
|
||||
|
||||
const baseClasses =
|
||||
"text-xs font-medium px-2.5 py-0.5 rounded-sm me-2";
|
||||
|
||||
const activeClasses =
|
||||
"bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300";
|
||||
|
||||
const inactiveClasses =
|
||||
"bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300";
|
||||
|
||||
return (
|
||||
<span className={`${baseClasses} ${isActive ? activeClasses : inactiveClasses}`}>
|
||||
{isActive ? "Ativo" : "Inativo"}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TCensecTipoAtoTable({
|
||||
data,
|
||||
tCensec,
|
||||
|
|
@ -39,10 +58,10 @@ export default function TCensecTipoAtoTable({
|
|||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>#</TableHead>
|
||||
<TableHead>CENSEC</TableHead>
|
||||
<TableHead>Situação</TableHead>
|
||||
<TableHead>Central do CENSEC</TableHead>
|
||||
{/*<TableHead>Código</TableHead>*/}
|
||||
<TableHead>Descrição</TableHead>
|
||||
<TableHead>Situação</TableHead>
|
||||
<TableHead>Separação</TableHead>
|
||||
<TableHead>Revogação</TableHead>
|
||||
<TableHead className="text-right">Ações</TableHead>
|
||||
|
|
@ -57,12 +76,12 @@ export default function TCensecTipoAtoTable({
|
|||
<TableCell className="font-medium">
|
||||
{item.censec_tipoato_id}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusBadge situacao={item.situacao} />
|
||||
</TableCell>
|
||||
<TableCell>{censecDesc}</TableCell>
|
||||
{/*<TableCell>{item.codigo ?? "-"}</TableCell>*/}
|
||||
<TableCell>{item.descricao}</TableCell>
|
||||
<TableCell>
|
||||
{item.situacao === "A" ? "Ativo" : "Inativo"}
|
||||
</TableCell>
|
||||
<TableCell>{item.tipo_separacao ?? "-"}</TableCell>
|
||||
<TableCell>{item.tipo_revogacao ?? "-"}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { z } from "zod";
|
||||
import { SituacoesEnum } from "@/enums/SituacoesEnum";
|
||||
|
||||
export const TCensecTipoAtoSchema = z.object({
|
||||
censec_tipoato_id: z.number().int(),
|
||||
censec_id: z.number().int(),
|
||||
censec_id: z.number("Precisa referir a qual Central do Censec pertence").int(),
|
||||
codigo: z.number().int().nullable(),
|
||||
descricao: z.string(),
|
||||
situacao: z.enum(["A", "I"]),
|
||||
descricao: z.string().min(1, "Campo descrição deve ser preenchido"),
|
||||
situacao: z.enum(SituacoesEnum, { message: "Tipo inválido" }),
|
||||
tipo_separacao: z.enum(["S", "N"]).nullable(),
|
||||
tipo_revogacao: z.enum(["S", "N"]).nullable(),
|
||||
});
|
||||
|
|
@ -3,13 +3,8 @@ import { TCensecTipoAtoIndexData } from "../../_data/TCensecTipoAto/GMedidaTipoI
|
|||
|
||||
async function executeTCensecTipoAtoIndexService() {
|
||||
|
||||
try {
|
||||
const response = await TCensecTipoAtoIndexData();
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
export const TCensecTipoAtoIndexService = withClientErrorHandler(executeTCensecTipoAtoIndexService)
|
||||
|
|
@ -6,8 +6,6 @@ async function executeTCensecTipoAtoSaveService(data: TCensecTipoAtoInterface) {
|
|||
|
||||
const response = await TCensecTipoAtoSaveData(data);
|
||||
|
||||
console.log('GTBRegimeComunhaoSaveData', response)
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
|
|
|||
124
src/app/_components/confirm_exclusion/ConfirmExclusion.tsx
Normal file
124
src/app/_components/confirm_exclusion/ConfirmExclusion.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogAction,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSlot,
|
||||
} from '@/components/ui/input-otp';
|
||||
|
||||
interface ConfirmExclusionProps {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
description?: string;
|
||||
expectedCode: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
onResendCode: () => void; // Função para reenviar o código
|
||||
}
|
||||
|
||||
export default function ConfirmExclusion({
|
||||
isOpen,
|
||||
title,
|
||||
description,
|
||||
expectedCode,
|
||||
confirmText = 'Continuar',
|
||||
cancelText = 'Cancelar',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
onResendCode, // A função para reenvio do código
|
||||
}: ConfirmExclusionProps) {
|
||||
const [code, setCode] = useState('');
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
const [isResending, setIsResending] = useState(false); // Novo estado para controle de envio do código
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
setCode(value);
|
||||
setIsValid(value === expectedCode);
|
||||
};
|
||||
|
||||
const handleResendCode = async () => {
|
||||
setIsResending(true);
|
||||
try {
|
||||
await onResendCode(); // Chamando a função de reenvio
|
||||
} catch (error) {
|
||||
console.error("Erro ao reenviar código", error);
|
||||
} finally {
|
||||
setIsResending(false); // Resetando o estado de envio
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog open={isOpen} onOpenChange={(open) => !open && onCancel()}>
|
||||
<AlertDialogContent className="max-w-lg mx-auto p-6">
|
||||
<AlertDialogHeader className="text-center">
|
||||
<AlertDialogTitle className="text-xl font-semibold text-center">{title}</AlertDialogTitle>
|
||||
{description && (
|
||||
<AlertDialogDescription className="mt-2 text-sm text-muted-foreground text-center">
|
||||
{description}
|
||||
</AlertDialogDescription>
|
||||
)}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<div className="py-4 text-sm text-muted-foreground space-y-4">
|
||||
<div className="flex justify-center">
|
||||
<InputOTP
|
||||
maxLength={expectedCode.length}
|
||||
value={code}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<InputOTPGroup className="flex gap-4">
|
||||
{expectedCode.split('').map((_, index) => (
|
||||
<InputOTPSlot
|
||||
key={index}
|
||||
index={index}
|
||||
className="w-12 h-12 text-center text-lg border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
))}
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
|
||||
{/* Botão "Reenviar Código" */}
|
||||
<div className="flex justify-center items-center text-center gap-2 mt-4">
|
||||
<AlertDialogDescription>
|
||||
Não recebeu o código?
|
||||
</AlertDialogDescription>
|
||||
<button
|
||||
onClick={handleResendCode}
|
||||
className={`text-blue-500 font-semibold cursor-pointer ${isResending ? 'cursor-not-allowed text-gray-400' : 'hover:text-blue-600'}`}
|
||||
disabled={isResending} // Desabilita o botão enquanto o código está sendo reenviado
|
||||
>
|
||||
{isResending ? 'Enviando...' : 'Reenviar Código'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={onCancel} className="text-gray-600 hover:text-gray-800">
|
||||
{cancelText}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={onConfirm}
|
||||
disabled={!isValid}
|
||||
>
|
||||
{confirmText}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
51
src/app/_components/confirm_exclusion/useConfirmExclusion.ts
Normal file
51
src/app/_components/confirm_exclusion/useConfirmExclusion.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { useCallback, useState } from "react";
|
||||
|
||||
interface UseConfirmExclusionOptions {
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
expectedCode: string; // código que o usuário precisa digitar
|
||||
}
|
||||
|
||||
export function useConfirmExclusion({ onConfirm, onCancel, expectedCode }: UseConfirmExclusionOptions) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [code, setCode] = useState("");
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
|
||||
const openDialog = useCallback(() => setIsOpen(true), []);
|
||||
const closeDialog = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
setCode(""); // limpa o código quando fecha
|
||||
setIsValid(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
if (isValid) {
|
||||
onConfirm?.();
|
||||
closeDialog();
|
||||
}
|
||||
}, [isValid, onConfirm, closeDialog]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
onCancel?.();
|
||||
closeDialog();
|
||||
}, [onCancel, closeDialog]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: string) => {
|
||||
setCode(value);
|
||||
setIsValid(value === expectedCode);
|
||||
},
|
||||
[expectedCode]
|
||||
);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
code,
|
||||
isValid,
|
||||
openDialog,
|
||||
closeDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
handleChange,
|
||||
};
|
||||
}
|
||||
77
src/components/ui/input-otp.tsx
Normal file
77
src/components/ui/input-otp.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { OTPInput, OTPInputContext } from "input-otp"
|
||||
import { MinusIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function InputOTP({
|
||||
className,
|
||||
containerClassName,
|
||||
...props
|
||||
}: React.ComponentProps<typeof OTPInput> & {
|
||||
containerClassName?: string
|
||||
}) {
|
||||
return (
|
||||
<OTPInput
|
||||
data-slot="input-otp"
|
||||
containerClassName={cn(
|
||||
"flex items-center gap-2 has-disabled:opacity-50",
|
||||
containerClassName
|
||||
)}
|
||||
className={cn("disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-group"
|
||||
className={cn("flex items-center", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function InputOTPSlot({
|
||||
index,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
index: number
|
||||
}) {
|
||||
const inputOTPContext = React.useContext(OTPInputContext)
|
||||
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-slot"
|
||||
data-active={isActive}
|
||||
className={cn(
|
||||
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div data-slot="input-otp-separator" role="separator" {...props}>
|
||||
<MinusIcon />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
||||
Loading…
Add table
Reference in a new issue