diff --git a/frontend/src/app/post-job/page.tsx b/frontend/src/app/post-job/page.tsx index 1bda69e..210786c 100644 --- a/frontend/src/app/post-job/page.tsx +++ b/frontend/src/app/post-job/page.tsx @@ -1,1141 +1,892 @@ "use client"; +import Image from "next/image"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; -import { translations, Language } from "./translations"; +import { Search } from "lucide-react"; import { toast } from "sonner"; + import { Navbar } from "@/components/navbar"; import { Footer } from "@/components/footer"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; -import { - Building2, Briefcase, Mail, Lock, Phone, MapPin, - Eye, EyeOff, Globe -} from "lucide-react"; -import { LocationPicker } from "@/components/location-picker"; -import { RichTextEditor } from "@/components/rich-text-editor"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { useTranslation } from "@/lib/i18n"; -import { JobFormBuilder, Question } from "@/components/job-form-builder"; import { Checkbox } from "@/components/ui/checkbox"; +import { RichTextEditor } from "@/components/rich-text-editor"; -// Common Country Codes -const COUNTRY_CODES = [ - { code: "+55", country: "Brasil (BR)" }, - { code: "+1", country: "Estados Unidos (US)" }, - { code: "+351", country: "Portugal (PT)" }, - { code: "+44", country: "Reino Unido (UK)" }, - { code: "+33", country: "França (FR)" }, - { code: "+49", country: "Alemanha (DE)" }, - { code: "+34", country: "Espanha (ES)" }, - { code: "+39", country: "Itália (IT)" }, - { code: "+81", country: "Japão (JP)" }, - { code: "+86", country: "China (CN)" }, - { code: "+91", country: "Índia (IN)" }, - { code: "+52", country: "México (MX)" }, - { code: "+54", country: "Argentina (AR)" }, - { code: "+57", country: "Colômbia (CO)" }, - { code: "+56", country: "Chile (CL)" }, - { code: "+51", country: "Peru (PE)" }, -].sort((a, b) => a.country.localeCompare(b.country)); +type Step = 1 | 2 | 3 | 4; -// Currency symbol helper -const getCurrencySymbol = (code: string): string => { - const symbols: Record = { - 'BRL': 'R$', 'USD': '$', 'EUR': '€', 'JPY': '¥', 'GBP': '£', - 'CNY': '¥', 'AED': 'د.إ', 'CAD': 'C$', 'AUD': 'A$', 'CHF': 'Fr' - }; - return symbols[code] || code; +type SalaryMode = "range" | "fixed"; + +type ApplicationChannel = "email" | "url" | "phone"; + +const cleanDigits = (value: string) => value.replace(/\D/g, ""); + +const isValidCNPJ = (value: string) => { + const cnpj = cleanDigits(value); + if (!cnpj) return true; + if (cnpj.length !== 14) return false; + if (/^(\d)\1+$/.test(cnpj)) return false; + + const calcCheckDigit = (base: string, weights: number[]) => { + const sum = base + .split("") + .reduce((acc, current, index) => acc + Number(current) * weights[index], 0); + const mod = sum % 11; + return mod < 2 ? 0 : 11 - mod; + }; + + const base12 = cnpj.slice(0, 12); + const digit1 = calcCheckDigit(base12, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); + const digit2 = calcCheckDigit(`${base12}${digit1}`, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); + + return cnpj === `${base12}${digit1}${digit2}`; }; -type PostPaymentStatus = "pending_review" | "active" | "failed_payment"; +const isHttpsUrl = (value: string) => /^https:\/\/.+/i.test(value); -const statusLabels: Record = { - pending_review: "pending_review", - active: "active", - failed_payment: "failed_payment", +const isEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); + +const isPhoneWithDDI = (value: string) => /^\+\d{1,3}\s?\d{8,14}$/.test(value.trim()); + +const countries = ["US", "BR", "PT", "ES", "UK", "DE", "FR", "JP"]; + +const pricingByCountry: Record = { + US: { amount: "US$130", duration: "30 dias" }, + BR: { amount: "R$490", duration: "30 dias" }, + PT: { amount: "€99", duration: "30 dias" }, + ES: { amount: "€99", duration: "30 dias" }, + UK: { amount: "£89", duration: "30 dias" }, + DE: { amount: "€119", duration: "30 dias" }, + FR: { amount: "€109", duration: "30 dias" }, + JP: { amount: "¥14,900", duration: "30 dias" }, }; - - export default function PostJobPage() { - const router = useRouter(); - const [step, setStep] = useState<1 | 2 | 3 | 4>(1); - const { locale, setLocale } = useTranslation(); + const router = useRouter(); - const lang = useMemo(() => (locale === "pt-BR" ? "pt" : locale), [locale]); - const t = translations[lang]; + const [step, setStep] = useState(1); + const [loading, setLoading] = useState(false); - // Helper inside to use t - const getSalaryPeriodLabel = (type: string): string => { - const labels: Record = { - 'hourly': t.options.period.hourly, - 'daily': t.options.period.daily, - 'weekly': t.options.period.weekly, - 'monthly': t.options.period.monthly, - 'yearly': t.options.period.yearly - }; - return labels[type] || ''; - }; + const [job, setJob] = useState({ + title: "", + location: "", + country: "", + contractType: "", + workingHours: "", + salaryMode: "range" as SalaryMode, + salaryMin: "", + salaryMax: "", + salaryFixed: "", + currency: "USD", + salaryPeriod: "monthly", + description: "", + descriptionLanguage: "", + applicationChannel: "email" as ApplicationChannel, + applicationEmail: "", + applicationUrl: "", + applicationPhone: "", + resumeRequirement: "optional", + useCepSearch: false, + cep: "", + }); - const [loading, setLoading] = useState(false); - const [transactionReceipt, setTransactionReceipt] = useState<{ id: string; status: PostPaymentStatus; createdAt: string } | null>(null); + const [company, setCompany] = useState({ + name: "", + website: "", + employeeCount: "", + foundedYear: "", + description: "", + hidePublicProfile: false, + document: "", + }); - // Company/User data - const [company, setCompany] = useState({ - name: "", - email: "", - document: "", - password: "", - confirmPassword: "", - ddi: "+55", - phone: "", - website: "", - employeeCount: "", - foundedYear: "", - description: "", - hidePublicProfile: false, - }); + const [billing, setBilling] = useState({ + legalType: "company", + document: "", + billingCountry: "", + address: "", + contactName: "", + contactLastName: "", + contactEmail: "", + contactPhone: "", + contactMobile: "", + acceptTerms: false, + acceptMarketing: false, + }); - const [billing, setBilling] = useState({ - legalType: "company", - document: "", - billingCountry: "", - address: "", - paymentMethod: "", - }); + const [paymentMethod, setPaymentMethod] = useState(""); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const currentPrice = useMemo(() => { + const country = billing.billingCountry || job.country; + return pricingByCountry[country] || { amount: "Consulte comercial", duration: "30 dias" }; + }, [billing.billingCountry, job.country]); - // Job data - const [job, setJob] = useState({ - title: "", - description: "", - location: "", - country: "", - salaryMin: "", - salaryMax: "", - salaryFixed: "", // For fixed salary mode - currency: "BRL", // Default currency - salaryType: "monthly", // Default salary period - employmentType: "", - workMode: "remote", - workingHours: "", - salaryNegotiable: false, // Candidate proposes salary - descriptionLanguage: "", - applicationChannel: "email", - applicationEmail: "", - applicationUrl: "", - applicationPhone: "", - resumeRequirement: "optional", - jobCategory: "", - benefits: [] as string[], - }); + const validateStep1 = () => { + if (!job.title || !job.location || !job.country || !job.description || !job.descriptionLanguage) { + toast.error("Preencha título, localidade, país, idioma e descrição da vaga."); + return false; + } - const [questions, setQuestions] = useState([]); + if (job.title.length > 65) { + toast.error("O título da vaga deve ter no máximo 65 caracteres."); + return false; + } - // Salary mode toggle: 'fixed' | 'range' - const [salaryMode, setSalaryMode] = useState<'fixed' | 'range'>('fixed'); + if (job.applicationChannel === "email" && !isEmail(job.applicationEmail)) { + toast.error("Informe um e-mail de candidatura válido."); + return false; + } - const BENEFIT_OPTIONS = ["Plano de saúde", "Vale refeição", "Vale transporte", "Bônus", "Home office", "Gym pass"]; + if (job.applicationChannel === "url" && !isHttpsUrl(job.applicationUrl)) { + toast.error("Informe uma URL HTTPS válida para candidatura."); + return false; + } - const JOB_CATEGORIES = ["Tecnologia", "Produto", "Dados", "Marketing", "Vendas", "Operações", "Financeiro", "RH"]; + if (job.applicationChannel === "phone" && !isPhoneWithDDI(job.applicationPhone)) { + toast.error("Informe um telefone com DDI válido. Exemplo: +55 11999998888"); + return false; + } - const JOB_COUNTRIES = ["BR", "PT", "US", "ES", "UK", "DE", "FR", "JP"]; + if (job.salaryMode === "fixed" && !job.salaryFixed) { + toast.error("Informe o salário fixo."); + return false; + } - const PRICING_BY_COUNTRY: Record = { - US: { amount: "US$130", duration: "30 dias" }, - BR: { amount: "R$490", duration: "30 dias" }, - PT: { amount: "€99", duration: "30 dias" }, - ES: { amount: "€99", duration: "30 dias" }, - UK: { amount: "£89", duration: "30 dias" }, - DE: { amount: "€119", duration: "30 dias" }, - FR: { amount: "€109", duration: "30 dias" }, - JP: { amount: "¥14,900", duration: "30 dias" }, - }; + if (job.salaryMode === "range" && (!job.salaryMin || !job.salaryMax)) { + toast.error("Informe a faixa salarial (mínimo e máximo)."); + return false; + } - const cleanCNPJ = (value: string) => value.replace(/\D/g, ""); + if (company.document && !isValidCNPJ(company.document)) { + toast.error("CNPJ da empresa inválido."); + return false; + } - const isValidCNPJ = (value: string) => { - const cnpj = cleanCNPJ(value); - if (cnpj.length !== 14) return false; - if (/^(\d)\1+$/.test(cnpj)) return false; + return true; + }; - const calcCheckDigit = (base: string, weights: number[]) => { - const sum = base - .split("") - .reduce((acc, current, index) => acc + Number(current) * weights[index], 0); - const mod = sum % 11; - return mod < 2 ? 0 : 11 - mod; - }; + const validateStep3 = () => { + if (!billing.document || !billing.billingCountry || !billing.address || !billing.contactEmail) { + toast.error("Preencha documento, país, endereço e e-mail de faturamento."); + return false; + } - const base12 = cnpj.slice(0, 12); - const digit1 = calcCheckDigit(base12, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); - const digit2 = calcCheckDigit(`${base12}${digit1}`, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); - return cnpj === `${base12}${digit1}${digit2}`; - }; + if (!isEmail(billing.contactEmail)) { + toast.error("Informe um e-mail de faturamento válido."); + return false; + } - const isHttpsUrl = (value: string) => /^https:\/\/.+/i.test(value); - const isPhoneWithDDI = (value: string) => /^\+\d{1,3}\s?\d{8,14}$/.test(value.trim()); + if (!billing.acceptTerms) { + toast.error("Você precisa aceitar as condições legais para continuar."); + return false; + } - const formatPhoneForDisplay = (value: string) => { - // Simple formatting to just allow numbers and basic separators if needed - // For now, just pass through but maybe restrict chars? - return value.replace(/[^\d\s-]/g, ""); - }; + return true; + }; - const getPostPaymentStatus = (paymentMethod: string): PostPaymentStatus => { - if (!paymentMethod) return "failed_payment"; - if (paymentMethod === "boleto") return "pending_review"; - return "active"; - }; + const handleNext = () => { + if (step === 1) { + if (!validateStep1()) return; + setStep(2); + return; + } - const validateForm = () => { - if (!company.name || !company.email || !company.password) { - toast.error(t.errors.company_required); - setStep(1); // Ensure we are on step 1 for company data errors - return false; - } + if (step === 2) { + setStep(3); + return; + } - if (company.document && !isValidCNPJ(company.document)) { - toast.error("CNPJ inválido."); - setStep(1); - return false; - } + if (step === 3) { + if (!validateStep3()) return; + setStep(4); + } + }; - if (company.password !== company.confirmPassword) { - toast.error(t.errors.password_mismatch); - setStep(1); // Ensure we are on step 1 for password mismatch - return false; - } + const handleSubmit = async () => { + if (!validateStep1() || !validateStep3()) return; - if (company.password.length < 8) { - toast.error(t.errors.password_length); - setStep(1); // Ensure we are on step 1 for password length - return false; - } + if (!paymentMethod) { + toast.error("Selecione um método de pagamento."); + return; + } - if (!job.title || !job.description || !job.location || !job.country || !job.descriptionLanguage) { - toast.error(t.errors.job_required); - setStep(1); // Stay on step 1 for job data errors - return false; - } + setLoading(true); + try { + const apiBase = process.env.NEXT_PUBLIC_API_URL || ""; + const password = "Temp@123456"; + const billingPhone = cleanDigits(billing.contactMobile || billing.contactPhone || job.applicationPhone); - if (job.title.length > 65) { - toast.error("O título da vaga deve ter no máximo 65 caracteres."); - setStep(1); - return false; - } + const registerRes = await fetch(`${apiBase}/api/v1/auth/register/company`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + companyName: company.name, + email: billing.contactEmail, + document: cleanDigits(company.document) || null, + password, + phone: billingPhone ? `+55${billingPhone}` : null, + website: company.website || null, + employeeCount: company.employeeCount || null, + foundedYear: company.foundedYear ? Number(company.foundedYear) : null, + description: company.description || null, + }), + }); - if (job.applicationChannel === "email" && !job.applicationEmail) { - toast.error("Informe um e-mail para candidatura."); - setStep(1); - return false; - } + if (!registerRes.ok) { + const err = await registerRes.json(); + throw new Error(err.message || "Erro ao registrar empresa"); + } - if (job.applicationChannel === "url" && !isHttpsUrl(job.applicationUrl)) { - toast.error("Informe uma URL HTTPS válida para candidatura."); - setStep(1); - return false; - } + const { token } = await registerRes.json(); - if (job.applicationChannel === "phone" && !isPhoneWithDDI(job.applicationPhone)) { - toast.error("Informe um telefone com DDI válido (ex: +55 11999998888)."); - setStep(1); - return false; - } + const salaryMin = job.salaryMode === "fixed" ? Number(job.salaryFixed || 0) : Number(job.salaryMin || 0); + const salaryMax = job.salaryMode === "fixed" ? Number(job.salaryFixed || 0) : Number(job.salaryMax || 0); - if (!billing.document || !billing.billingCountry || !billing.address) { - toast.error("Preencha os dados obrigatórios de faturamento."); - setStep(4); - return false; - } + const jobRes = await fetch(`${apiBase}/api/v1/jobs`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + title: job.title, + description: job.description, + location: `${job.location}, ${job.country}`, + salaryMin: salaryMin || null, + salaryMax: salaryMax || null, + salaryType: job.salaryPeriod, + currency: job.currency, + salaryNegotiable: false, + employmentType: job.contractType || null, + workingHours: job.workingHours || null, + workMode: "onsite", + status: "review", + questions: null, + languageLevel: job.descriptionLanguage || null, + requirements: { + category: null, + resumeRequirement: job.resumeRequirement, + applicationChannel: job.applicationChannel, + applicationEmail: job.applicationChannel === "email" ? job.applicationEmail : null, + applicationUrl: job.applicationChannel === "url" ? job.applicationUrl : null, + applicationPhone: job.applicationChannel === "phone" ? job.applicationPhone : null, + hideCompanyData: company.hidePublicProfile, + }, + benefits: { selected: [] }, + metadata: { + paymentMethod, + billingCountry: billing.billingCountry, + billingAddress: billing.address, + }, + }), + }); - if (job.country && billing.billingCountry && job.country !== billing.billingCountry) { - toast.error("País da vaga e país de faturamento devem ser compatíveis."); - setStep(4); - return false; - } + if (!jobRes.ok) { + const err = await jobRes.json(); + throw new Error(err.message || "Erro ao criar vaga"); + } - if (!billing.paymentMethod) { - toast.error("Selecione um método de pagamento."); - setStep(4); - return false; - } + localStorage.setItem("token", token); + localStorage.setItem("auth_token", token); - return true; - }; + toast.success("Vaga cadastrada com sucesso!"); + router.push("/dashboard/jobs"); + } catch (error) { + const message = error instanceof Error ? error.message : "Erro inesperado ao publicar vaga"; + toast.error(message); + } finally { + setLoading(false); + } + }; - const handleNext = () => { - // Only validate step 1 fields to move to step 2 - if (step === 1) { - if (!company.name || !company.email || !company.password) { - toast.error(t.errors.company_required); - return; - } - if (company.password !== company.confirmPassword) { - toast.error(t.errors.password_mismatch); - return; - } - if (company.password.length < 8) { - toast.error(t.errors.password_length); - return; - } - if (!job.title || !job.description || !job.location || !job.country || !job.descriptionLanguage) { - toast.error("Preencha título, localidade, país, idioma e descrição da vaga."); - return; - } - if (job.title.length > 65) { - toast.error("O título da vaga deve ter no máximo 65 caracteres."); - return; - } - setStep(2); - } else if (step === 2) { - setStep(3); - } else if (step === 3) { - setStep(4); - } - }; + return ( +
+ - const handleSubmit = async () => { - if (!validateForm()) return; +
+
+
+

+ Anuncie vagas de emprego de forma rápida e eficiente +

- setLoading(true); - try { - const apiBase = process.env.NEXT_PUBLIC_API_URL || ""; +
    + {[ + "Uma das maiores comunidades de profissionais do mercado", + "Plataforma com alta visibilidade e acesso diário", + "Grande movimentação de candidaturas todos os dias", + "Novos talentos se conectando constantemente", + ].map((item) => ( +
  • + + {item} +
  • + ))} +
- // Format phone: DDI + Phone (digits only) - const cleanPhone = company.phone.replace(/\D/g, ""); - const finalPhone = cleanPhone ? `${company.ddi}${cleanPhone}` : ""; + Profissionais reunidos +
- // 1. Register Company (creates user + company) - const registerRes = await fetch(`${apiBase}/api/v1/auth/register/company`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - companyName: company.name, - email: company.email, - document: cleanCNPJ(company.document) || null, - password: company.password, - phone: finalPhone, - website: company.website || null, - employeeCount: company.employeeCount || null, - foundedYear: company.foundedYear ? parseInt(company.foundedYear) : null, - description: company.description || null, - }), - }); +
+
+

+ Anuncie a sua vaga de emprego
GRÁTIS! +

+

+ Mais de 50 mil currículos cadastrados +

- if (!registerRes.ok) { - const err = await registerRes.json(); - throw new Error(err.message || "Erro ao registrar empresa"); - } - - const { token } = await registerRes.json(); - - // 2. Create Job with token - const calculatedPostPaymentStatus = getPostPaymentStatus(billing.paymentMethod); - - const jobRes = await fetch(`${apiBase}/api/v1/jobs`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${token}`, - }, - body: JSON.stringify({ - title: job.title, - description: job.description, - location: `${job.location}, ${job.country}`, - // Salary logic: if negotiable, send null values - salaryMin: job.salaryNegotiable ? null : (salaryMode === 'fixed' ? (job.salaryFixed ? parseInt(job.salaryFixed) : null) : (job.salaryMin ? parseInt(job.salaryMin) : null)), - salaryMax: job.salaryNegotiable ? null : (salaryMode === 'fixed' ? (job.salaryFixed ? parseInt(job.salaryFixed) : null) : (job.salaryMax ? parseInt(job.salaryMax) : null)), - salaryType: job.salaryNegotiable ? null : job.salaryType, - currency: job.salaryNegotiable ? null : job.currency, - salaryNegotiable: job.salaryNegotiable, - employmentType: job.employmentType || null, - workingHours: job.workingHours || null, - workMode: job.workMode, - status: "review", - questions: questions.length > 0 ? questions : null, - languageLevel: job.descriptionLanguage || null, - requirements: { - category: job.jobCategory || null, - resumeRequirement: job.resumeRequirement, - applicationChannel: job.applicationChannel, - applicationEmail: job.applicationEmail || null, - applicationUrl: job.applicationUrl || null, - applicationPhone: job.applicationPhone || null, - hideCompanyData: company.hidePublicProfile, - }, - benefits: { - selected: job.benefits, - }, - }), - }); - - if (!jobRes.ok) { - const err = await jobRes.json(); - throw new Error(err.message || "Erro ao criar vaga"); - } - - // Save token for future use - localStorage.setItem("token", token); - localStorage.setItem("auth_token", token); - - const receipt = { - id: `TX-${Date.now().toString(36).toUpperCase()}`, - status: calculatedPostPaymentStatus, - createdAt: new Date().toISOString(), - }; - localStorage.setItem("last_job_posting_receipt", JSON.stringify(receipt)); - setTransactionReceipt(receipt); - - toast.success(t.success.job_created); - router.push("/dashboard/jobs"); - - } catch (err: any) { - toast.error(err.message || t.errors.submit_failed); - } finally { - setLoading(false); - } - }; - - return ( -
- - -
-
-
-
- -
-
-

{t.title}

-

- {t.subtitle} -

-
+
+ {[ + "Detalhes da vaga", + "Pré-visualização", + "Informações de faturamento", + "Pagamento", + ].map((label, index) => { + const number = (index + 1) as Step; + return ( +
= number ? "bg-[#ef9225] text-white border-[#ef9225]" : "bg-white text-[#1d2c44]"}`}> + {index + 1}. {label}
+ ); + })} +
- {/* Progress Steps */} -
- {[1, 2, 3, 4].map((s) => ( -
= s ? "text-primary" : "text-muted-foreground"}`} - > -
= s ? "bg-primary text-white" : "bg-muted"}`}> - {s} -
- - {s === 1 && "Dados"} - {s === 2 && "Formulário"} - {s === 3 && "Pré-visualização"} - {s === 4 && "Faturamento"} - -
+ {step === 1 && ( +
+
+ + setJob({ ...job, title: e.target.value })} + /> +

+ {job.title.length}/65 caracteres. Evite abreviações e excesso de pontuação. +

+
+ +
+
+ + setJob({ ...job, location: e.target.value })} + /> +

Apenas uma localização por anúncio.

+
+ +
+ + {job.useCepSearch && ( +
+ + setJob({ ...job, cep: e.target.value })} + /> +
+ )} + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + {job.salaryMode === "fixed" ? ( +
+ + setJob({ ...job, salaryFixed: e.target.value })} + /> +
+ ) : ( +
+
+ + setJob({ ...job, salaryMin: e.target.value })} + /> +
+
+ + setJob({ ...job, salaryMax: e.target.value })} + /> +
+
+ )} +
+
+ +
+ + setJob({ ...job, description: value })} + placeholder="Organize em parágrafos e listas com habilidades e qualificações..." + minHeight="160px" + /> +
+ +
+
+

Sobre a empresa (opcional)

+
- - - - {step === 1 && t.cardTitle.step1} - {step === 2 && "Configure o Formulário"} - {step === 3 && "Pré-visualização"} - {step === 4 && t.cardTitle.step2} - - - {step === 1 && t.cardDesc.step1} - {step === 2 && "Defina as perguntas que os candidatos deverão responder."} - {step === 3 && "Confira como o anúncio será exibido antes de prosseguir."} - {step === 4 && "Informe os dados fiscais para finalizar a publicação."} - - - - {/* Step 1: Company */} - {step === 1 && ( -
-
- -
- - setCompany({ ...company, name: e.target.value })} - placeholder={t.company.namePlaceholder} - className="pl-10" - /> -
-
-
- -
- - setCompany({ ...company, email: e.target.value })} - placeholder="contato@empresa.com" - className="pl-10" - /> -
-
+
+
+ + setCompany({ ...company, name: e.target.value })} + /> +
+
+ + setCompany({ ...company, website: e.target.value })} + /> +
+
-
- - setCompany({ ...company, document: e.target.value })} - placeholder="00.000.000/0000-00" - /> -
+
+
+ + +
+
+ + setCompany({ ...company, foundedYear: e.target.value })} + /> +
+
+ + setCompany({ ...company, document: e.target.value })} + /> +
+
- {/* Password Field */} -
- -
- - setCompany({ ...company, password: e.target.value })} - placeholder="••••••••" - className="pl-10 pr-10" // Extra padding for eye icon - /> - -
-
+
+ + setCompany({ ...company, description: value })} + placeholder="Convide os candidatos a conhecer a organização..." + minHeight="120px" + /> +
+
- {/* Confirm Password Field */} -
- -
- - setCompany({ ...company, confirmPassword: e.target.value })} - placeholder="••••••••" - className="pl-10 pr-10" - /> - -
-
+
+
+ + +
+
+ + +
+
- {/* Phone Field with DDI */} -
- -
-
- -
-
- - setCompany({ ...company, phone: e.target.value })} - placeholder="11 99999-9999" - className="pl-10 border-0 shadow-none focus-visible:ring-0 rounded-l-none h-10" - /> -
-
-

- {t.company.phoneHelp} -

-
+ {job.applicationChannel === "email" && ( +
+ + setJob({ ...job, applicationEmail: e.target.value })} + /> +
+ )} - {/* Website */} -
- -
- - setCompany({ ...company, website: e.target.value })} - placeholder="https://www.suaempresa.com.br" - className="pl-10" - /> -
-
+ {job.applicationChannel === "url" && ( +
+ + setJob({ ...job, applicationUrl: e.target.value })} + /> +
+ )} - {/* Employee Count & Founded Year */} -
-
- - -
-
- - setCompany({ ...company, foundedYear: e.target.value })} - placeholder="ex: 2010" - min="1800" - max={new Date().getFullYear()} - /> -
-
+ {job.applicationChannel === "phone" && ( +
+ + setJob({ ...job, applicationPhone: e.target.value })} + /> +
+ )} - {/* About Company */} -
- - setCompany({ ...company, description: val })} - placeholder="Descreva sua empresa, cultura, missão e valores..." - minHeight="120px" - /> -
- -
- setCompany({ ...company, hidePublicProfile: checked === true })} - className="mt-1" - /> -
- -

Quando ativo, nome, site e descrição da empresa não aparecem na vaga pública.

-
-
- - {/* Separator */} -
-

- {t.job.title} -

-
- -
- -
- - setJob({ ...job, title: e.target.value })} - placeholder={t.job.jobTitlePlaceholder} - className="pl-10" - maxLength={65} - /> -
-

{job.title.length}/65 caracteres

-
-
- - setJob({ ...job, description: val })} - placeholder="Descreva as responsabilidades, requisitos e benefícios..." - minHeight="150px" - /> -
-
-
- {/* Includes Label and Layout internally. */} - setJob({ ...job, location: val })} - /> -
-
-
-
- - -
-
- - -
-
- {/* Salary Section */} -
-
- - -
- - {!job.salaryNegotiable && ( - <> - {/* Currency and Period Row */} -
-
- - -
-
- - -
-
- - {/* Salary Value(s) */} - {salaryMode === 'fixed' ? ( - setJob({ ...job, salaryFixed: e.target.value })} - placeholder="Valor" - /> - ) : ( -
- setJob({ ...job, salaryMin: e.target.value })} - placeholder="Mín" - /> - setJob({ ...job, salaryMax: e.target.value })} - placeholder="Máx" - /> -
- )} - - - )} -
-
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - {job.applicationChannel === "email" && ( -
- - setJob({ ...job, applicationEmail: e.target.value })} - placeholder="jobs@empresa.com" - /> -
- )} - - {job.applicationChannel === "url" && ( -
- - setJob({ ...job, applicationUrl: e.target.value })} - placeholder="https://empresa.com/carreiras" - /> -
- )} - - {job.applicationChannel === "phone" && ( -
- - setJob({ ...job, applicationPhone: e.target.value })} - placeholder="+55 11999998888" - /> -
- )} - -
-
- - -
-
- -
- {BENEFIT_OPTIONS.map((benefit) => { - const checked = job.benefits.includes(benefit); - return ( - - ); - })} -
-
-
- - -
- )} - - {/* Step 2: Questions */} - {step === 2 && ( -
-
-

Crie formulários inteligentes: Faça perguntas específicas para filtrar os melhores candidatos.

-
- - - -
- - -
-
- )} - - {/* Step 3: Preview */} - {step === 3 && ( -
-
-

- {t.common.company} -

-

{t.common.name}: {company.name}

-

{t.common.email}: {company.email}

- {company.phone &&

{t.common.phone}: {company.ddi} {company.phone}

} -
-
-

- {t.common.job} -

-

{t.common.title}: {job.title}

-

{t.common.location}: {job.location || "Não informado"}

-

País: {job.country || "Não informado"}

-

Idioma: {job.descriptionLanguage || "Não informado"}

-

{t.common.salary}: { - job.salaryNegotiable - ? t.job.salaryNegotiable - : salaryMode === 'fixed' - ? (job.salaryFixed ? `${getCurrencySymbol(job.currency)} ${job.salaryFixed} ${getSalaryPeriodLabel(job.salaryType)}` : t.job.salaryNegotiable) - : (job.salaryMin && job.salaryMax ? `${getCurrencySymbol(job.currency)} ${job.salaryMin} - ${job.salaryMax} ${getSalaryPeriodLabel(job.salaryType)}` : t.job.salaryNegotiable) - }

-

Perguntas Personalizadas: {questions.length}

-

Canal de candidatura: {job.applicationChannel}

-

Currículo: {job.resumeRequirement}

-

Área: {job.jobCategory || "Não informado"}

-

Benefícios: {job.benefits.length > 0 ? job.benefits.join(", ") : "Não informado"}

-

Visibilidade da empresa: {company.hidePublicProfile ? "Oculta na vaga pública" : "Visível"}

- {company.hidePublicProfile && ( -

Campos ocultos: nome da empresa, site e descrição.

- )} -

{t.common.type}: { - (job.employmentType ? (t.options.contract[job.employmentType as keyof typeof t.options.contract] || job.employmentType) : t.options.any) - } / { - job.workingHours === 'full-time' ? t.options.hours.fullTime : job.workingHours === 'part-time' ? t.options.hours.partTime : t.options.any - } / { - job.workMode === 'remote' ? t.options.mode.remote : job.workMode === 'hybrid' ? t.options.mode.hybrid : t.options.mode.onsite - }

-
-
- - -
-
- )} - - {/* Step 4: Billing + Publish */} - {step === 4 && ( -
-
-
- - -
-
- - setBilling({ ...billing, document: e.target.value })} - placeholder={billing.legalType === "company" ? "CNPJ" : "CPF/NIF"} - /> -
-
-
- - -
-
- -