feat(contact): enhance form submission and improve user experience

- Updated the Contact component to include a WhatsApp integration for project requests, streamlining communication.
- Improved form handling with clearer placeholder text and refined emoji-based message templates for better user engagement.
- Adjusted submission delay for a more responsive user experience and enhanced button labeling for clarity.
This commit is contained in:
Evandro Fonseca Junior committed 2026-01-12 16:44:37 -03:00
1 parent b58c5dabbc
commit dfae6bae2f
1 file changed
+49 -47
+49 -47
View File
@@ -1,12 +1,14 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useState } from "react";
import { motion } from "framer-motion"; // Ajustado para o padrão mais estável import { motion } from "framer-motion";
import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react"; import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react";
// Importamos o componente original.
// Nota: Se o erro de tipo persistir, corrigiremos a interface aqui no wrapper.
import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button"; import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button";
// --- CONFIGURAÇÃO DE ENGENHARIA ---
// Substitua pelo seu número real: CódigoPaís + DDD + Número (sem espaços ou símbolos)
const WHATSAPP_NUMBER = "5547992742365";
interface FormData { interface FormData {
name: string; name: string;
email: string; email: string;
@@ -15,15 +17,12 @@ interface FormData {
} }
/** /**
* Wrapper Component para corrigir o erro de tipagem TS2322 * Wrapper Component: Resolve o erro de tipagem TS2322 (IntrinsicAttributes)
* Aqui garantimos que o componente aceite as cores e tamanhos * Estendemos a interface para aceitar propriedades de estilo customizadas.
*/ */
interface ExtendedShimmerProps { interface ExtendedShimmerProps extends React.ComponentProps<typeof MagicShimmerButton> {
children: React.ReactNode;
className?: string;
shimmerColor?: string; shimmerColor?: string;
shimmerSize?: string; shimmerSize?: string;
onClick?: () => void;
type?: "button" | "submit" | "reset"; type?: "button" | "submit" | "reset";
disabled?: boolean; disabled?: boolean;
} }
@@ -39,9 +38,7 @@ const CustomShimmerButton = ({
<MagicShimmerButton <MagicShimmerButton
{...props} {...props}
className={className} className={className}
// Passamos como variáveis CSS se o componente original suportar, // @ts-ignore - Ignora restrição de tipos da lib original se necessário
// ou apenas omitimos se o componente da MagicUI for estático.
// @ts-ignore - Ignoramos se a lib original tiver tipos restritos
shimmerColor={shimmerColor} shimmerColor={shimmerColor}
shimmerSize={shimmerSize} shimmerSize={shimmerSize}
> >
@@ -63,10 +60,36 @@ export default function Contact() {
e.preventDefault(); e.preventDefault();
setIsSubmitting(true); setIsSubmitting(true);
// Simulação de latência de rede (Princípio de UX Feedback) await new Promise((resolve) => setTimeout(resolve, 800));
await new Promise((resolve) => setTimeout(resolve, 1500));
console.log("Payload de Engenharia enviado:", formData); // Definimos os emojis via Unicode Escape para máxima compatibilidade
const icons = {
rocket: "\u{1F680}",
person: "\u{1F464}",
email: "\u{1F4E7}",
tools: "\u{1F6E0}",
chat: "\u{1F4AC}"
};
const messageTemplate = `${icons.rocket} *NOVA REQUISICAO DE PROJETO*
*Origem:* Portfolio Profissional
*Prioridade:* Alta (Lead Qualificado)
---
${icons.person} *CLIENTE:* ${formData.name}
${icons.email} *EMAIL:* ${formData.email}
${icons.tools} *AREA:* ${formData.subject.toUpperCase()}
${icons.chat} *DESCRICAO:*
"${formData.message}"
---
_Mensagem gerada via Evandro.dev | Garuva, SC_`;
const encodedMessage = encodeURIComponent(messageTemplate);
const whatsappUrl = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodedMessage}`;
window.open(whatsappUrl, "_blank", "noopener,noreferrer");
setIsSubmitting(false); setIsSubmitting(false);
}; };
@@ -79,7 +102,6 @@ export default function Contact() {
<section id="contact" className="w-full max-w-2xl mx-auto p-6"> <section id="contact" className="w-full max-w-2xl mx-auto p-6">
<form onSubmit={handleSubmit} className="space-y-6"> <form onSubmit={handleSubmit} className="space-y-6">
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
{/* Nome */}
<div className="space-y-2"> <div className="space-y-2">
<label className={labelStyles}> <label className={labelStyles}>
<User className="w-3 h-3" /> Nome_Completo <User className="w-3 h-3" /> Nome_Completo
@@ -87,16 +109,13 @@ export default function Contact() {
<input <input
required required
type="text" type="text"
placeholder="Digite seu nome" placeholder="Ex: Alan Turing"
className={inputStyles} className={inputStyles}
value={formData.name} value={formData.name}
onChange={(e) => onChange={(e) => setFormData({ ...formData, name: e.target.value })}
setFormData({ ...formData, name: e.target.value })
}
/> />
</div> </div>
{/* Email */}
<div className="space-y-2"> <div className="space-y-2">
<label className={labelStyles}> <label className={labelStyles}>
<Mail className="w-3 h-3" /> Endpoint_Email <Mail className="w-3 h-3" /> Endpoint_Email
@@ -104,17 +123,14 @@ export default function Contact() {
<input <input
required required
type="email" type="email"
placeholder="seuemail@gmail.com" placeholder="contato@empresa.com"
className={inputStyles} className={inputStyles}
value={formData.email} value={formData.email}
onChange={(e) => onChange={(e) => setFormData({ ...formData, email: e.target.value })}
setFormData({ ...formData, email: e.target.value })
}
/> />
</div> </div>
</div> </div>
{/* Tipo de Requisição */}
<div className="space-y-2"> <div className="space-y-2">
<label className={labelStyles}> <label className={labelStyles}>
<Sparkles className="w-3 h-3" /> Tipo_de_Requisicao <Sparkles className="w-3 h-3" /> Tipo_de_Requisicao
@@ -122,18 +138,15 @@ export default function Contact() {
<select <select
className={inputStyles} className={inputStyles}
value={formData.subject} value={formData.subject}
onChange={(e) => onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
setFormData({ ...formData, subject: e.target.value })
}
> >
<option value="full-stack">Desenvolvimento Full-Stack</option> <option value="full-stack">Desenvolvimento Full-Stack</option>
<option value="cloud">Cloud & Infrastructure</option> <option value="cloud">Cloud & Infrastructure</option>
<option value="iot">Internet of Things (IoT)</option> <option value="iot">Internet of Things (IoT)</option>
<option value="consultancy">Consultoria Técnica</option> <option value="consultancy">Consultoria Técnica Sênior</option>
</select> </select>
</div> </div>
{/* Mensagem */}
<div className="space-y-2"> <div className="space-y-2">
<label className={labelStyles}> <label className={labelStyles}>
<MessageSquare className="w-3 h-3" /> Descricao_do_Projeto <MessageSquare className="w-3 h-3" /> Descricao_do_Projeto
@@ -141,21 +154,14 @@ export default function Contact() {
<textarea <textarea
required required
rows={5} rows={5}
placeholder="Descreva os requisitos técnicos e objetivos do seu sistema..." placeholder="Descreva os requisitos técnicos e objetivos do sistema..."
className={`${inputStyles} resize-none`} className={`${inputStyles} resize-none`}
value={formData.message} value={formData.message}
onChange={(e) => onChange={(e) => setFormData({ ...formData, message: e.target.value })}
setFormData({ ...formData, message: e.target.value })
}
/> />
</div> </div>
{/* Botão de Submissão */} <motion.div whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} className="pt-4">
<motion.div
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
className="pt-4"
>
<CustomShimmerButton <CustomShimmerButton
type="submit" type="submit"
className="w-full h-14 rounded-xl shadow-2xl" className="w-full h-14 rounded-xl shadow-2xl"
@@ -163,17 +169,13 @@ export default function Contact() {
shimmerSize="0.2em" shimmerSize="0.2em"
disabled={isSubmitting} disabled={isSubmitting}
> >
{/* CONTAINER INTERNO (FAIL-SAFE):
Forçamos um novo contexto flex-row aqui.
O 'w-full' e 'h-full' garantem que ele ocupe todo o botão.
*/}
<div className="flex flex-row items-center justify-center gap-3 w-full h-full"> <div className="flex flex-row items-center justify-center gap-3 w-full h-full">
{isSubmitting ? ( {isSubmitting ? (
<div className="w-6 h-6 border-2 border-white/30 border-t-white rounded-full animate-spin" /> <div className="w-6 h-6 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : ( ) : (
<> <>
<span className="text-white font-bold tracking-widest uppercase text-sm leading-none"> <span className="text-white font-bold tracking-widest uppercase text-sm leading-none">
Enviar request Executar Request
</span> </span>
<Send className="w-4 h-4 text-white" /> <Send className="w-4 h-4 text-white" />
</> </>
@@ -184,7 +186,7 @@ export default function Contact() {
<footer className="pt-4"> <footer className="pt-4">
<p className="text-[10px] text-center text-slate-600 font-mono uppercase tracking-tighter"> <p className="text-[10px] text-center text-slate-600 font-mono uppercase tracking-tighter">
Qualidade ponta-ponta garantida | Garuva, SC Engenharia de Software de Alta Performance | Garuva, SC
</p> </p>
</footer> </footer>
</form> </form>