Merge pull request #57 from rede5/codex/implement-additional-12-percent

feat(post-job): add billing/payment (country pricing, validation) and set job status to review
This commit is contained in:
Tiago Yamamoto 2026-02-14 17:30:54 -03:00 committed by GitHub
commit 7a628f17dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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 ### 3) Informações de faturamento
- [x] Capturar dados fiscais (pessoa/empresa, documento, endereço de cobrança). - [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). - [x] 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] Validar consistência entre país da vaga e país de faturamento conforme regra de negócio.
### 4) Pagamento ### 4) Pagamento
- [ ] Seleção de método de pagamento disponível no país. - [x] Seleção de método de pagamento disponível no país.
- [ ] Confirmação final com resumo: vaga + faturamento + valor. - [x] Confirmação final com resumo: vaga + faturamento + valor.
- [ ] Geração de comprovante/registro de transação. - [ ] Geração de comprovante/registro de transação.
- [ ] Definição de status da vaga pós-pagamento (`pending_review`, `active`, `failed_payment`). - [ ] 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: "", document: "",
billingCountry: "", billingCountry: "",
address: "", address: "",
paymentMethod: "",
}); });
const [showPassword, setShowPassword] = useState(false); 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 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 cleanCNPJ = (value: string) => value.replace(/\D/g, "");
const isValidCNPJ = (value: string) => { const isValidCNPJ = (value: string) => {
@ -227,6 +239,18 @@ export default function PostJobPage() {
return false; 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; return true;
}; };
@ -316,7 +340,7 @@ export default function PostJobPage() {
employmentType: job.employmentType || null, employmentType: job.employmentType || null,
workingHours: job.workingHours || null, workingHours: job.workingHours || null,
workMode: job.workMode, workMode: job.workMode,
status: "pending", status: "review",
questions: questions.length > 0 ? questions : null, questions: questions.length > 0 ? questions : null,
languageLevel: job.descriptionLanguage || null, languageLevel: job.descriptionLanguage || null,
requirements: { requirements: {
@ -1042,9 +1066,25 @@ export default function PostJobPage() {
/> />
</div> </div>
<div className="bg-muted/50 rounded-lg p-4 text-sm"> <div>
<p><strong>Resumo:</strong> {job.title} · {job.country || "País não informado"}</p> <Label>Método de pagamento *</Label>
<p><strong>Status após envio:</strong> pending_review</p> <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>
<div className="flex gap-3"> <div className="flex gap-3">