diff --git a/geradoresfe/src/components/Geradores/GeradorCard.tsx b/geradoresfe/src/components/Geradores/GeradorCard.tsx index b27e9c4..574c742 100644 --- a/geradoresfe/src/components/Geradores/GeradorCard.tsx +++ b/geradoresfe/src/components/Geradores/GeradorCard.tsx @@ -1,16 +1,17 @@ -import { useState } from "react"; +// src/components/Geradores/GeradorCard.tsx +import { useCallback, useState } from "react"; import { RefreshCwIcon, CopyIcon } from "lucide-react"; import { Button } from "../ui/Button"; import { Input } from "../ui/Input"; import { Select } from "../ui/Select"; import { ResultDisplay } from "../ui/ResultDisplay"; -import { toast } from 'react-hot-toast'; +import { toast } from "react-hot-toast"; -type Parametro = - | { nome: string; tipo: "text" | "number" | "password"; placeholder?: string } - | { nome: string; tipo: "select"; options: { label: string; value: string }[] }; +export type Parametro = + | { nome: string; tipo: "text" | "number" | "password"; placeholder?: string; default?: string } + | { nome: string; tipo: "select"; options: { label: string; value: string }[]; default?: string }; -type GeradorCardProps = { +export type GeradorCardProps = { readonly titulo: string; readonly descricao?: string; readonly parametros?: Parametro[]; @@ -25,50 +26,92 @@ export default function GeradorCard({ onGerar, onValidar, }: GeradorCardProps) { - const [resultado, setResultado] = useState(""); - const [paramInputs, setParamInputs] = useState>({}); - const [validado, setValidado] = useState(null); - - const handleInputChange = (nome: string, valor: string) => { - setParamInputs((prev) => ({ ...prev, [nome]: valor })); - }; - - const handleGerar = async () => { - const gerado = await onGerar(paramInputs); - setResultado(gerado); - setValidado(null); - }; - - const handleValidar = async () => { - if (onValidar) { - const isValid = await onValidar(resultado); - setValidado(isValid); + // Inicializa paramInputs com defaults vindos de `parametros` + const initialParamInputs = parametros.reduce>((acc, p) => { + debugger; + if (p.default) { + acc[p.nome] = p.default; + } else { + acc[p.nome] = ""; } - }; + return acc; + }, {}); - const handleCopiar = () => { + const [resultado, setResultado] = useState(""); + const [paramInputs, setParamInputs] = useState>(initialParamInputs); + const [validado, setValidado] = useState(null); + const [loading, setLoading] = useState(false); + + const handleInputChange = useCallback((nome: string, valor: string) => { + setParamInputs((prev) => ({ ...prev, [nome]: valor })); + }, []); + + const handleGerar = useCallback(async () => { + setLoading(true); + setValidado(null); + try { + const gerado = await onGerar(paramInputs); + setResultado(typeof gerado === "string" ? gerado : String(gerado ?? "")); + toast.success(`${titulo} gerado com sucesso.`, { duration: 2000 }); + } catch (err) { + console.error("Erro ao gerar:", err); + toast.error("Falha ao gerar. Tente novamente."); + } finally { + setLoading(false); + } + }, [onGerar, paramInputs, titulo]); + + const handleValidar = useCallback(async () => { + if (!onValidar) return; + if (!resultado) { + toast.error("Nenhum valor para validar."); + return; + } + + setLoading(true); + try { + const isValid = await onValidar(resultado); + setValidado(Boolean(isValid)); + if (isValid) { + toast.success("Válido", { duration: 1500 }); + } else { + toast.error("Inválido", { duration: 1500 }); + } + } catch (err) { + console.error("Erro ao validar:", err); + toast.error("Falha na validação. Tente novamente."); + setValidado(null); + } finally { + setLoading(false); + } + }, [onValidar, resultado]); + + const handleCopiar = useCallback(async () => { if (!resultado) return; - navigator.clipboard.writeText(resultado); - toast.success(`${titulo} copiado!`, - { - duration: 3000 - }); - }; + try { + await navigator.clipboard.writeText(resultado); + toast.success(`${titulo} copiado!`, { duration: 2000 }); + } catch (err) { + console.error("Erro ao copiar:", err); + toast.error("Não foi possível copiar para a área de transferência."); + } + }, [resultado, titulo]); - const handleResultadoChange = (valor: string) => { + const handleResultadoChange = useCallback((valor: string) => { setResultado(valor); - setValidado(null); // resetar estado de validação ao editar - }; + setValidado(null); // reset ao editar + }, []); return ( -
- {/* Título e descrição */} +

