feat(post-job): advance billing and payment flow parity

This commit is contained in:
Tiago Yamamoto 2026-02-14 17:30:37 -03:00
parent cd99fdb66a
commit 66c35ac6c5
2 changed files with 48 additions and 8 deletions

View file

@ -177,12 +177,12 @@ Mapear o que já existe no GoHorseJobs e o que ainda falta para alcançar um flu
### 3) Informações de faturamento
- [x] Capturar dados fiscais (pessoa/empresa, documento, endereço de cobrança).
- [ ] Exibir plano/preço por país e duração (ex.: **US$130/30 dias** para EUA, quando aplicável).
- [ ] Validar consistência entre país da vaga e país de faturamento conforme regra de negócio.
- [x] Exibir plano/preço por país e duração (ex.: **US$130/30 dias** para EUA, quando aplicável).
- [x] Validar consistência entre país da vaga e país de faturamento conforme regra de negócio.
### 4) Pagamento
- [ ] Seleção de método de pagamento disponível no país.
- [ ] Confirmação final com resumo: vaga + faturamento + valor.
- [x] Seleção de método de pagamento disponível no país.
- [x] Confirmação final com resumo: vaga + faturamento + valor.
- [ ] Geração de comprovante/registro de transação.
- [ ] Definição de status da vaga pós-pagamento (`pending_review`, `active`, `failed_payment`).

View file

@ -95,6 +95,7 @@ export default function PostJobPage() {
document: "",
billingCountry: "",
address: "",
paymentMethod: "",
});
const [showPassword, setShowPassword] = useState(false);
@ -136,6 +137,17 @@ export default function PostJobPage() {
const JOB_COUNTRIES = ["BR", "PT", "US", "ES", "UK", "DE", "FR", "JP"];
const PRICING_BY_COUNTRY: Record<string, { amount: string; duration: string }> = {
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" },
};
const cleanCNPJ = (value: string) => value.replace(/\D/g, "");
const isValidCNPJ = (value: string) => {
@ -227,6 +239,18 @@ export default function PostJobPage() {
return false;
}
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 (!billing.paymentMethod) {
toast.error("Selecione um método de pagamento.");
setStep(4);
return false;
}
return true;
};
@ -316,7 +340,7 @@ export default function PostJobPage() {
employmentType: job.employmentType || null,
workingHours: job.workingHours || null,
workMode: job.workMode,
status: "pending",
status: "review",
questions: questions.length > 0 ? questions : null,
languageLevel: job.descriptionLanguage || null,
requirements: {
@ -1042,9 +1066,25 @@ export default function PostJobPage() {
/>
</div>
<div className="bg-muted/50 rounded-lg p-4 text-sm">
<p><strong>Resumo:</strong> {job.title} · {job.country || "País não informado"}</p>
<p><strong>Status após envio:</strong> pending_review</p>
<div>
<Label>Método de pagamento *</Label>
<select
value={billing.paymentMethod}
onChange={(e) => setBilling({ ...billing, paymentMethod: e.target.value })}
className="w-full px-3 py-2 border rounded-lg bg-background"
>
<option value="">Selecione</option>
<option value="card">Cartão de crédito</option>
<option value="pix">PIX</option>
<option value="boleto">Boleto</option>
</select>
</div>
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-1">
<p><strong>Plano:</strong> {PRICING_BY_COUNTRY[billing.billingCountry || job.country]?.amount || "Consulte comercial"} / {PRICING_BY_COUNTRY[billing.billingCountry || job.country]?.duration || "30 dias"}</p>
<p><strong>Resumo:</strong> {job.title} · {job.country || "País não informado"} · {billing.billingCountry || "Faturamento não informado"}</p>
<p><strong>Pagamento:</strong> {billing.paymentMethod ? (billing.paymentMethod === "card" ? "Cartão de crédito" : billing.paymentMethod === "pix" ? "PIX" : "Boleto") : "Não selecionado"}</p>
<p><strong>Status após envio:</strong> review</p>
</div>
<div className="flex gap-3">