diff --git a/docs/TASKS.md b/docs/TASKS.md
index 6b97bc9..e52e171 100644
--- a/docs/TASKS.md
+++ b/docs/TASKS.md
@@ -24,7 +24,7 @@ Lista detalhada de tarefas para evitar retrabalho.
- Frontend: profileApi.uploadAvatar
- [x] **Public Job Posting**
- - Frontend: `/post-job` page
+ - Frontend: `/jobs/new` page
- 3-step wizard (Company + Job + Confirm)
- [x] **Documentation**
diff --git a/docs/publicar-vaga-routes.md b/docs/publicar-vaga-routes.md
index 67adb68..f73f32f 100644
--- a/docs/publicar-vaga-routes.md
+++ b/docs/publicar-vaga-routes.md
@@ -2,11 +2,12 @@
As principais rotas usadas para publicar vagas no projeto são:
-1. `/publicar-vaga` — landing/formulário público de anúncio de vaga.
-2. `/post-job` — fluxo principal multi-etapas para publicação com dados da empresa e da vaga.
-3. `/register/job` — formulário público alternativo para criação de vaga.
+1. `/jobs/new` — fluxo público principal (multi-etapas) para publicação com dados da empresa e da vaga.
+2. `/publicar-vaga` — rota legada redirecionando para `/jobs/new`.
+3. `/dashboard/jobs/new` — fluxo interno no dashboard para cadastro rápido de vaga (design system do dashboard).
+4. `/register/job` — formulário público alternativo para criação de vaga.
## Pontos de entrada no sistema
-- Link para `/publicar-vaga` na página **About** e no **Footer**.
-- Link para `/post-job` na página **Contact**.
+- Link para `/jobs/new` na página **About**, **Footer** e **Contact**.
+- Link para `/dashboard/jobs/new` nas páginas internas do dashboard (Jobs, My Jobs e sidebar da empresa).
diff --git a/frontend/FRONTEND.md b/frontend/FRONTEND.md
index 4af7b1f..2af81b4 100644
--- a/frontend/FRONTEND.md
+++ b/frontend/FRONTEND.md
@@ -72,7 +72,7 @@ src/
│ ├── page.tsx # Landing page
│ ├── login/ # Autenticação
│ ├── register/ # Registro (candidate, company)
-│ ├── post-job/ # **NEW** Wizard público
+│ ├── jobs/new/ # **NEW** Wizard público
│ ├── jobs/ # Listagem e detalhes
│ │ ├── page.tsx # Listagem
│ │ ├── [id]/page.tsx # Detalhes
@@ -143,7 +143,7 @@ src/
| `/login` | Login |
| `/register/candidate` | Registro candidato |
| `/register/company` | Registro empresa |
-| `/post-job` | **NEW** Wizard público (registro + vaga) |
+| `/jobs/new` | **NEW** Wizard público (registro + vaga) |
| `/about` | Sobre |
| `/contact` | Contato |
| `/faq` | FAQ |
diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx
index f43761a..7b0f157 100644
--- a/frontend/src/app/about/page.tsx
+++ b/frontend/src/app/about/page.tsx
@@ -243,7 +243,7 @@ export default function AboutPage() {
{t('about.cta.findJobs')}
{t('about.cta.postJob')}
diff --git a/frontend/src/app/contact/page.tsx b/frontend/src/app/contact/page.tsx
index 648388b..2925f20 100644
--- a/frontend/src/app/contact/page.tsx
+++ b/frontend/src/app/contact/page.tsx
@@ -152,7 +152,7 @@ export default function ContactPage() {
-
+
diff --git a/frontend/src/app/dashboard/jobs/new/page.tsx b/frontend/src/app/dashboard/jobs/new/page.tsx
index 642716a..1b15f29 100644
--- a/frontend/src/app/dashboard/jobs/new/page.tsx
+++ b/frontend/src/app/dashboard/jobs/new/page.tsx
@@ -1,5 +1,293 @@
-import { redirect } from "next/navigation"
+"use client"
-export default function DashboardNewJobRedirectPage() {
- redirect("/publicar-vaga")
+import { useEffect, useState } from "react"
+import { Loader2, PlusCircle } from "lucide-react"
+import { toast } from "sonner"
+
+import { Button } from "@/components/ui/button"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Textarea } from "@/components/ui/textarea"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { adminCompaniesApi, jobsApi, type AdminCompany, type CreateJobPayload } from "@/lib/api"
+
+export default function DashboardNewJobPage() {
+ const [loading, setLoading] = useState(false)
+ const [loadingCompanies, setLoadingCompanies] = useState(true)
+ const [companies, setCompanies] = useState([])
+
+ const [formData, setFormData] = useState({
+ title: "",
+ description: "",
+ location: "",
+ salaryMin: "",
+ salaryMax: "",
+ salaryType: "monthly",
+ currency: "BRL",
+ employmentType: "",
+ workingHours: "",
+ companyId: "",
+ })
+
+ useEffect(() => {
+ const loadCompanies = async () => {
+ try {
+ setLoadingCompanies(true)
+ const data = await adminCompaniesApi.list(undefined, 1, 100)
+ setCompanies(data.data ?? [])
+ } catch (error) {
+ console.error("Falha ao carregar empresas:", error)
+ toast.error("Falha ao carregar empresas")
+ } finally {
+ setLoadingCompanies(false)
+ }
+ }
+
+ loadCompanies()
+ }, [])
+
+ const canSubmit =
+ formData.title.trim().length >= 5 &&
+ formData.description.trim().length >= 20 &&
+ formData.companyId !== ""
+
+ const handleInputChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }))
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+
+ if (!canSubmit) {
+ toast.error("Preencha os campos obrigatórios")
+ return
+ }
+
+ setLoading(true)
+ try {
+ const payload: CreateJobPayload = {
+ companyId: formData.companyId,
+ title: formData.title,
+ description: formData.description,
+ location: formData.location || undefined,
+ employmentType:
+ (formData.employmentType as CreateJobPayload["employmentType"]) || undefined,
+ salaryMin: formData.salaryMin ? parseFloat(formData.salaryMin) : undefined,
+ salaryMax: formData.salaryMax ? parseFloat(formData.salaryMax) : undefined,
+ salaryType: (formData.salaryType as CreateJobPayload["salaryType"]) || undefined,
+ currency: (formData.currency as CreateJobPayload["currency"]) || undefined,
+ workingHours: formData.workingHours || undefined,
+ status: "draft",
+ }
+
+ await jobsApi.create(payload)
+ toast.success("Vaga cadastrada no dashboard com sucesso")
+ setFormData({
+ title: "",
+ description: "",
+ location: "",
+ salaryMin: "",
+ salaryMax: "",
+ salaryType: "monthly",
+ currency: "BRL",
+ employmentType: "",
+ workingHours: "",
+ companyId: "",
+ })
+ } catch (error: any) {
+ console.error("Falha ao cadastrar vaga:", error)
+ toast.error(error.message || "Falha ao cadastrar vaga")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
+
Nova vaga
+
+ Cadastre vagas usando o padrão visual do dashboard.
+
+
+
+
+
+
+
+ Dados da vaga
+
+
+ Informe os detalhes obrigatórios para criar a vaga em rascunho.
+
+
+
+
+
+
+
+ )
}
diff --git a/frontend/src/app/dashboard/jobs/page.tsx b/frontend/src/app/dashboard/jobs/page.tsx
index 10449d1..74fffd1 100644
--- a/frontend/src/app/dashboard/jobs/page.tsx
+++ b/frontend/src/app/dashboard/jobs/page.tsx
@@ -191,7 +191,7 @@ export default function AdminJobsPage() {
{t('admin.jobs.title')}
{t('admin.jobs.subtitle')}
-
+
{t('admin.jobs.newJob')}
diff --git a/frontend/src/app/dashboard/my-jobs/page.tsx b/frontend/src/app/dashboard/my-jobs/page.tsx
index 9f245fa..cfb9651 100644
--- a/frontend/src/app/dashboard/my-jobs/page.tsx
+++ b/frontend/src/app/dashboard/my-jobs/page.tsx
@@ -150,7 +150,7 @@ export default function MyJobsPage() {
Manage your job postings for {user?.companyId ? "your company" : "..."}
-
+
Post New Job
@@ -238,7 +238,7 @@ export default function MyJobsPage() {
No jobs found
Start by posting your first job using the button above.
-
+
Post New Job
diff --git a/frontend/src/app/post-job/page.test.tsx b/frontend/src/app/jobs/new/page.test.tsx
similarity index 100%
rename from frontend/src/app/post-job/page.test.tsx
rename to frontend/src/app/jobs/new/page.test.tsx
diff --git a/frontend/src/app/post-job/page.tsx b/frontend/src/app/jobs/new/page.tsx
similarity index 100%
rename from frontend/src/app/post-job/page.tsx
rename to frontend/src/app/jobs/new/page.tsx
diff --git a/frontend/src/app/post-job/translations.ts b/frontend/src/app/jobs/new/translations.ts
similarity index 100%
rename from frontend/src/app/post-job/translations.ts
rename to frontend/src/app/jobs/new/translations.ts
diff --git a/frontend/src/app/publicar-vaga/page.tsx b/frontend/src/app/publicar-vaga/page.tsx
index fa2ffe0..a1fc4ea 100644
--- a/frontend/src/app/publicar-vaga/page.tsx
+++ b/frontend/src/app/publicar-vaga/page.tsx
@@ -1,348 +1,5 @@
-"use client"
+import { redirect } from "next/navigation"
-import { useEffect, useState } from "react"
-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 {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-import { Check, Briefcase, Loader2, MapPin, DollarSign, Clock, Building2 } from "lucide-react"
-import Image from "next/image"
-import Link from "next/link"
-import { jobsApi, adminCompaniesApi, type CreateJobPayload, type AdminCompany } from "@/lib/api"
-import { toast } from "sonner"
-
-export default function PublicarVagaPage() {
- const [loading, setLoading] = useState(false)
- const [loadingCompanies, setLoadingCompanies] = useState(true)
- const [companies, setCompanies] = useState([])
-
- const [formData, setFormData] = useState({
- title: "",
- description: "",
- location: "",
- salaryMin: "",
- salaryMax: "",
- salaryType: "monthly",
- currency: "BRL",
- employmentType: "",
- workingHours: "",
- companyId: "",
- })
-
- useEffect(() => {
- const loadCompanies = async () => {
- try {
- setLoadingCompanies(true)
- const data = await adminCompaniesApi.list(undefined, 1, 100)
- setCompanies(data.data ?? [])
- } catch (error) {
- console.error("Falha ao carregar empresas:", error)
- toast.error("Falha ao carregar empresas")
- } finally {
- setLoadingCompanies(false)
- }
- }
-
- loadCompanies()
- }, [])
-
- const canSubmit = () => {
- return (
- formData.title.length >= 5 &&
- formData.description.length >= 20 &&
- formData.companyId !== ""
- )
- }
-
- const handleInputChange = (field: string, value: string) => {
- setFormData((prev) => ({ ...prev, [field]: value }))
- }
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault()
-
- if (!canSubmit()) {
- toast.error("Preencha os campos obrigatórios")
- return
- }
-
- setLoading(true)
- try {
- const payload: CreateJobPayload = {
- companyId: formData.companyId,
- title: formData.title,
- description: formData.description,
- location: formData.location || undefined,
- employmentType: formData.employmentType as CreateJobPayload["employmentType"] || undefined,
- salaryMin: formData.salaryMin ? parseFloat(formData.salaryMin) : undefined,
- salaryMax: formData.salaryMax ? parseFloat(formData.salaryMax) : undefined,
- salaryType: formData.salaryType as CreateJobPayload["salaryType"] || undefined,
- currency: formData.currency as CreateJobPayload["currency"] || undefined,
- workingHours: formData.workingHours || undefined,
- status: "draft",
- }
-
- await jobsApi.create(payload)
- toast.success("Vaga publicada com sucesso! Ela será revisada em breve.")
- setFormData({
- title: "",
- description: "",
- location: "",
- salaryMin: "",
- salaryMax: "",
- salaryType: "monthly",
- currency: "BRL",
- employmentType: "",
- workingHours: "",
- companyId: "",
- })
- } catch (error: any) {
- console.error("Falha ao publicar vaga:", error)
- toast.error(error.message || "Falha ao publicar vaga")
- } finally {
- setLoading(false)
- }
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- Anuncie vagas de emprego
- de forma rápida e eficiente
-
-
-
-
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 cadastrando constantemente
-
-
-
-
-
-
-
-
-
- Anuncie a sua vaga de emprego GRÁTIS!
-
-
- Mais de 50 mil currículos cadastrados
-
-
-
-
-
-
- Título da vaga *
-
- handleInputChange("title", e.target.value)}
- className="h-11 bg-white border-gray-300"
- />
-
-
-
- Descrição da vaga *
- handleInputChange("description", e.target.value)}
- className="bg-white border-gray-300"
- />
-
-
-
-
-
- Localização
-
- handleInputChange("location", e.target.value)}
- className="h-11 bg-white border-gray-300"
- />
-
-
-
-
- Tipo de contrato
-
- handleInputChange("employmentType", v)}>
-
-
-
-
- Permanente
- Tempo integral
- Meio período
- Contrato
- Temporário
- Estágio/Trainee
-
-
-
-
-
-
-
-
-
- Moeda
- handleInputChange("currency", v)}>
-
-
- BRL - R$
- USD - $
- EUR - €
- GBP - £
-
-
-
-
-
- Período do salário
- handleInputChange("salaryType", v)}>
-
-
- Por hora
- Por dia
- Por semana
- Por mês
- Por ano
-
-
-
-
-
- Jornada de trabalho
- handleInputChange("workingHours", e.target.value)}
- className="h-11 bg-white border-gray-300"
- />
-
-
-
-
-
- Empresa *
-
- {loadingCompanies ? (
-
- Carregando empresas...
-
- ) : (
-
handleInputChange("companyId", v)}>
-
-
-
-
- {companies.length === 0 ? (
- Nenhuma empresa disponível
- ) : (
- companies.map((company) => (
- {company.name}
- ))
- )}
-
-
- )}
-
-
-
-
- {loading ? <> PUBLICANDO...> : "ANUNCIAR VAGA GRÁTIS"}
-
-
-
-
-
- Você é um candidato?{" "}
-
- Cadastre-se grátis aqui!
-
-
-
-
-
-
-
© GoHorse Jobs Brasil. Todos os direitos reservados.
-
-
-
-
-
-
-
- )
+export default function PublicarVagaRedirectPage() {
+ redirect("/jobs/new")
}
diff --git a/frontend/src/components/company-sidebar.tsx b/frontend/src/components/company-sidebar.tsx
index d8fafad..5857be8 100644
--- a/frontend/src/components/company-sidebar.tsx
+++ b/frontend/src/components/company-sidebar.tsx
@@ -93,7 +93,7 @@ export function CompanySidebar({ className }: CompanySidebarProps) {
-
+
New job
diff --git a/frontend/src/components/footer.tsx b/frontend/src/components/footer.tsx
index c0dfa0f..159925b 100644
--- a/frontend/src/components/footer.tsx
+++ b/frontend/src/components/footer.tsx
@@ -52,7 +52,7 @@ export function Footer() {
-
+
{t("footerMain.companies.postJob")}