{titulo}

{descricao &&

{descricao}

}
- {/* Parâmetros dinâmicos */} {parametros.length > 0 && (
{parametros.map((param) => { @@ -78,9 +121,10 @@ export default function GeradorCard({ key={param.nome} label={param.nome} options={param.options} - value={paramInputs[param.nome] || ""} + value={paramInputs[param.nome] ?? ""} onChange={(val) => handleInputChange(param.nome, val)} placeholder={`Selecionar ${param.nome}`} + aria-label={param.nome} /> ); } @@ -90,21 +134,23 @@ export default function GeradorCard({ key={param.nome} type={param.tipo} placeholder={param.placeholder || param.nome} - value={paramInputs[param.nome] || ""} + value={paramInputs[param.nome] ?? ""} onChange={(e) => handleInputChange(param.nome, e.target.value)} + aria-label={param.nome} /> ); })}
)} - {/* Resultado + botão de copiar como ícone */}
+ {resultado && ( + /> )}
- {/* Botões distribuídos numa linha com preenchimento igual */}
{onValidar && ( - )}
diff --git a/geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx b/geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx index d203e2e..040e4c2 100644 --- a/geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx +++ b/geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx @@ -1,5 +1,5 @@ -import GeradorCard from "../GeradorCard"; -import { EnumToOptions } from "../../../library/utils"; +import GeradorCard, { type Parametro } from "../GeradorCard"; +import { EnumToOptions, getEnumKeyByValue } from "../../../library/utils"; import { NIFType } from "../../../service/api"; import GeradorService from "../../../Api/GeradorApi"; @@ -9,31 +9,31 @@ const NifTypes = EnumToOptions(NIFType).map((opt) => ({ })); const GenerateNIF = () => { - - const handleGenerateNIF = async (params: Record) => { - const tipoSelecionado = params["Tipo de NIF"]; - const nif = await GeradorService.GenerateNIF(tipoSelecionado); - + const handleGenerateNIF = async (params?: Record) => { + const tipoSelecionado = params?.["Tipo de NIF"] ?? String(NIFType.PessoaSingular2); + const nif = String(await GeradorService.GenerateNIF(tipoSelecionado)); return nif; }; const handleValidateNIF = async (valor: string) => { - // Lógica real de validação - const bol = await GeradorService.ValidateNIF(valor); + const bol = Boolean(await GeradorService.ValidateNIF(valor)); return bol; }; + const parametros: Parametro[] = [ + { + nome: "Tipo de NIF", + tipo: "select", + options: NifTypes, + default: getEnumKeyByValue(NIFType, NIFType.PessoaSingular2) + }, + ]; + return (
diff --git a/geradoresfe/src/library/utils.tsx b/geradoresfe/src/library/utils.tsx index cf63772..b3a9a43 100644 --- a/geradoresfe/src/library/utils.tsx +++ b/geradoresfe/src/library/utils.tsx @@ -1,18 +1,24 @@ const Utils = { EnumToOptions, - // Outras funções podem ser adicionadas aqui + // Outras fun��es podem ser adicionadas aqui }; export function EnumToOptions>(enumObj: T): any[] { return Object.entries(enumObj) - .filter(([key]) => isNaN(Number(key))) // Ignora entradas numéricas, caso existam + .filter(([key]) => isNaN(Number(key))) // Ignora entradas num�ricas, caso existam .map(([key, value]) => ({ value: key, // A chave do enum como `value` label: value, // O valor do enum como `label` })); } +export function getEnumKeyByValue>( + enumObj: T, + value: string +): string | undefined { + return Object.keys(enumObj).find((key) => enumObj[key as keyof T] === value); +}