- Improved the contact form's message generation with more professional and detailed templates - Replaced the grid-based background animation with a WebGL-powered particle system using OGL - Added GSAP for advanced animation capabilities - Adjusted layout styling for better centering and spacing across components - Enhanced visual feedback and interactivity of the animated background This change significantly upgrades the user experience and visual appeal of the contact section while maintaining performance through efficient rendering techniques.
798 lines
29 KiB
TypeScript
798 lines
29 KiB
TypeScript
"use client";
|
|
|
|
import { useState, ChangeEvent, FormEvent, useEffect } from "react";
|
|
import { usePortfolioData } from "@/lib/usePortfolioData";
|
|
import { motion } from "motion/react";
|
|
|
|
interface ContactFormProps {
|
|
formType: "simple" | "advanced";
|
|
setFormType: (type: "simple" | "advanced") => void;
|
|
}
|
|
|
|
export function ContactForm({ formType, setFormType }: ContactFormProps) {
|
|
const { data } = usePortfolioData();
|
|
const [contactMethod, setContactMethod] = useState<"whatsapp" | "email">(
|
|
"whatsapp"
|
|
);
|
|
const [userName, setUserName] = useState<string>("");
|
|
const [userEmail, setUserEmail] = useState<string>("");
|
|
const [userPhone, setUserPhone] = useState<string>("");
|
|
const [userCompany, setUserCompany] = useState<string>("");
|
|
const [howFound, setHowFound] = useState<string>("");
|
|
const [projectType, setProjectType] = useState<string>("");
|
|
const [otherProjectType, setOtherProjectType] = useState<string>(""); // Novo estado para "Outro" tipo de projeto
|
|
const [desiredDeadline, setDesiredDeadline] = useState<string>("");
|
|
const [budgetValue, setBudgetValue] = useState<string>(""); // Novo estado para o valor do orçamento
|
|
const [technicalComplexity, setTechnicalComplexity] = useState<string>("");
|
|
const [hasInternalTeam, setHasInternalTeam] = useState<string>("");
|
|
const [customMessage, setCustomMessage] = useState<string>("");
|
|
const [isDecisor, setIsDecisor] = useState<string>("");
|
|
const [projectApproved, setProjectApproved] = useState<string>("");
|
|
const [urgency, setUrgency] = useState<string>("");
|
|
const [preferredModality, setPreferredModality] = useState<string>("");
|
|
const [isExpanded, setIsExpanded] = useState<boolean>(false); // Novo estado para controlar a expansão do formulário avançado
|
|
const [nameError, setNameError] = useState<string | null>(null);
|
|
const [emailError, setEmailError] = useState<string | null>(null);
|
|
const [phoneError, setPhoneError] = useState<string | null>(null);
|
|
const [otherProjectTypeError, setOtherProjectTypeError] = useState<
|
|
string | null
|
|
>(null); // Novo estado de erro
|
|
const [honeypot, setHoneypot] = useState<string>(""); // Estado para o campo honeypot
|
|
const [startTime, setStartTime] = useState<number>(0); // Estado para o tempo de início do formulário
|
|
const [nameFocused, setNameFocused] = useState<boolean>(false);
|
|
const [emailFocused, setEmailFocused] = useState<boolean>(false);
|
|
const [phoneFocused, setPhoneFocused] = useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
setStartTime(Date.now()); // Registra o tempo de carregamento do componente
|
|
}, []);
|
|
|
|
if (!data) {
|
|
return null; // Ou um componente de carregamento/erro
|
|
}
|
|
|
|
const getGreetingMessageWithUserName = () => {
|
|
const hour = new Date().getHours();
|
|
const dayPart =
|
|
hour >= 8 && hour < 12
|
|
? "manhã"
|
|
: hour >= 12 && hour < 18
|
|
? "tarde"
|
|
: "noite";
|
|
|
|
return `Olá Evandro, espero que esta ${dayPart} esteja sendo produtiva.
|
|
|
|
Meu nome é ${userName} e entrei em contato pois vi seu portfólio e identifiquei que suas habilidades como Desenvolvedor Full-Stack podem agregar muito ao projeto que tenho em mente.
|
|
|
|
Gostaria de agendar uma conversa para discutirmos detalhes e entender como podemos trabalhar juntos de forma eficiente e produtiva.
|
|
|
|
Fico no aguardo do seu contato para marcarmos um horário que seja conveniente para ambos.
|
|
|
|
Atenciosamente,
|
|
${userName}`;
|
|
};
|
|
|
|
const generateAdvancedMessage = () => {
|
|
if (customMessage) return customMessage;
|
|
|
|
// Saudação profissional
|
|
let message = `Prezado Evandro,\n\n`;
|
|
|
|
// Informações do contato
|
|
message += `Meu nome é ${userName}`;
|
|
if (userCompany) {
|
|
message += `, represento a empresa ${userCompany}`;
|
|
}
|
|
message += `.\n`;
|
|
|
|
if (howFound) {
|
|
message += `Conheci seu trabalho através de ${howFound}.\n`;
|
|
}
|
|
|
|
message += `\n`;
|
|
|
|
// Detalhes do projeto
|
|
message += `=== DETALHES DO PROJETO ===\n`;
|
|
message += `Tipo de projeto: `;
|
|
if (projectType === "outro" && otherProjectType) {
|
|
message += `${otherProjectType}\n`;
|
|
} else if (projectType) {
|
|
message += `${projectType}\n`;
|
|
} else {
|
|
message += `Não especificado\n`;
|
|
}
|
|
|
|
if (desiredDeadline) {
|
|
message += `Prazo desejado: ${desiredDeadline}\n`;
|
|
}
|
|
|
|
if (budgetValue) {
|
|
message += `Orçamento disponível: ${budgetValue}\n`;
|
|
}
|
|
|
|
if (technicalComplexity) {
|
|
message += `Complexidade técnica estimada: ${technicalComplexity}\n`;
|
|
}
|
|
|
|
if (hasInternalTeam) {
|
|
message += `Equipe técnica interna: ${
|
|
hasInternalTeam === "sim"
|
|
? "Sim"
|
|
: hasInternalTeam === "nao"
|
|
? "Não"
|
|
: "Parcialmente"
|
|
}\n`;
|
|
}
|
|
|
|
message += `\n`;
|
|
|
|
// Contatos
|
|
message += `=== INFORMAÇÕES PARA CONTATO ===\n`;
|
|
if (userEmail) {
|
|
message += `📧 Email: ${userEmail}\n`;
|
|
}
|
|
if (userPhone) {
|
|
message += `📱 Telefone/WhatsApp: ${userPhone}\n`;
|
|
}
|
|
|
|
message += `\n`;
|
|
|
|
// Mensagem final
|
|
message += `Estou entrando em contato pois acredito que seus conhecimentos como Desenvolvedor Full-Stack podem agregar muito ao projeto que tenho em mente.\n\n`;
|
|
message += `Fico no aguardo do seu contato para discutirmos detalhes e definirmos os próximos passos.\n\n`;
|
|
message += `Atenciosamente,\n`;
|
|
message += `${userName}`;
|
|
|
|
return message;
|
|
};
|
|
|
|
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
setUserName(e.target.value);
|
|
if (e.target.value.trim() !== "") setNameError(null);
|
|
};
|
|
|
|
const handleEmailChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
setUserEmail(e.target.value);
|
|
if (e.target.value.trim() !== "") setEmailError(null);
|
|
};
|
|
|
|
const handlePhoneChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
setUserPhone(e.target.value);
|
|
if (e.target.value.trim() !== "") setPhoneError(null);
|
|
};
|
|
|
|
const handleNameFocus = () => setNameFocused(true);
|
|
const handleEmailFocus = () => setEmailFocused(true);
|
|
const handlePhoneFocus = () => setPhoneFocused(true);
|
|
|
|
const handleCompanyChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
setUserCompany(e.target.value);
|
|
};
|
|
|
|
const handleHowFoundChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
setHowFound(e.target.value);
|
|
};
|
|
|
|
const handleProjectTypeChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
setProjectType(e.target.value);
|
|
};
|
|
|
|
const handleDesiredDeadlineChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
setDesiredDeadline(e.target.value);
|
|
};
|
|
|
|
const handleBudgetValueChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
setBudgetValue(e.target.value);
|
|
};
|
|
|
|
const handleTechnicalComplexityChange = (
|
|
e: ChangeEvent<HTMLSelectElement>
|
|
) => {
|
|
setTechnicalComplexity(e.target.value);
|
|
};
|
|
|
|
const handleHasInternalTeamChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
setHasInternalTeam(e.target.value);
|
|
};
|
|
|
|
const handleMessageChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
setCustomMessage(e.target.value);
|
|
};
|
|
|
|
const generateWhatsAppUrl = () => {
|
|
const message = encodeURIComponent(
|
|
formType === "advanced"
|
|
? generateAdvancedMessage()
|
|
: customMessage || getGreetingMessageWithUserName()
|
|
);
|
|
const phone = data.contact.whatsapp.replace(/\D/g, ""); // Remove caracteres não numéricos
|
|
return `https://wa.me/${phone}?text=${message}`;
|
|
};
|
|
|
|
const generateEmailUrl = () => {
|
|
const subject = encodeURIComponent("Proposta de Projeto - Portfólio");
|
|
const body = encodeURIComponent(
|
|
formType === "advanced"
|
|
? generateAdvancedMessage()
|
|
: `Nome: ${userName}\nEmail: ${userEmail}\nTelefone: ${userPhone}\nEmpresa/Posição: ${userCompany}\nComo conheceu: ${howFound}\n\nTipo de Projeto: ${projectType}\nPrazo Desejado: ${desiredDeadline}\nOrçamento Disponível: ${budgetValue}\nComplexidade Técnica: ${technicalComplexity}\nTem equipe interna: ${hasInternalTeam}\n\nMensagem: ${
|
|
customMessage || getGreetingMessageWithUserName()
|
|
}`
|
|
);
|
|
return `mailto:${data.contact.email}?subject=${subject}&body=${body}`;
|
|
};
|
|
|
|
const handleSubmit = (e: FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
let hasError = false;
|
|
if (userName.trim() === "") {
|
|
setNameError("Por favor, insira seu nome.");
|
|
hasError = true;
|
|
} else {
|
|
setNameError(null);
|
|
}
|
|
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(userEmail)) {
|
|
setEmailError("Por favor, insira um email válido.");
|
|
hasError = true;
|
|
} else {
|
|
setEmailError(null);
|
|
}
|
|
|
|
if (userPhone.trim() === "") {
|
|
setPhoneError("Por favor, insira seu telefone.");
|
|
hasError = true;
|
|
} else {
|
|
setPhoneError(null);
|
|
}
|
|
|
|
if (projectType === "outro" && otherProjectType.trim() === "") {
|
|
setOtherProjectTypeError("Por favor, especifique o tipo de projeto.");
|
|
hasError = true;
|
|
} else {
|
|
setOtherProjectTypeError(null);
|
|
}
|
|
|
|
// Validação Honeypot
|
|
if (honeypot) {
|
|
console.log("Honeypot preenchido, provável bot.");
|
|
return; // Ignora a submissão
|
|
}
|
|
|
|
// Validação de tempo de preenchimento (mínimo de 3 segundos)
|
|
const timeElapsed = Date.now() - startTime;
|
|
if (timeElapsed < 3000) {
|
|
console.log("Formulário preenchido muito rápido, provável bot.");
|
|
return; // Ignora a submissão
|
|
}
|
|
|
|
if (hasError) return;
|
|
|
|
// Verificação de interação mínima
|
|
if (!nameFocused || !emailFocused || !phoneFocused) {
|
|
console.log("Campos principais não foram focados, provável bot.");
|
|
// Opcional: exibir uma mensagem de erro para o usuário
|
|
return;
|
|
}
|
|
|
|
if (contactMethod === "whatsapp") {
|
|
window.open(generateWhatsAppUrl(), "_blank");
|
|
} else {
|
|
window.open(generateEmailUrl(), "_blank");
|
|
}
|
|
};
|
|
|
|
const getCtaMessage = () => {
|
|
if (projectType === "consultoria") {
|
|
return "Agendar Consultoria Gratuita";
|
|
}
|
|
if (budgetValue && parseFloat(budgetValue.replace(/\D/g, "")) < 2000) {
|
|
return "Discutir Soluções de Baixo Custo";
|
|
}
|
|
if (projectType && desiredDeadline && budgetValue) {
|
|
return "Receber Proposta Personalizada";
|
|
}
|
|
return "Enviar Formulário";
|
|
};
|
|
|
|
const getEstimatedValue = () => {
|
|
let value = 0;
|
|
// Lógica de estimativa baseada nas respostas
|
|
switch (projectType) {
|
|
case "web-app":
|
|
value += 10000;
|
|
break;
|
|
case "landing-page":
|
|
value += 2000;
|
|
break;
|
|
case "mvp":
|
|
value += 5000;
|
|
break;
|
|
case "iot-app":
|
|
value += 15000;
|
|
break;
|
|
case "integracao":
|
|
value += 7000;
|
|
break;
|
|
case "manutencao":
|
|
value += 1000;
|
|
break;
|
|
case "consultoria":
|
|
value += 500;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (desiredDeadline) {
|
|
case "1-3-weeks":
|
|
value *= 1.2; // Urgência aumenta o valor
|
|
break;
|
|
case "1-2-months":
|
|
value *= 1.1;
|
|
break;
|
|
case "6-plus-months":
|
|
value *= 0.9; // Prazo longo pode reduzir o valor
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (technicalComplexity) {
|
|
case "alta":
|
|
value *= 1.3;
|
|
break;
|
|
case "muito-alta":
|
|
value *= 1.5;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (hasInternalTeam === "nao") {
|
|
value *= 1.1; // Sem equipe interna pode indicar mais trabalho
|
|
}
|
|
|
|
// Ajustar pelo valor do orçamento, se aplicável
|
|
if (budgetValue) {
|
|
const parsedBudget = parseFloat(budgetValue.replace(/\D/g, ""));
|
|
if (!isNaN(parsedBudget)) {
|
|
// Se o valor estimado for maior que o orçamento fornecido, ajusta para o orçamento
|
|
value = Math.min(value, parsedBudget);
|
|
}
|
|
}
|
|
|
|
return value > 0 ? `Estimativa: R$ ${value.toFixed(2)}` : "Estimativa: N/A";
|
|
};
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 30 }}
|
|
whileInView={{ opacity: 1, x: 0 }}
|
|
transition={{ duration: 0.8, delay: 0.4 }}
|
|
viewport={{ once: true }}
|
|
className="space-y-4"
|
|
>
|
|
<h3 className="text-2xl font-semibold mb-6 text-blue-primary">
|
|
Preencha o Formulário
|
|
</h3>
|
|
|
|
<div className="flex justify-center gap-4 mb-8">
|
|
<button
|
|
type="button"
|
|
onClick={() => setFormType("simple")}
|
|
className={`py-2 px-4 rounded-lg font-semibold transition-all duration-300 ${
|
|
formType === "simple"
|
|
? "bg-blue-primary text-white shadow-lg shadow-blue-primary/25 border-2 border-blue-500"
|
|
: "bg-gray-700 text-gray-300 opacity-50 cursor-pointer"
|
|
}`}
|
|
>
|
|
Formulário Simples
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setFormType("advanced")}
|
|
className={`py-2 px-4 rounded-lg font-semibold transition-all duration-300 ${
|
|
formType === "advanced"
|
|
? "bg-blue-primary text-white shadow-lg shadow-blue-primary/25 border-2 border-blue-500"
|
|
: "bg-gray-700 text-gray-300 opacity-50 cursor-pointer"
|
|
}`}
|
|
>
|
|
Formulário Avançado
|
|
</button>
|
|
</div>
|
|
|
|
{formType === "simple" ? (
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Campo Honeypot */}
|
|
<div style={{ display: "none" }}>
|
|
<label htmlFor="honeypot">Não preencha este campo</label>
|
|
<input
|
|
type="text"
|
|
id="honeypot"
|
|
name="honeypot"
|
|
value={honeypot}
|
|
onChange={(e) => setHoneypot(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
{/* Formulário Simples: Apenas Nome */}
|
|
<div>
|
|
<label
|
|
htmlFor="simple-user-name"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Seu Nome:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="simple-user-name"
|
|
className={`w-full p-3 rounded-lg bg-white/5 border ${
|
|
nameError ? "border-red-500" : "border-white/10"
|
|
} text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent`}
|
|
placeholder="Seu nome"
|
|
value={userName}
|
|
onChange={handleNameChange}
|
|
onFocus={handleNameFocus}
|
|
required
|
|
/>
|
|
{nameError && (
|
|
<p className="text-red-400 text-xs mt-1">{nameError}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Botão de Contato para o formulário simples */}
|
|
<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
|
|
<button
|
|
type="submit"
|
|
className="w-full py-4 px-6 rounded-lg font-semibold text-lg transition-all duration-300 bg-green-600 hover:bg-green-700 text-white shadow-lg shadow-green-600/25"
|
|
onClick={() => window.open(generateWhatsAppUrl(), "_blank")}
|
|
>
|
|
Enviar Mensagem via WhatsApp
|
|
</button>
|
|
</motion.div>
|
|
</form>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Campo Honeypot */}
|
|
<div style={{ display: "none" }}>
|
|
<label htmlFor="honeypot">Não preencha este campo</label>
|
|
<input
|
|
type="text"
|
|
id="honeypot"
|
|
name="honeypot"
|
|
value={honeypot}
|
|
onChange={(e) => setHoneypot(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
{/* Dados Básicos */}
|
|
<div>
|
|
<h4 className="text-xl font-semibold mb-4 text-gray-200">
|
|
Dados Básicos
|
|
</h4>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label
|
|
htmlFor="user-name"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Nome Completo:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="user-name"
|
|
className={`w-full p-3 rounded-lg bg-white/5 border ${
|
|
nameError ? "border-red-500" : "border-white/10"
|
|
} text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent`}
|
|
placeholder="Seu nome completo"
|
|
value={userName}
|
|
onChange={handleNameChange}
|
|
onFocus={handleNameFocus}
|
|
required
|
|
/>
|
|
{nameError && (
|
|
<p className="text-red-400 text-xs mt-1">{nameError}</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="user-email"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Email Corporativo:
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="user-email"
|
|
className={`w-full p-3 rounded-lg bg-white/5 border ${
|
|
emailError ? "border-red-500" : "border-white/10"
|
|
} text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent`}
|
|
placeholder="[email protected]"
|
|
value={userEmail}
|
|
onChange={handleEmailChange}
|
|
onFocus={handleEmailFocus}
|
|
required
|
|
/>
|
|
{emailError && (
|
|
<p className="text-red-400 text-xs mt-1">{emailError}</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="user-phone"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
WhatsApp/Telefone:
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="user-phone"
|
|
className={`w-full p-3 rounded-lg bg-white/5 border ${
|
|
phoneError ? "border-red-500" : "border-white/10"
|
|
} text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent`}
|
|
placeholder="(XX) XXXXX-XXXX"
|
|
value={userPhone}
|
|
onChange={handlePhoneChange}
|
|
onFocus={handlePhoneFocus}
|
|
required
|
|
/>
|
|
{phoneError && (
|
|
<p className="text-red-400 text-xs mt-1">{phoneError}</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="user-company"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Empresa/Posição:
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="user-company"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
placeholder="Nome da empresa, sua posição"
|
|
value={userCompany}
|
|
onChange={handleCompanyChange}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="how-found"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Como conheceu meu trabalho?
|
|
</label>
|
|
<select
|
|
id="how-found"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
value={howFound}
|
|
onChange={handleHowFoundChange}
|
|
>
|
|
<option value="">Selecione uma opção</option>
|
|
<option value="linkedin">LinkedIn</option>
|
|
<option value="github">GitHub</option>
|
|
<option value="indicacao">Indicação</option>
|
|
<option value="pesquisa">Pesquisa Online</option>
|
|
<option value="outro">Outro</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tile de Expandir/Recolher */}
|
|
<div
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
className="p-4 rounded-lg bg-white/5 border border-white/10 text-white cursor-pointer hover:bg-white/10 transition-colors duration-300 mb-6 flex justify-between items-center"
|
|
>
|
|
<h4 className="text-xl font-semibold text-gray-200">
|
|
{isExpanded
|
|
? "Detalhes do Projeto (Recolher)"
|
|
: "Detalhes do Projeto (Expandir)"}
|
|
</h4>
|
|
<svg
|
|
className={`w-6 h-6 transition-transform duration-300 ${
|
|
isExpanded ? "rotate-180" : ""
|
|
}`}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth="2"
|
|
d="M19 9l-7 7-7-7"
|
|
></path>
|
|
</svg>
|
|
</div>
|
|
|
|
<div
|
|
className={`overflow-hidden transition-all duration-500 ease-in-out ${
|
|
isExpanded ? "max-h-[1000px] opacity-100" : "max-h-0 opacity-0"
|
|
} space-y-6`}
|
|
>
|
|
{/* Conteúdo do Formulário Avançado */}
|
|
{/* Sobre o Projeto */}
|
|
<div>
|
|
<h4 className="text-xl font-semibold mb-4 text-gray-200">
|
|
Sobre o Projeto
|
|
</h4>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label
|
|
htmlFor="project-type"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Tipo de Projeto:
|
|
</label>
|
|
<select
|
|
id="project-type"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
value={projectType}
|
|
onChange={handleProjectTypeChange}
|
|
>
|
|
<option value="">Selecione o tipo</option>
|
|
<option value="web-app">Aplicação Web Completa</option>
|
|
<option value="landing-page">Landing Page</option>
|
|
<option value="mvp">MVP (Produto Mínimo Viável)</option>
|
|
<option value="iot-app">Aplicação IoT</option>
|
|
<option value="integracao">Integração de Sistemas</option>
|
|
<option value="manutencao">Manutenção/Ajustes</option>
|
|
<option value="consultoria">Consultoria/Mentoria</option>
|
|
<option value="outro">Outro</option>
|
|
</select>
|
|
</div>
|
|
{projectType === "outro" && (
|
|
<div>
|
|
<label
|
|
htmlFor="other-project-type"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Qual outro tipo de projeto?
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="other-project-type"
|
|
className={`w-full p-3 rounded-lg bg-white/5 border ${
|
|
otherProjectTypeError
|
|
? "border-red-500"
|
|
: "border-white/10"
|
|
} text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent`}
|
|
placeholder="Ex: Desenvolvimento de Plugin, Automação de Processos"
|
|
value={otherProjectType}
|
|
onChange={(e) => {
|
|
setOtherProjectType(e.target.value);
|
|
if (e.target.value.trim() !== "")
|
|
setOtherProjectTypeError(null);
|
|
}}
|
|
required
|
|
/>
|
|
{otherProjectTypeError && (
|
|
<p className="text-red-400 text-xs mt-1">
|
|
{otherProjectTypeError}
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
<div>
|
|
<label
|
|
htmlFor="desired-deadline"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Prazo Desejado:
|
|
</label>
|
|
<select
|
|
id="desired-deadline"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
value={desiredDeadline}
|
|
onChange={handleDesiredDeadlineChange}
|
|
>
|
|
<option value="">Selecione o prazo</option>
|
|
<option value="1-3-weeks">1-3 Semanas</option>
|
|
<option value="1-2-months">1-2 Meses</option>
|
|
<option value="2-4-months">2-4 Meses</option>
|
|
<option value="4-6-months">4-6 Meses</option>
|
|
<option value="6-plus-months">Mais de 6 Meses</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="budget-value"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Orçamento Disponível (R$):
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="budget-value"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
placeholder="Ex: 5000, 10000, A Combinar"
|
|
value={budgetValue}
|
|
onChange={handleBudgetValueChange}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="technical-complexity"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Complexidade Técnica Estimada:
|
|
</label>
|
|
<select
|
|
id="technical-complexity"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
value={technicalComplexity}
|
|
onChange={handleTechnicalComplexityChange}
|
|
>
|
|
<option value="">Selecione a complexidade</option>
|
|
<option value="baixa">Baixa</option>
|
|
<option value="media">Média</option>
|
|
<option value="alta">Alta</option>
|
|
<option value="muito-alta">Muito Alta</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label
|
|
htmlFor="has-internal-team"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Já tem equipe técnica interna?
|
|
</label>
|
|
<select
|
|
id="has-internal-team"
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white focus:ring-2 focus:ring-blue-primary focus:border-transparent"
|
|
value={hasInternalTeam}
|
|
onChange={handleHasInternalTeamChange}
|
|
>
|
|
<option value="">Selecione</option>
|
|
<option value="sim">Sim</option>
|
|
<option value="nao">Não</option>
|
|
<option value="parcial">Parcialmente</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Custom Message Input */}
|
|
<div className="mb-6">
|
|
<label
|
|
htmlFor="custom-message"
|
|
className="block text-sm font-medium text-gray-300 mb-2"
|
|
>
|
|
Mensagem Adicional (Opcional):
|
|
</label>
|
|
<textarea
|
|
id="custom-message"
|
|
rows={4}
|
|
className="w-full p-3 rounded-lg bg-white/5 border border-white/10 text-white placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
placeholder="Descreva seu projeto ou suas necessidades aqui..."
|
|
value={customMessage}
|
|
onChange={handleMessageChange}
|
|
></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Contact Button */}
|
|
<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
|
|
<button
|
|
type="submit"
|
|
className="w-full py-4 px-6 rounded-lg font-semibold text-lg transition-all duration-300 bg-blue-primary hover:bg-blue-primary/90 text-white shadow-lg shadow-blue-primary/25 border-2 border-blue-500"
|
|
>
|
|
{getCtaMessage()}
|
|
</button>
|
|
</motion.div>
|
|
{projectType && desiredDeadline && budgetValue && (
|
|
<p className="text-center text-lg font-semibold text-blue-400 mt-4">
|
|
{getEstimatedValue()}
|
|
</p>
|
|
)}
|
|
</form>
|
|
)}
|
|
</motion.div>
|
|
);
|
|
}
|