This commit initializes the project with a basic Next.js setup, including: - Integration of `tailwindcss` and `tw-animate-css` for styling. - Definition of a comprehensive color palette using CSS variables for both light and dark themes, supporting a theming system. - Implementation of a custom `aurora` keyframe animation. - Configuration of global CSS styles for `border` and `body` elements. - Setup of `next.config.ts` with image optimization, performance enhancements, and security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy). - Update of `package.json` to include new dependencies for UI components (`class-variance-authority`, `clsx`, `lucide-react`, `motion`, `tailwind-merge`, `tailwindcss-animate`) and development tools (`@biomejs/biome`, `@tailwindcss/postcss`, `tw-animate-css`). - Refinement of `app/layout.tsx` to include comprehensive metadata for SEO (title, description, keywords, authors, Open Graph, Twitter cards, robots), preconnect/dns-prefetch links for external resources, favicon, manifest, and mobile app meta tags. - Restructuring of `app/page.tsx` to use dynamic imports for various sections (Hero, About, Skills, Experience, Projects, Contact, WorkModalities) to improve performance, and integration of `Squares` component for background effects. This foundational commit prepares the application for further development by establishing a robust and performant base.
770 lines
29 KiB
TypeScript
770 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 baseMessage = (name: string) => {
|
|
if (hour >= 8 && hour < 12) {
|
|
// Manhã
|
|
return `Bom dia Evandro, meu nome é ${name}. Vi seu portfólio e estou interessado nos seus serviços. Podemos conversar?`;
|
|
} else if (hour >= 13 && hour < 18) {
|
|
// Tarde
|
|
return `Boa tarde Evandro, meu nome é ${name}. Vi seu portfólio e estou interessado nos seus serviços. Podemos conversar?`;
|
|
} else {
|
|
// Noite
|
|
return `Olá Evandro, meu nome é ${name}. Vi seu portfólio e estou interessado nos seus serviços. Podemos conversar quando estiver disponível?`;
|
|
}
|
|
};
|
|
return baseMessage(userName);
|
|
};
|
|
|
|
const generateAdvancedMessage = () => {
|
|
if (customMessage) return customMessage;
|
|
|
|
let message = `Olá Evandro,\n\n`;
|
|
message += `Meu nome é ${userName}`;
|
|
if (userCompany) {
|
|
message += ` e sou de ${userCompany}`;
|
|
}
|
|
if (howFound) {
|
|
message += `. Conheci seu trabalho via ${howFound}`;
|
|
}
|
|
message += `.\n\n`;
|
|
|
|
message += `Estou entrando em contato para discutir um projeto de `;
|
|
if (projectType === "outro" && otherProjectType) {
|
|
message += `${otherProjectType}`;
|
|
} else if (projectType) {
|
|
message += `${projectType}`;
|
|
} else {
|
|
message += `[Tipo de Projeto Não Especificado]`;
|
|
}
|
|
message += `.\n`;
|
|
|
|
if (desiredDeadline) {
|
|
message += `O prazo desejado é ${desiredDeadline}.\n`;
|
|
}
|
|
if (budgetValue) {
|
|
message += `O orçamento disponível é ${budgetValue}.\n`;
|
|
}
|
|
if (technicalComplexity) {
|
|
message += `A complexidade técnica estimada é ${technicalComplexity}.\n`;
|
|
}
|
|
if (hasInternalTeam) {
|
|
message += `Já possuímos uma equipe técnica ${hasInternalTeam}.\n`;
|
|
}
|
|
|
|
message += `\nMeus contatos:\n`;
|
|
if (userEmail) {
|
|
message += `Email: ${userEmail}\n`;
|
|
}
|
|
if (userPhone) {
|
|
message += `Telefone: ${userPhone}\n`;
|
|
}
|
|
|
|
message += `\nAguardamos seu retorno para discutir os próximos passos.`;
|
|
|
|
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>
|
|
);
|
|
}
|