Merge pull request #15 from rede5/codex/update-page-slugs-and-content-language

Translate UI and rename routes to English
This commit is contained in:
Tiago Yamamoto 2025-12-22 15:30:42 -03:00 committed by GitHub
commit 360393ba15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 729 additions and 758 deletions

View file

@ -3,31 +3,31 @@ import { Footer } from "@/components/footer"
import { Card, CardContent } from "@/components/ui/card"
import { Target, Users, Zap, Shield } from "lucide-react"
export default function SobrePage() {
export default function AboutPage() {
const values = [
{
icon: Target,
title: "Missão",
title: "Mission",
description:
"Conectar talentos excepcionais com oportunidades transformadoras, facilitando o crescimento profissional e empresarial.",
"Connect exceptional talent with transformative opportunities, helping professionals and companies grow.",
},
{
icon: Users,
title: "Comunidade",
title: "Community",
description:
"Construir uma comunidade inclusiva onde candidatos e empresas encontram o match perfeito para suas necessidades.",
"Build an inclusive community where candidates and companies find the right match for their needs.",
},
{
icon: Zap,
title: "Eficiência",
title: "Efficiency",
description:
"Simplificar o processo de recrutamento com tecnologia intuitiva e processos otimizados para economizar tempo.",
"Simplify recruiting with intuitive technology and optimized workflows that save time.",
},
{
icon: Shield,
title: "Confiança",
title: "Trust",
description:
"Garantir transparência e segurança em todas as interações, protegendo dados e promovendo relações éticas.",
"Ensure transparency and security in every interaction, protecting data and promoting ethical relationships.",
},
]
@ -41,11 +41,11 @@ export default function SobrePage() {
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto text-center">
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-6 text-balance">
Sobre o Portal de Empregos
About the Jobs Portal
</h1>
<p className="text-lg text-muted-foreground leading-relaxed text-pretty">
Somos uma plataforma dedicada a transformar a forma como pessoas encontram oportunidades profissionais e
como empresas descobrem talentos excepcionais.
We are a platform dedicated to transforming how people find professional opportunities and how companies
discover exceptional talent.
</p>
</div>
</div>
@ -55,19 +55,19 @@ export default function SobrePage() {
<section className="py-16 md:py-24">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto">
<h2 className="text-3xl font-bold text-foreground mb-6">Nossa História</h2>
<h2 className="text-3xl font-bold text-foreground mb-6">Our Story</h2>
<div className="space-y-4 text-muted-foreground leading-relaxed">
<p>
Fundado em 2025, o Portal de Empregos nasceu da necessidade de simplificar o processo de recrutamento,
tornando-o mais transparente, eficiente e acessível para todos.
Founded in 2025, GoHorse Jobs was created to simplify recruiting, making it more transparent,
efficient, and accessible to everyone.
</p>
<p>
Acreditamos que encontrar o emprego ideal não deveria ser complicado. Por isso, criamos uma plataforma
minimalista e intuitiva que coloca candidatos e empresas em primeiro lugar.
We believe that finding the right job should be simple. That is why we built a minimalist,
intuitive platform that puts candidates and companies first.
</p>
<p>
Hoje, conectamos milhares de profissionais com empresas inovadoras, ajudando a construir carreiras de
sucesso e equipes extraordinárias.
Today, we connect thousands of professionals with innovative companies, helping build successful
careers and extraordinary teams.
</p>
</div>
</div>
@ -78,7 +78,7 @@ export default function SobrePage() {
<section className="py-16 md:py-24 bg-muted/30">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-5xl mx-auto">
<h2 className="text-3xl font-bold text-foreground mb-12 text-center">Nossos Valores</h2>
<h2 className="text-3xl font-bold text-foreground mb-12 text-center">Our Values</h2>
<div className="grid md:grid-cols-2 gap-6">
{values.map((value, index) => (
<Card key={index}>

View file

@ -12,7 +12,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Label } from "@/components/ui/label"
import { Mail, MessageSquare, Phone, MapPin } from "lucide-react"
export default function ContatoPage() {
export default function ContactPage() {
const [submitted, setSubmitted] = useState(false)
const handleSubmit = (e: React.FormEvent) => {
@ -30,9 +30,9 @@ export default function ContatoPage() {
<section className="bg-muted/30 py-16 md:py-24">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto text-center">
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-6 text-balance">Entre em Contato</h1>
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-6 text-balance">Get in Touch</h1>
<p className="text-lg text-muted-foreground text-pretty">
Tem alguma dúvida ou sugestão? Estamos aqui para ajudar. Entre em contato conosco.
Have a question or suggestion? We are here to help. Reach out anytime.
</p>
</div>
</div>
@ -45,33 +45,33 @@ export default function ContatoPage() {
{/* Contact Form */}
<Card>
<CardHeader>
<CardTitle>Envie uma mensagem</CardTitle>
<CardDescription>Preencha o formulário e retornaremos em breve</CardDescription>
<CardTitle>Send a message</CardTitle>
<CardDescription>Fill out the form and we will get back to you soon.</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Nome completo</Label>
<Input id="name" placeholder="Seu nome" required />
<Label htmlFor="name">Full name</Label>
<Input id="name" placeholder="Your name" required />
</div>
<div className="space-y-2">
<Label htmlFor="email">E-mail</Label>
<Input id="email" type="email" placeholder="seu@email.com" required />
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="you@email.com" required />
</div>
<div className="space-y-2">
<Label htmlFor="subject">Assunto</Label>
<Input id="subject" placeholder="Como podemos ajudar?" required />
<Label htmlFor="subject">Subject</Label>
<Input id="subject" placeholder="How can we help?" required />
</div>
<div className="space-y-2">
<Label htmlFor="message">Mensagem</Label>
<Textarea id="message" placeholder="Descreva sua dúvida ou sugestão..." rows={5} required />
<Label htmlFor="message">Message</Label>
<Textarea id="message" placeholder="Describe your question or suggestion..." rows={5} required />
</div>
<Button type="submit" className="w-full cursor-pointer" disabled={submitted}>
{submitted ? "Mensagem enviada!" : "Enviar mensagem"}
{submitted ? "Message sent!" : "Send message"}
</Button>
</form>
</CardContent>
@ -80,15 +80,15 @@ export default function ContatoPage() {
{/* Contact Info */}
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold mb-6">Outras formas de contato</h2>
<h2 className="text-2xl font-bold mb-6">Other ways to reach us</h2>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="p-3 rounded-lg bg-primary/10">
<Mail className="h-5 w-5 text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">E-mail</h3>
<p className="text-sm text-muted-foreground">contato@portalempregos.com</p>
<h3 className="font-semibold mb-1">Email</h3>
<p className="text-sm text-muted-foreground">hello@gohorsejobs.com</p>
</div>
</div>
@ -97,7 +97,7 @@ export default function ContatoPage() {
<Phone className="h-5 w-5 text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">Telefone</h3>
<h3 className="font-semibold mb-1">Phone</h3>
<p className="text-sm text-muted-foreground">(11) 9999-9999</p>
</div>
</div>
@ -107,9 +107,9 @@ export default function ContatoPage() {
<MapPin className="h-5 w-5 text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">Endereço</h3>
<h3 className="font-semibold mb-1">Address</h3>
<p className="text-sm text-muted-foreground">
Av. Paulista, 1000
1000 Paulista Ave
<br />
São Paulo, SP - 01310-100
</p>
@ -121,8 +121,8 @@ export default function ContatoPage() {
<MessageSquare className="h-5 w-5 text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">Suporte</h3>
<p className="text-sm text-muted-foreground">Segunda a Sexta, 9h às 18h</p>
<h3 className="font-semibold mb-1">Support</h3>
<p className="text-sm text-muted-foreground">Monday to Friday, 9am to 6pm</p>
</div>
</div>
</div>
@ -130,13 +130,12 @@ export default function ContatoPage() {
<Card className="bg-muted/50">
<CardContent className="pt-6">
<h3 className="font-semibold mb-2">Perguntas Frequentes</h3>
<h3 className="font-semibold mb-2">Frequently Asked Questions</h3>
<p className="text-sm text-muted-foreground mb-4">
Antes de entrar em contato, confira nossa seção de perguntas frequentes. Talvez sua dúvida
esteja respondida .
Before reaching out, check our FAQ section. Your question may already be answered there.
</p>
<Button variant="outline" className="w-full cursor-pointer bg-transparent">
Ver FAQ
View FAQ
</Button>
</CardContent>
</Card>

View file

@ -32,33 +32,33 @@ export default function AdminCandidatesPage() {
<div className="space-y-8">
{/* Header */}
<div>
<h1 className="text-3xl font-bold text-foreground">Gestão de Candidatos</h1>
<p className="text-muted-foreground mt-1">Visualize e gerencie todos os candidatos cadastrados</p>
<h1 className="text-3xl font-bold text-foreground">Candidate management</h1>
<p className="text-muted-foreground mt-1">View and manage all registered candidates</p>
</div>
{/* Stats */}
<div className="grid gap-4 md:grid-cols-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>Total de Candidatos</CardDescription>
<CardDescription>Total candidates</CardDescription>
<CardTitle className="text-3xl">{mockCandidates.length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Novos (30 dias)</CardDescription>
<CardDescription>New (30 days)</CardDescription>
<CardTitle className="text-3xl">24</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Candidaturas Ativas</CardDescription>
<CardDescription>Active applications</CardDescription>
<CardTitle className="text-3xl">{"49"}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Taxa de Contratação</CardDescription>
<CardDescription>Hiring rate</CardDescription>
<CardTitle className="text-3xl">8%</CardTitle>
</CardHeader>
</Card>
@ -71,7 +71,7 @@ export default function AdminCandidatesPage() {
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Buscar candidatos por nome ou email..."
placeholder="Search candidates by name or email..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
@ -83,12 +83,12 @@ export default function AdminCandidatesPage() {
<Table>
<TableHeader>
<TableRow>
<TableHead>Candidato</TableHead>
<TableHead>Candidate</TableHead>
<TableHead>Email</TableHead>
<TableHead>Telefone</TableHead>
<TableHead>Localização</TableHead>
<TableHead>Candidaturas</TableHead>
<TableHead className="text-right">Ações</TableHead>
<TableHead>Phone</TableHead>
<TableHead>Location</TableHead>
<TableHead>Applications</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -118,8 +118,8 @@ export default function AdminCandidatesPage() {
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Perfil do Candidato</DialogTitle>
<DialogDescription>Informações detalhadas sobre {candidate.name}</DialogDescription>
<DialogTitle>Candidate profile</DialogTitle>
<DialogDescription>Detailed information about {candidate.name}</DialogDescription>
</DialogHeader>
{selectedCandidate && (
<div className="space-y-6">
@ -166,12 +166,12 @@ export default function AdminCandidatesPage() {
</div>
<div>
<h4 className="font-semibold mb-2">Sobre</h4>
<h4 className="font-semibold mb-2">About</h4>
<p className="text-sm text-muted-foreground">{selectedCandidate.bio}</p>
</div>
<div>
<h4 className="font-semibold mb-2">Candidaturas Recentes</h4>
<h4 className="font-semibold mb-2">Recent applications</h4>
<div className="space-y-2">
{selectedCandidate.applications.map((app: any) => (
<div
@ -191,9 +191,9 @@ export default function AdminCandidatesPage() {
: "secondary"
}
>
{app.status === "pending" && "Pendente"}
{app.status === "accepted" && "Aceito"}
{app.status === "rejected" && "Rejeitado"}
{app.status === "pending" && "Pending"}
{app.status === "accepted" && "Accepted"}
{app.status === "rejected" && "Rejected"}
</Badge>
</div>
))}

View file

@ -51,7 +51,7 @@ export default function AdminCompaniesPage() {
setCompanies(data || [])
} catch (error) {
console.error("Error loading companies:", error)
toast.error("Erro ao carregar empresas")
toast.error("Failed to load companies")
} finally {
setLoading(false)
}
@ -61,13 +61,13 @@ export default function AdminCompaniesPage() {
try {
setCreating(true)
await companiesApi.create(formData)
toast.success("Empresa criada com sucesso!")
toast.success("Company created successfully!")
setIsDialogOpen(false)
setFormData({ name: "", slug: "", email: "" })
loadCompanies()
} catch (error) {
console.error("Error creating company:", error)
toast.error("Erro ao criar empresa")
toast.error("Failed to create company")
} finally {
setCreating(false)
}
@ -93,29 +93,29 @@ export default function AdminCompaniesPage() {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-foreground">Gestão de Empresas</h1>
<p className="text-muted-foreground mt-1">Gerencie todas as empresas cadastradas</p>
<h1 className="text-3xl font-bold text-foreground">Company management</h1>
<p className="text-muted-foreground mt-1">Manage all registered companies</p>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={loadCompanies} disabled={loading}>
<RefreshCw className={`h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}`} />
Atualizar
Refresh
</Button>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="gap-2">
<Plus className="h-4 w-4" />
Nova Empresa
New company
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Cadastrar Nova Empresa</DialogTitle>
<DialogDescription>Preencha os dados da empresa</DialogDescription>
<DialogTitle>Create new company</DialogTitle>
<DialogDescription>Fill in the company details</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="name">Nome da Empresa</Label>
<Label htmlFor="name">Company name</Label>
<Input
id="name"
value={formData.name}
@ -126,7 +126,7 @@ export default function AdminCompaniesPage() {
slug: generateSlug(e.target.value),
})
}
placeholder="Empresa XYZ"
placeholder="Company XYZ"
/>
</div>
<div className="grid gap-2">
@ -145,15 +145,15 @@ export default function AdminCompaniesPage() {
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
placeholder="contato@empresa.com"
placeholder="hello@company.com"
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>Cancelar</Button>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>Cancel</Button>
<Button onClick={handleCreate} disabled={creating}>
{creating && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
Criar Empresa
Create company
</Button>
</DialogFooter>
</DialogContent>
@ -165,25 +165,25 @@ export default function AdminCompaniesPage() {
<div className="grid gap-4 md:grid-cols-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>Total de Empresas</CardDescription>
<CardDescription>Total companies</CardDescription>
<CardTitle className="text-3xl">{companies.length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Empresas Ativas</CardDescription>
<CardDescription>Active companies</CardDescription>
<CardTitle className="text-3xl">{companies.filter((c) => c.active).length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Verificadas</CardDescription>
<CardDescription>Verified</CardDescription>
<CardTitle className="text-3xl">{companies.filter((c) => c.verified).length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Pendentes</CardDescription>
<CardDescription>Pending</CardDescription>
<CardTitle className="text-3xl">{companies.filter((c) => !c.verified).length}</CardTitle>
</CardHeader>
</Card>
@ -196,7 +196,7 @@ export default function AdminCompaniesPage() {
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Buscar empresas por nome ou email..."
placeholder="Search companies by name or email..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
@ -213,19 +213,19 @@ export default function AdminCompaniesPage() {
<Table>
<TableHeader>
<TableRow>
<TableHead>Empresa</TableHead>
<TableHead>Company</TableHead>
<TableHead>Slug</TableHead>
<TableHead>Email</TableHead>
<TableHead>Status</TableHead>
<TableHead>Verificada</TableHead>
<TableHead>Data Criação</TableHead>
<TableHead>Verified</TableHead>
<TableHead>Created</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredCompanies.length === 0 ? (
<TableRow>
<TableCell colSpan={6} className="text-center text-muted-foreground py-8">
Nenhuma empresa encontrada
No companies found
</TableCell>
</TableRow>
) : (
@ -241,7 +241,7 @@ export default function AdminCompaniesPage() {
<TableCell>{company.email || "-"}</TableCell>
<TableCell>
<Badge variant={company.active ? "default" : "secondary"}>
{company.active ? "Ativa" : "Inativa"}
{company.active ? "Active" : "Inactive"}
</Badge>
</TableCell>
<TableCell>
@ -252,7 +252,7 @@ export default function AdminCompaniesPage() {
)}
</TableCell>
<TableCell>
{company.created_at ? new Date(company.created_at).toLocaleDateString("pt-BR") : "-"}
{company.created_at ? new Date(company.created_at).toLocaleDateString("en-US") : "-"}
</TableCell>
</TableRow>
))

View file

@ -41,83 +41,83 @@ export default function AdminJobsPage() {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-foreground">Gestão de Vagas</h1>
<p className="text-muted-foreground mt-1">Gerencie todas as vagas publicadas na plataforma</p>
<h1 className="text-3xl font-bold text-foreground">Job management</h1>
<p className="text-muted-foreground mt-1">Manage all jobs posted on the platform</p>
</div>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="gap-2">
<Plus className="h-4 w-4" />
Nova Vaga
New job
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Criar Nova Vaga</DialogTitle>
<DialogDescription>Preencha os detalhes da nova vaga de emprego</DialogDescription>
<DialogTitle>Create new job</DialogTitle>
<DialogDescription>Fill in the details for the new job opening</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="title">Título da Vaga</Label>
<Input id="title" placeholder="Ex: Desenvolvedor Full Stack" />
<Label htmlFor="title">Job title</Label>
<Input id="title" placeholder="e.g. Full Stack Developer" />
</div>
<div className="grid gap-2">
<Label htmlFor="company">Empresa</Label>
<Input id="company" placeholder="Nome da empresa" />
<Label htmlFor="company">Company</Label>
<Input id="company" placeholder="Company name" />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="location">Localização</Label>
<Label htmlFor="location">Location</Label>
<Input id="location" placeholder="São Paulo, SP" />
</div>
<div className="grid gap-2">
<Label htmlFor="type">Tipo</Label>
<Label htmlFor="type">Type</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Selecione" />
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="full-time">Tempo Integral</SelectItem>
<SelectItem value="part-time">Meio Período</SelectItem>
<SelectItem value="contract">Contrato</SelectItem>
<SelectItem value="Remoto">Remoto</SelectItem>
<SelectItem value="full-time">Full time</SelectItem>
<SelectItem value="part-time">Part time</SelectItem>
<SelectItem value="contract">Contract</SelectItem>
<SelectItem value="remote">Remote</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="salary">Salário</Label>
<Input id="salary" placeholder="R$ 8.000 - R$ 12.000" />
<Label htmlFor="salary">Salary</Label>
<Input id="salary" placeholder="R$ 8,000 - R$ 12,000" />
</div>
<div className="grid gap-2">
<Label htmlFor="level">vel</Label>
<Label htmlFor="level">Level</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Selecione" />
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="junior">Júnior</SelectItem>
<SelectItem value="pleno">Pleno</SelectItem>
<SelectItem value="senior">Sênior</SelectItem>
<SelectItem value="junior">Junior</SelectItem>
<SelectItem value="mid">Mid-level</SelectItem>
<SelectItem value="senior">Senior</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="description">Descrição</Label>
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
placeholder="Descreva as responsabilidades e requisitos da vaga..."
placeholder="Describe the responsibilities and requirements..."
rows={4}
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>
Cancelar
Cancel
</Button>
<Button onClick={() => setIsDialogOpen(false)}>Publicar Vaga</Button>
<Button onClick={() => setIsDialogOpen(false)}>Publish job</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@ -127,25 +127,25 @@ export default function AdminJobsPage() {
<div className="grid gap-4 md:grid-cols-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>Total de Vagas</CardDescription>
<CardDescription>Total jobs</CardDescription>
<CardTitle className="text-3xl">{jobs.length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Vagas Ativas</CardDescription>
<CardDescription>Active jobs</CardDescription>
<CardTitle className="text-3xl">{jobs.length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Candidaturas</CardDescription>
<CardDescription>Applications</CardDescription>
<CardTitle className="text-3xl">{"436"}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Taxa de Conversão</CardDescription>
<CardDescription>Conversion rate</CardDescription>
<CardTitle className="text-3xl">12%</CardTitle>
</CardHeader>
</Card>
@ -158,7 +158,7 @@ export default function AdminJobsPage() {
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Buscar vagas por título ou empresa..."
placeholder="Search jobs by title or company..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
@ -170,13 +170,13 @@ export default function AdminJobsPage() {
<Table>
<TableHeader>
<TableRow>
<TableHead>Vaga</TableHead>
<TableHead>Empresa</TableHead>
<TableHead>Localização</TableHead>
<TableHead>Tipo</TableHead>
<TableHead>Candidaturas</TableHead>
<TableHead>Role</TableHead>
<TableHead>Company</TableHead>
<TableHead>Location</TableHead>
<TableHead>Type</TableHead>
<TableHead>Applications</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Ações</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -190,7 +190,7 @@ export default function AdminJobsPage() {
</TableCell>
<TableCell>{((job.id.charCodeAt(0) * 7) % 50) + 10}</TableCell>
<TableCell>
<Badge variant="default">Ativa</Badge>
<Badge variant="default">Active</Badge>
</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end gap-2">

View file

@ -15,7 +15,7 @@ const mockConversations = [
id: "1",
name: "Ana Silva",
avatar: "/professional-woman-diverse.png",
lastMessage: "Obrigada pela resposta sobre a vaga!",
lastMessage: "Thanks for the response about the role!",
timestamp: "10:30",
unread: 2,
},
@ -23,16 +23,16 @@ const mockConversations = [
id: "2",
name: "Carlos Santos",
avatar: "/professional-man.jpg",
lastMessage: "Quando posso esperar um retorno?",
timestamp: "Ontem",
lastMessage: "When can I expect an update?",
timestamp: "Yesterday",
unread: 0,
},
{
id: "3",
name: "Maria Oliveira",
avatar: "/professional-woman-smiling.png",
lastMessage: "Gostaria de mais informações sobre os benefícios",
timestamp: "2 dias",
lastMessage: "I'd like more information about the benefits.",
timestamp: "2 days ago",
unread: 1,
},
]
@ -41,21 +41,21 @@ const mockMessages = [
{
id: "1",
sender: "Ana Silva",
content: "Olá! Gostaria de saber mais sobre a vaga de Desenvolvedor Full Stack.",
content: "Hi! I'd like to know more about the Full Stack Developer role.",
timestamp: "10:15",
isAdmin: false,
},
{
id: "2",
sender: "Você",
content: "Olá Ana! Claro, ficarei feliz em ajudar. A vaga é para trabalho remoto e oferece benefícios completos.",
sender: "You",
content: "Hi Ana! Of course—happy to help. The role is remote and includes full benefits.",
timestamp: "10:20",
isAdmin: true,
},
{
id: "3",
sender: "Ana Silva",
content: "Obrigada pela resposta sobre a vaga!",
content: "Thanks for the response about the role!",
timestamp: "10:30",
isAdmin: false,
},
@ -81,21 +81,21 @@ export default function AdminMessagesPage() {
<div className="space-y-8">
{/* Header */}
<div>
<h1 className="text-3xl font-bold text-foreground">Mensagens</h1>
<p className="text-muted-foreground mt-1">Comunique-se com candidatos e empresas</p>
<h1 className="text-3xl font-bold text-foreground">Messages</h1>
<p className="text-muted-foreground mt-1">Communicate with candidates and companies</p>
</div>
{/* Stats */}
<div className="grid gap-4 md:grid-cols-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>Total de Conversas</CardDescription>
<CardDescription>Total conversations</CardDescription>
<CardTitle className="text-3xl">{mockConversations.length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Não Lidas</CardDescription>
<CardDescription>Unread</CardDescription>
<CardTitle className="text-3xl">
{mockConversations.reduce((acc, conv) => acc + conv.unread, 0)}
</CardTitle>
@ -103,13 +103,13 @@ export default function AdminMessagesPage() {
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Respondidas Hoje</CardDescription>
<CardDescription>Replied today</CardDescription>
<CardTitle className="text-3xl">12</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Tempo Médio de Resposta</CardDescription>
<CardDescription>Average response time</CardDescription>
<CardTitle className="text-3xl">2h</CardTitle>
</CardHeader>
</Card>
@ -124,7 +124,7 @@ export default function AdminMessagesPage() {
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Buscar conversas..."
placeholder="Search conversations..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
@ -201,7 +201,7 @@ export default function AdminMessagesPage() {
<Paperclip className="h-4 w-4" />
</Button>
<Textarea
placeholder="Digite sua mensagem..."
placeholder="Type your message..."
value={messageText}
onChange={(e) => setMessageText(e.target.value)}
onKeyDown={(e) => {

View file

@ -1,16 +1,10 @@
"use client"
import { DashboardHeader } from "@/components/dashboard-header" // Keep header if we want consistent layout, though global layout handles it
// Actually global layout handles sidebar/header.
// We just need the content.
// But wait, "My Jobs" page doesn't exist yet!
// I'll create a placeholder for now to prevent 404s on the new links.
export default function MyJobsPage() {
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">Minhas Vagas</h1>
<p>Funcionalidade em desenvolvimento.</p>
<h1 className="text-2xl font-bold mb-4">My jobs</h1>
<p>Feature in progress.</p>
</div>
)
}

View file

@ -53,7 +53,7 @@ export default function AdminUsersPage() {
setUsers(data || [])
} catch (error) {
console.error("Error loading users:", error)
toast.error("Erro ao carregar usuários")
toast.error("Failed to load users")
} finally {
setLoading(false)
}
@ -63,27 +63,27 @@ export default function AdminUsersPage() {
try {
setCreating(true)
await usersApi.create(formData)
toast.success("Usuário criado com sucesso!")
toast.success("User created successfully!")
setIsDialogOpen(false)
setFormData({ name: "", email: "", password: "", role: "jobSeeker" })
loadUsers()
} catch (error) {
console.error("Error creating user:", error)
toast.error("Erro ao criar usuário")
toast.error("Failed to create user")
} finally {
setCreating(false)
}
}
const handleDelete = async (id: string) => {
if (!confirm("Tem certeza que deseja excluir este usuário?")) return
if (!confirm("Are you sure you want to delete this user?")) return
try {
await usersApi.delete(id)
toast.success("Usuário excluído!")
toast.success("User deleted!")
loadUsers()
} catch (error) {
console.error("Error deleting user:", error)
toast.error("Erro ao excluir usuário")
toast.error("Failed to delete user")
}
}
@ -96,11 +96,11 @@ export default function AdminUsersPage() {
const getRoleBadge = (role: string) => {
const labels: Record<string, string> = {
superadmin: "Super Admin",
companyAdmin: "Admin Empresa",
recruiter: "Recrutador",
jobSeeker: "Candidato",
companyAdmin: "Company admin",
recruiter: "Recruiter",
jobSeeker: "Candidate",
admin: "Admin",
company: "Empresa"
company: "Company"
}
const colors: Record<string, "default" | "secondary" | "destructive" | "outline"> = {
superadmin: "destructive",
@ -110,7 +110,7 @@ export default function AdminUsersPage() {
admin: "destructive",
company: "default"
}
const label = labels[role] || role || "Usuário"
const label = labels[role] || role || "User"
return <Badge variant={colors[role] || "outline"}>{label}</Badge>
}
@ -119,34 +119,34 @@ export default function AdminUsersPage() {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-foreground">Gestão de Usuários</h1>
<p className="text-muted-foreground mt-1">Gerencie todos os usuários da plataforma</p>
<h1 className="text-3xl font-bold text-foreground">User management</h1>
<p className="text-muted-foreground mt-1">Manage all platform users</p>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={loadUsers} disabled={loading}>
<RefreshCw className={`h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}`} />
Atualizar
Refresh
</Button>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="gap-2">
<Plus className="h-4 w-4" />
Novo Usuário
New user
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Criar Novo Usuário</DialogTitle>
<DialogDescription>Preencha os dados do novo usuário</DialogDescription>
<DialogTitle>Create new user</DialogTitle>
<DialogDescription>Fill in the new user details</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="name">Nome</Label>
<Label htmlFor="name">Name</Label>
<Input
id="name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder="Nome completo"
placeholder="Full name"
/>
</div>
<div className="grid gap-2">
@ -156,39 +156,39 @@ export default function AdminUsersPage() {
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
placeholder="email@exemplo.com"
placeholder="email@example.com"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="password">Senha</Label>
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
placeholder="Senha segura"
placeholder="Secure password"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="role">Função</Label>
<Label htmlFor="role">Role</Label>
<Select value={formData.role} onValueChange={(v) => setFormData({ ...formData, role: v })}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="superadmin">Super Admin</SelectItem>
<SelectItem value="companyAdmin">Admin Empresa</SelectItem>
<SelectItem value="recruiter">Recrutador</SelectItem>
<SelectItem value="jobSeeker">Candidato</SelectItem>
<SelectItem value="companyAdmin">Company admin</SelectItem>
<SelectItem value="recruiter">Recruiter</SelectItem>
<SelectItem value="jobSeeker">Candidate</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>Cancelar</Button>
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>Cancel</Button>
<Button onClick={handleCreate} disabled={creating}>
{creating && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
Criar Usuário
Create user
</Button>
</DialogFooter>
</DialogContent>
@ -200,7 +200,7 @@ export default function AdminUsersPage() {
<div className="grid gap-4 md:grid-cols-4">
<Card>
<CardHeader className="pb-3">
<CardDescription>Total de Usuários</CardDescription>
<CardDescription>Total users</CardDescription>
<CardTitle className="text-3xl">{users.length}</CardTitle>
</CardHeader>
</Card>
@ -214,13 +214,13 @@ export default function AdminUsersPage() {
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Recrutadores</CardDescription>
<CardDescription>Recruiters</CardDescription>
<CardTitle className="text-3xl">{users.filter((u) => u.role === "recruiter").length}</CardTitle>
</CardHeader>
</Card>
<Card>
<CardHeader className="pb-3">
<CardDescription>Candidatos</CardDescription>
<CardDescription>Candidates</CardDescription>
<CardTitle className="text-3xl">{users.filter((u) => u.role === "jobSeeker").length}</CardTitle>
</CardHeader>
</Card>
@ -233,7 +233,7 @@ export default function AdminUsersPage() {
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Buscar usuários por nome ou email..."
placeholder="Search users by name or email..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
@ -250,19 +250,19 @@ export default function AdminUsersPage() {
<Table>
<TableHeader>
<TableRow>
<TableHead>Nome</TableHead>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Função</TableHead>
<TableHead>Role</TableHead>
<TableHead>Status</TableHead>
<TableHead>Data Criação</TableHead>
<TableHead className="text-right">Ações</TableHead>
<TableHead>Created</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredUsers.length === 0 ? (
<TableRow>
<TableCell colSpan={6} className="text-center text-muted-foreground py-8">
Nenhum usuário encontrado
No users found
</TableCell>
</TableRow>
) : (
@ -273,11 +273,11 @@ export default function AdminUsersPage() {
<TableCell>{getRoleBadge(user.role)}</TableCell>
<TableCell>
<Badge variant={user.status === "active" ? "default" : "secondary"}>
{user.status === "active" ? "Ativo" : user.status}
{user.status === "active" ? "Active" : user.status}
</Badge>
</TableCell>
<TableCell>
{user.created_at ? new Date(user.created_at).toLocaleDateString("pt-BR") : "-"}
{user.created_at ? new Date(user.created_at).toLocaleDateString("en-US") : "-"}
</TableCell>
<TableCell className="text-right">
<Button

View file

@ -44,12 +44,12 @@ import { Footer } from "@/components/footer";
import { useNotify } from "@/contexts/notification-context";
import { mockJobs } from "@/lib/mock-data";
// Definição Dos Passos
// Step definitions
const steps = [
{ id: 1, title: "Dados Pessoais", icon: User },
{ id: 2, title: "Currículo e Documentos", icon: FileText },
{ id: 3, title: "Experiência", icon: Briefcase },
{ id: 4, title: "Perguntas Adicionais", icon: MessageSquare },
{ id: 1, title: "Personal Details", icon: User },
{ id: 2, title: "Resume & Documents", icon: FileText },
{ id: 3, title: "Experience", icon: Briefcase },
{ id: 4, title: "Additional Questions", icon: MessageSquare },
];
@ -66,25 +66,25 @@ export default function JobApplicationPage({
const [currentStep, setCurrentStep] = useState(1);
const [isSubmitting, setIsSubmitting] = useState(false);
// Achar os detalhes da vaga
// Find job details
const job = mockJobs.find((j) => j.id === id) || mockJobs[0];
// Estado do formulário
// Form state
const [formData, setFormData] = useState({
// Etapa 1
// Step 1
fullName: "",
email: "",
phone: "",
linkedin: "",
privacyAccepted: false,
// Etapa 2
// Step 2
resume: null as File | null,
coverLetter: "",
portfolioUrl: "",
// Etapa 3
// Step 3
salaryExpectation: "",
hasExperience: "",
// Etapa 4
// Step 4
whyUs: "",
availability: [] as string[],
});
@ -98,22 +98,22 @@ export default function JobApplicationPage({
case 1:
if (!formData.fullName || !formData.email || !formData.phone) {
notify.error(
"Campos obrigatórios",
"Por favor, preencha todos os campos obrigatórios."
"Required fields",
"Please fill out all required fields."
);
return false;
}
if (!formData.email.includes("@")) {
notify.error(
"E-mail inválido",
"Por favor, insira um e-mail válido."
"Invalid email",
"Please enter a valid email address."
);
return false;
}
if (!formData.privacyAccepted) {
notify.error(
"Termos de Privacidade",
"Você precisa aceitar a política de privacidade para continuar."
"Privacy policy",
"You must accept the privacy policy to continue."
);
return false;
}
@ -123,8 +123,8 @@ export default function JobApplicationPage({
case 3:
if (!formData.salaryExpectation || !formData.hasExperience) {
notify.error(
"Campos obrigatórios",
"Por favor, responda todas as perguntas."
"Required fields",
"Please answer all questions."
);
return false;
}
@ -132,8 +132,8 @@ export default function JobApplicationPage({
case 4:
if (!formData.whyUs || formData.availability.length === 0) {
notify.error(
"Campos obrigatórios",
"Por favor, preencha o motivo e selecione pelo menos uma disponibilidade."
"Required fields",
"Please provide your reason and select at least one availability option."
);
return false;
}
@ -163,21 +163,21 @@ export default function JobApplicationPage({
const handleSubmit = async () => {
setIsSubmitting(true);
// Simular um chamado de API
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 2000));
notify.success(
"Candidatura enviada com sucesso!",
`Boa sorte! Sua candidatura para ${job.title} foi recebida.`
"Application submitted!",
`Good luck! Your application for ${job.title} has been received.`
);
router.push("/dashboard/candidato/candidaturas");
router.push("/dashboard/my-applications");
};
const handleSaveDraft = () => {
notify.info(
"Rascunho salvo",
"Você pode continuar sua candidatura mais tarde."
"Draft saved",
"You can finish your application later."
);
};
@ -198,28 +198,28 @@ export default function JobApplicationPage({
className="inline-flex items-center text-sm text-muted-foreground hover:text-primary mb-4 transition-colors"
>
<ArrowLeft className="mr-2 h-4 w-4" />
Voltar para detalhes da vaga
Back to job details
</Link>
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h1 className="text-2xl md:text-3xl font-bold text-foreground">
Candidatura: {job.title}
Application: {job.title}
</h1>
<p className="text-muted-foreground mt-1">
{job.company} {job.location}
</p>
</div>
<div className="text-sm font-medium bg-primary/10 text-primary px-3 py-1 rounded-full self-start md:self-center">
Tempo estimado: 5 min
Estimated time: 5 min
</div>
</div>
</div>
{/* Progresso das etapas */}
{/* Step progress */}
<div className="mb-8">
<div className="flex justify-between mb-2">
<span className="text-sm font-medium text-muted-foreground">
Etapa {currentStep} de {steps.length}:{" "}
Step {currentStep} of {steps.length}:{" "}
<span className="text-foreground">
{steps[currentStep - 1].title}
</span>
@ -230,7 +230,7 @@ export default function JobApplicationPage({
</div>
<Progress value={progress} className="h-2" />
{/* Indicador de etapas (DESKTOP) */}
{/* Step indicator (desktop) */}
<div className="hidden md:flex justify-between mt-4 px-2">
{steps.map((step) => {
const Icon = step.icon;
@ -271,7 +271,7 @@ export default function JobApplicationPage({
</div>
</div>
{/* Conteúdo do formulário */}
{/* Form content */}
<div className="grid gap-6">
<AnimatePresence mode="wait">
<motion.div
@ -285,20 +285,20 @@ export default function JobApplicationPage({
<CardHeader>
<CardTitle>{steps[currentStep - 1].title}</CardTitle>
<CardDescription>
Preencha as informações abaixo para continuar.
Fill in the information below to continue.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Etapa 1: Dados Pessoais */}
{/* Step 1: Personal Details */}
{currentStep === 1 && (
<div className="space-y-4">
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="fullName">Nome Completo *</Label>
<Label htmlFor="fullName">Full name *</Label>
<Input
id="fullName"
placeholder="Seu nome completo"
placeholder="Your full name"
value={formData.fullName}
onChange={(e) =>
handleInputChange("fullName", e.target.value)
@ -306,11 +306,11 @@ export default function JobApplicationPage({
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">E-mail *</Label>
<Label htmlFor="email">Email *</Label>
<Input
id="email"
type="email"
placeholder="seu@email.com"
placeholder="you@email.com"
value={formData.email}
onChange={(e) =>
handleInputChange("email", e.target.value)
@ -321,7 +321,7 @@ export default function JobApplicationPage({
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="phone">Telefone / WhatsApp *</Label>
<Label htmlFor="phone">Phone / WhatsApp *</Label>
<Input
id="phone"
placeholder="(00) 00000-0000"
@ -335,7 +335,7 @@ export default function JobApplicationPage({
<Label htmlFor="linkedin">LinkedIn (URL)</Label>
<Input
id="linkedin"
placeholder="linkedin.com/in/seu-perfil"
placeholder="linkedin.com/in/your-profile"
value={formData.linkedin}
onChange={(e) =>
handleInputChange("linkedin", e.target.value)
@ -356,22 +356,21 @@ export default function JobApplicationPage({
htmlFor="privacy"
className="text-sm font-normal text-muted-foreground"
>
Li e concordo com a{" "}
<a href="#" className="text-primary underline">
Política de Privacidade
</a>{" "}
e autorizo o tratamento dos meus dados para fins de
recrutamento.
I have read and agree to the{" "}
<Link href="/privacy" className="text-primary underline">
Privacy Policy
</Link>{" "}
and authorize the processing of my data for recruitment purposes.
</Label>
</div>
</div>
)}
{/* Etapa 2: Dccumentos */}
{/* Step 2: Documents */}
{currentStep === 2 && (
<div className="space-y-6">
<div className="space-y-3">
<Label>Currículo (CV) *</Label>
<Label>Resume (CV) *</Label>
<div className="border-2 border-dashed border-muted-foreground/25 rounded-lg p-8 text-center hover:bg-muted/50 transition-colors cursor-pointer">
<div className="flex flex-col items-center gap-2">
<div className="p-3 bg-primary/10 rounded-full text-primary">
@ -379,10 +378,10 @@ export default function JobApplicationPage({
</div>
<div className="space-y-1">
<p className="text-sm font-medium">
Clique para fazer upload ou arraste o arquivo
Click to upload or drag the file here
</p>
<p className="text-xs text-muted-foreground">
PDF, DOCX ou TXT (Máx. 5MB)
PDF, DOCX, or TXT (Max 5MB)
</p>
</div>
</div>
@ -391,7 +390,7 @@ export default function JobApplicationPage({
<div className="space-y-2">
<Label htmlFor="portfolio">
Portfólio / Site Pessoal (Opcional)
Portfolio / Personal Website (Optional)
</Label>
<Input
id="portfolio"
@ -405,11 +404,11 @@ export default function JobApplicationPage({
<div className="space-y-2">
<Label htmlFor="coverLetter">
Carta de Apresentação (Opcional)
Cover Letter (Optional)
</Label>
<Textarea
id="coverLetter"
placeholder="Escreva uma breve apresentação sobre você..."
placeholder="Write a short introduction about yourself..."
className="min-h-[150px]"
value={formData.coverLetter}
onChange={(e) =>
@ -420,11 +419,11 @@ export default function JobApplicationPage({
</div>
)}
{/* Etapa 3: Experiências */}
{/* Step 3: Experience */}
{currentStep === 3 && (
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="salary">Pretensão Salarial *</Label>
<Label htmlFor="salary">Salary expectation *</Label>
<Select
value={formData.salaryExpectation}
onValueChange={(val) =>
@ -432,23 +431,23 @@ export default function JobApplicationPage({
}
>
<SelectTrigger>
<SelectValue placeholder="Selecione uma faixa" />
<SelectValue placeholder="Select a range" />
</SelectTrigger>
<SelectContent>
<SelectItem value="ate-3k">
Até R$ 3.000
<SelectItem value="up-to-3k">
Up to R$ 3,000
</SelectItem>
<SelectItem value="3k-5k">
R$ 3.000 - R$ 5.000
R$ 3,000 - R$ 5,000
</SelectItem>
<SelectItem value="5k-8k">
R$ 5.000 - R$ 8.000
R$ 5,000 - R$ 8,000
</SelectItem>
<SelectItem value="8k-12k">
R$ 8.000 - R$ 12.000
R$ 8,000 - R$ 12,000
</SelectItem>
<SelectItem value="12k-plus">
Acima de R$ 12.000
Above R$ 12,000
</SelectItem>
</SelectContent>
</Select>
@ -456,8 +455,8 @@ export default function JobApplicationPage({
<div className="space-y-3">
<Label>
Você possui a experiência mínima requisitada para a
vaga? *
Do you have the minimum experience required for the
role? *
</Label>
<div className="flex gap-4">
<div className="flex items-center space-x-2 border p-3 rounded-md flex-1 hover:bg-muted/50 cursor-pointer">
@ -475,7 +474,7 @@ export default function JobApplicationPage({
htmlFor="exp-yes"
className="cursor-pointer flex-1"
>
Sim, possuo
Yes, I do
</Label>
</div>
<div className="flex items-center space-x-2 border p-3 rounded-md flex-1 hover:bg-muted/50 cursor-pointer">
@ -493,7 +492,7 @@ export default function JobApplicationPage({
htmlFor="exp-no"
className="cursor-pointer flex-1"
>
Não possuo
Not yet
</Label>
</div>
</div>
@ -501,16 +500,16 @@ export default function JobApplicationPage({
</div>
)}
{/* Etapa 4: Adicional */}
{/* Step 4: Additional */}
{currentStep === 4 && (
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="whyUs">
Por que você quer trabalhar na {job.company}? *
Why do you want to work at {job.company}? *
</Label>
<Textarea
id="whyUs"
placeholder="Conte-nos o que te atrai na nossa empresa e nesta vaga..."
placeholder="Tell us what attracts you to this company and role..."
className="min-h-[150px]"
maxLength={1000}
value={formData.whyUs}
@ -519,18 +518,18 @@ export default function JobApplicationPage({
}
/>
<div className="text-xs text-right text-muted-foreground">
{formData.whyUs.length}/1000 caracteres
{formData.whyUs.length}/1000 characters
</div>
</div>
<div className="space-y-3">
<Label>Disponibilidade *</Label>
<Label>Availability *</Label>
<div className="grid gap-2">
{[
"Trabalho Presencial",
"Trabalho Remoto",
"Viagens",
"Início Imediato",
"On-site work",
"Remote work",
"Travel",
"Immediate start",
].map((item) => (
<div
key={item}
@ -571,7 +570,7 @@ export default function JobApplicationPage({
disabled={currentStep === 1 || isSubmitting}
>
<ChevronLeft className="mr-2 h-4 w-4" />
Voltar
Back
</Button>
<div className="flex gap-2">
@ -582,7 +581,7 @@ export default function JobApplicationPage({
className="hidden sm:flex"
>
<Save className="mr-2 h-4 w-4" />
Salvar Rascunho
Save draft
</Button>
<Button
@ -591,15 +590,15 @@ export default function JobApplicationPage({
className="min-w-[120px]"
>
{isSubmitting ? (
"Enviando..."
"Submitting..."
) : currentStep === steps.length ? (
<>
Enviar Candidatura{" "}
Submit application{" "}
<CheckCircle2 className="ml-2 h-4 w-4" />
</>
) : (
<>
Próxima Etapa{" "}
Next step{" "}
<ChevronRight className="ml-2 h-4 w-4" />
</>
)}
@ -616,4 +615,4 @@ export default function JobApplicationPage({
<Footer />
</div>
);
}
}

View file

@ -64,12 +64,12 @@ export default function JobDetailPage({
<div className="w-16 h-16 bg-muted rounded-full flex items-center justify-center mx-auto mb-4">
<Briefcase className="w-8 h-8 text-muted-foreground" />
</div>
<h1 className="text-2xl font-bold mb-2">Vaga não encontrada</h1>
<h1 className="text-2xl font-bold mb-2">Job not found</h1>
<p className="text-muted-foreground mb-6">
A vaga que você está procurando não existe ou foi removida.
The job you are looking for does not exist or has been removed.
</p>
<Link href="/jobs">
<Button>Ver todas as vagas</Button>
<Button>View all jobs</Button>
</Link>
</motion.div>
</main>
@ -93,28 +93,28 @@ export default function JobDetailPage({
const diffInMs = now.getTime() - date.getTime();
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
if (diffInDays === 0) return "Hoje";
if (diffInDays === 1) return "Ontem";
if (diffInDays < 7) return `${diffInDays} dias atrás`;
if (diffInDays < 30) return `${Math.floor(diffInDays / 7)} semanas atrás`;
return `${Math.floor(diffInDays / 30)} meses atrás`;
if (diffInDays === 0) return "Today";
if (diffInDays === 1) return "Yesterday";
if (diffInDays < 7) return `${diffInDays} days ago`;
if (diffInDays < 30) return `${Math.floor(diffInDays / 7)} weeks ago`;
return `${Math.floor(diffInDays / 30)} months ago`;
};
const getTypeLabel = (type: string) => {
const typeLabels: { [key: string]: string } = {
"full-time": "Tempo integral",
"part-time": "Meio período",
contract: "Contrato",
Remoto: "Remoto",
"full-time": "Full time",
"part-time": "Part time",
contract: "Contract",
remote: "Remote",
};
return typeLabels[type] || type;
};
const mockCompanyInfo = {
size: "100-500 funcionários",
industry: "Tecnologia",
size: "100-500 employees",
industry: "Technology",
founded: "2015",
website: "www.empresa.com",
website: "www.company.com",
rating: 4.5,
};
@ -134,7 +134,7 @@ export default function JobDetailPage({
<Link href="/jobs">
<Button variant="ghost" className="gap-2 hover:bg-muted">
<ArrowLeft className="h-4 w-4" />
Voltar para vagas
Back to jobs
</Button>
</Link>
</motion.div>
@ -224,7 +224,7 @@ export default function JobDetailPage({
: ""
}`}
/>
{isFavorited ? "Favoritado" : "Favoritar"}
{isFavorited ? "Favorited" : "Favorite"}
</Button>
<Button
variant="outline"
@ -236,7 +236,7 @@ export default function JobDetailPage({
className={`h-4 w-4 mr-1 ${isBookmarked ? "fill-current" : ""
}`}
/>
{isBookmarked ? "Salvo" : "Salvar"}
{isBookmarked ? "Saved" : "Save"}
</Button>
<Button
variant="outline"
@ -284,11 +284,11 @@ export default function JobDetailPage({
{/* Apply Button - Mobile */}
<div className="lg:hidden pt-4">
<Link
href={`/jobs/${job.id}/candidatura`}
href={`/jobs/${job.id}/apply`}
className="w-full"
>
<Button size="lg" className="w-full cursor-pointer">
Candidatar-se
Apply now
</Button>
</Link>
</div>
@ -304,7 +304,7 @@ export default function JobDetailPage({
>
<Card>
<CardHeader>
<CardTitle className="text-xl">Sobre a vaga</CardTitle>
<CardTitle className="text-xl">About the role</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p className="text-muted-foreground leading-relaxed whitespace-pre-line">
@ -322,7 +322,7 @@ export default function JobDetailPage({
>
<Card>
<CardHeader>
<CardTitle className="text-xl">Requisitos</CardTitle>
<CardTitle className="text-xl">Requirements</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-3">
@ -345,21 +345,21 @@ export default function JobDetailPage({
>
<Card>
<CardHeader>
<CardTitle className="text-xl">Sobre a empresa</CardTitle>
<CardTitle className="text-xl">About the company</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-muted-foreground leading-relaxed">
{job.company} é uma empresa líder no mercado,
comprometida em criar um ambiente de trabalho inclusivo
e inovador. Oferecemos benefícios competitivos e
oportunidades de crescimento profissional.
{job.company} is a market leader committed to creating
an inclusive and innovative workplace. We offer
competitive benefits and opportunities for professional
growth.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4 border-t">
<div>
<div className="flex items-center gap-2 text-sm text-muted-foreground mb-1">
<Users className="h-4 w-4 shrink-0" />
<span>Tamanho</span>
<span>Size</span>
</div>
<p className="font-medium text-sm">
{mockCompanyInfo.size}
@ -368,7 +368,7 @@ export default function JobDetailPage({
<div>
<div className="flex items-center gap-2 text-sm text-muted-foreground mb-1">
<Building2 className="h-4 w-4 shrink-0" />
<span>Setor</span>
<span>Industry</span>
</div>
<p className="font-medium text-sm">
{mockCompanyInfo.industry}
@ -377,7 +377,7 @@ export default function JobDetailPage({
<div>
<div className="flex items-center gap-2 text-sm text-muted-foreground mb-1">
<Calendar className="h-4 w-4 shrink-0" />
<span>Fundada</span>
<span>Founded</span>
</div>
<p className="font-medium text-sm">
{mockCompanyInfo.founded}
@ -414,19 +414,19 @@ export default function JobDetailPage({
<Card>
<CardHeader>
<CardTitle className="text-lg">
Interessado na vaga?
Interested in this role?
</CardTitle>
<CardDescription>
Candidate-se agora e faça parte da nossa equipe!
Apply now and join our team!
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Link
href={`/jobs/${job.id}/candidatura`}
href={`/jobs/${job.id}/apply`}
className="w-full"
>
<Button size="lg" className="w-full cursor-pointer">
Candidatar-se
Apply now
</Button>
</Link>
@ -435,7 +435,7 @@ export default function JobDetailPage({
<div className="space-y-3 text-sm">
<div className="flex items-start justify-between gap-2">
<span className="text-muted-foreground">
Tipo de vaga:
Job type:
</span>
<Badge
variant="outline"
@ -446,7 +446,7 @@ export default function JobDetailPage({
</div>
<div className="flex items-start justify-between gap-2">
<span className="text-muted-foreground shrink-0">
Localização:
Location:
</span>
<span className="font-medium text-right">
{job.location}
@ -455,7 +455,7 @@ export default function JobDetailPage({
{job.salary && (
<div className="flex items-start justify-between gap-2">
<span className="text-muted-foreground">
Salário:
Salary:
</span>
<span className="font-medium text-right whitespace-nowrap">
{job.salary}
@ -464,7 +464,7 @@ export default function JobDetailPage({
)}
<div className="flex items-start justify-between gap-2">
<span className="text-muted-foreground">
Publicado:
Posted:
</span>
<span className="font-medium text-right whitespace-nowrap">
{formatTimeAgo(job.postedAt)}
@ -483,7 +483,7 @@ export default function JobDetailPage({
>
<Card>
<CardHeader>
<CardTitle className="text-lg">Vagas similares</CardTitle>
<CardTitle className="text-lg">Similar jobs</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{mockJobs
@ -510,7 +510,7 @@ export default function JobDetailPage({
))}
<Link href="/jobs">
<Button variant="outline" size="sm" className="w-full">
Ver todas as vagas
View all jobs
</Button>
</Link>
</CardContent>

View file

@ -44,7 +44,7 @@ function JobsContent() {
setShowFilters(true) // Show filters if searching
}
if (type === "remoto") {
if (type === "remote") {
setWorkModeFilter("remote")
setShowFilters(true)
}
@ -69,7 +69,7 @@ function JobsContent() {
setJobs(mappedJobs)
}
} catch (err) {
console.error("Erro ao buscar vagas", err)
console.error("Error fetching jobs", err)
if (isMounted) {
setError(t('jobs.error'))
setJobs(mockJobs)
@ -146,6 +146,11 @@ function JobsContent() {
return filtered
}, [debouncedSearchTerm, locationFilter, typeFilter, workModeFilter, sortBy, jobs])
const getTypeLabel = (type: string) => {
const label = t(`jobs.types.${type}`)
return label !== `jobs.types.${type}` ? label : type
}
// Pagination Logic
const totalPages = Math.ceil(filteredAndSortedJobs.length / ITEMS_PER_PAGE)
const paginatedJobs = filteredAndSortedJobs.slice(
@ -256,9 +261,7 @@ function JobsContent() {
<SelectItem value="all">{t('jobs.filters.all')}</SelectItem>
{uniqueTypes.map((type) => (
<SelectItem key={type} value={type}>
{type === "full-time" ? "Tempo integral" :
type === "part-time" ? "Meio período" :
type === "contract" ? "Contrato" : type}
{getTypeLabel(type)}
</SelectItem>
))}
</SelectContent>
@ -322,7 +325,7 @@ function JobsContent() {
</span>
{hasActiveFilters && (
<div className="flex items-center gap-2">
<span>Filtros ativos:</span>
<span>Active filters:</span>
{searchTerm && (
<Badge variant="secondary" className="gap-1">
"{searchTerm}"
@ -349,9 +352,9 @@ function JobsContent() {
)}
{workModeFilter !== "all" && (
<Badge variant="secondary" className="gap-1">
{workModeFilter === "remote" ? "Remoto" :
workModeFilter === "hybrid" ? "Híbrido" :
workModeFilter === "onsite" ? "Presencial" : workModeFilter}
{workModeFilter === "remote" ? t("workMode.remote") :
workModeFilter === "hybrid" ? t("workMode.hybrid") :
workModeFilter === "onsite" ? t("workMode.onsite") : workModeFilter}
<button onClick={() => setWorkModeFilter("all")} className="ml-1">
<X className="h-3 w-3" />
</button>
@ -448,7 +451,7 @@ function JobsContent() {
)
}
export default function VagasPage() {
export default function JobsPage() {
return (
<div className="min-h-screen flex flex-col">
<Navbar />

View file

@ -111,7 +111,7 @@ export default function HomePage() {
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</Link>
<Link href="/cadastro/empresa">
<Link href="/register/company">
<Button size="lg" variant="outline" className="w-full sm:w-auto bg-transparent">
<Building2 className="mr-2 h-4 w-4" />
{t('home.hero.imCompany')}
@ -277,7 +277,7 @@ export default function HomePage() {
{t('home.cta.subtitle')}
</p>
<div>
<Link href="/cadastro/candidato">
<Link href="/register/candidate">
<Button size="lg" variant="secondary">
{t('home.cta.button')}
<ArrowRight className="ml-2 h-4 w-4" />

View file

@ -8,13 +8,13 @@ export default function PrivacyPage() {
<main className="min-h-screen flex flex-col">
<Navbar />
<div className="container mx-auto px-4 py-12 flex-1">
<h1 className="text-3xl font-bold mb-6">Política de Privacidade</h1>
<h1 className="text-3xl font-bold mb-6">Privacy Policy</h1>
<div className="prose max-w-none">
<p>
Esta Política de Privacidade descreve como coletamos, usamos e protegemos suas informações pessoais.
This Privacy Policy describes how we collect, use, and protect your personal information.
</p>
<p className="mt-4">
Em construção...
Coming soon...
</p>
</div>
</div>

View file

@ -39,11 +39,11 @@ export default function ProfilePage() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
updateProfileData(formData)
alert("✅ Perfil salvo com sucesso no banco local!")
alert("✅ Profile saved successfully to local storage!")
}
const handleClearDatabase = () => {
if (confirm("⚠️ Tem certeza que deseja limpar todos os dados? Esta ação não pode ser desfeita.")) {
if (confirm("⚠️ Are you sure you want to clear all data? This action cannot be undone.")) {
localDB.clearAllData()
window.location.reload()
}
@ -67,7 +67,7 @@ export default function ProfilePage() {
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<Database className="w-12 h-12 mx-auto mb-4 animate-pulse" />
<p>Carregando dados do banco local...</p>
<p>Loading local data...</p>
</div>
</div>
)
@ -79,7 +79,7 @@ export default function ProfilePage() {
<div className="mb-6">
<Link href="/" className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground">
<ArrowLeft className="w-4 h-4 mr-2" />
Voltar
Back
</Link>
</div>
@ -89,7 +89,7 @@ export default function ProfilePage() {
<div className="flex items-center space-x-2">
<Database className="w-5 h-5 text-green-600" />
<span className="text-sm font-medium text-green-800">
Banco Local Ativo
Local storage active
</span>
</div>
<div className="flex space-x-2">
@ -99,7 +99,7 @@ export default function ProfilePage() {
onClick={handleExportData}
className="text-xs"
>
Exportar
Export
</Button>
<Button
size="sm"
@ -108,7 +108,7 @@ export default function ProfilePage() {
className="text-xs"
>
<Trash2 className="w-3 h-3 mr-1" />
Limpar
Clear
</Button>
</div>
</div>
@ -118,10 +118,10 @@ export default function ProfilePage() {
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold text-center">
Editar Perfil
Edit profile
</CardTitle>
<p className="text-center text-sm text-muted-foreground">
Seus dados são salvos automaticamente no navegador
Your data is saved automatically in your browser
</p>
</CardHeader>
<CardContent className="space-y-6">
@ -136,13 +136,13 @@ export default function ProfilePage() {
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="name">Nome completo</Label>
<Label htmlFor="name">Full name</Label>
<Input
id="name"
type="text"
value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)}
placeholder="Seu nome completo"
placeholder="Your full name"
/>
</div>
<div>
@ -152,13 +152,13 @@ export default function ProfilePage() {
type="email"
value={formData.email}
onChange={(e) => handleInputChange("email", e.target.value)}
placeholder="seu@email.com"
placeholder="you@email.com"
/>
</div>
</div>
<div>
<Label htmlFor="phone">Telefone</Label>
<Label htmlFor="phone">Phone</Label>
<Input
id="phone"
type="tel"
@ -169,19 +169,19 @@ export default function ProfilePage() {
</div>
<div>
<Label htmlFor="bio">Biografia</Label>
<Label htmlFor="bio">Bio</Label>
<Textarea
id="bio"
value={formData.bio}
onChange={(e) => handleInputChange("bio", e.target.value)}
placeholder="Conte um pouco sobre você..."
placeholder="Tell us a bit about yourself..."
rows={4}
/>
</div>
<Button type="submit" className="w-full" size="lg">
<Save className="w-4 h-4 mr-2" />
Salvar Perfil
Save profile
</Button>
</form>
</CardContent>
@ -190,13 +190,13 @@ export default function ProfilePage() {
{profileData && (
<Card className="mt-8">
<CardHeader>
<CardTitle className="text-lg">Dados Salvos no Banco Local</CardTitle>
<CardTitle className="text-lg">Saved local data</CardTitle>
</CardHeader>
<CardContent>
<pre className="text-xs bg-gray-100 p-4 rounded overflow-auto">
{JSON.stringify({
...profileData,
profileImage: profileData.profileImage ? "✅ Imagem salva" : "❌ Sem imagem"
profileImage: profileData.profileImage ? "✅ Image saved" : "❌ No image"
}, null, 2)}
</pre>
</CardContent>
@ -205,4 +205,4 @@ export default function ProfilePage() {
</div>
</div>
)
}
}

View file

@ -21,7 +21,7 @@ export default function ProfilePage() {
bio: ""
})
// Sincronizar dados do perfil com o formulário
// Sync profile data with the form
useEffect(() => {
if (profileData) {
setFormData({
@ -40,14 +40,14 @@ export default function ProfilePage() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
// Salvar dados no banco local
// Save data to local storage
updateProfileData(formData)
alert("✅ Perfil salvo com sucesso no banco local!")
alert("✅ Profile saved successfully to local storage!")
}
const handleClearDatabase = () => {
if (confirm("⚠️ Tem certeza que deseja limpar todos os dados? Esta ação não pode ser desfeita.")) {
if (confirm("⚠️ Are you sure you want to clear all data? This action cannot be undone.")) {
localDB.clearAllData()
window.location.reload()
}
@ -71,7 +71,7 @@ export default function ProfilePage() {
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<Database className="w-12 h-12 mx-auto mb-4 animate-pulse" />
<p>Carregando dados do banco local...</p>
<p>Loading local data...</p>
</div>
</div>
)
@ -83,18 +83,18 @@ export default function ProfilePage() {
<div className="mb-6">
<Link href="/" className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground">
<ArrowLeft className="w-4 h-4 mr-2" />
Voltar
Back
</Link>
</div>
{/* Status do Banco de Dados */}
{/* Local storage status */}
<Card className="mb-6 bg-green-50 border-green-200">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Database className="w-5 h-5 text-green-600" />
<span className="text-sm font-medium text-green-800">
Banco Local Ativo
Local storage active
</span>
</div>
<div className="flex space-x-2">
@ -104,7 +104,7 @@ export default function ProfilePage() {
onClick={handleExportData}
className="text-xs"
>
Exportar
Export
</Button>
<Button
size="sm"
@ -113,7 +113,7 @@ export default function ProfilePage() {
className="text-xs"
>
<Trash2 className="w-3 h-3 mr-1" />
Limpar
Clear
</Button>
</div>
</div>
@ -123,14 +123,14 @@ export default function ProfilePage() {
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold text-center">
Editar Perfil
Edit profile
</CardTitle>
<p className="text-center text-sm text-muted-foreground">
Seus dados são salvos automaticamente no navegador
Your data is saved automatically in your browser
</p>
</CardHeader>
<CardContent className="space-y-6">
{/* Upload de foto de perfil com banco de dados */}
{/* Profile photo upload with local data */}
<div className="flex justify-center">
<ProfilePictureUpload
fallbackText={formData.name ? formData.name.charAt(0).toUpperCase() : "U"}
@ -139,17 +139,17 @@ export default function ProfilePage() {
/>
</div>
{/* Formulário */}
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="name">Nome completo</Label>
<Label htmlFor="name">Full name</Label>
<Input
id="name"
type="text"
value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)}
placeholder="Seu nome completo"
placeholder="Your full name"
/>
</div>
<div>
@ -159,13 +159,13 @@ export default function ProfilePage() {
type="email"
value={formData.email}
onChange={(e) => handleInputChange("email", e.target.value)}
placeholder="seu@email.com"
placeholder="you@email.com"
/>
</div>
</div>
<div>
<Label htmlFor="phone">Telefone</Label>
<Label htmlFor="phone">Phone</Label>
<Input
id="phone"
type="tel"
@ -176,35 +176,35 @@ export default function ProfilePage() {
</div>
<div>
<Label htmlFor="bio">Biografia</Label>
<Label htmlFor="bio">Bio</Label>
<Textarea
id="bio"
value={formData.bio}
onChange={(e) => handleInputChange("bio", e.target.value)}
placeholder="Conte um pouco sobre você..."
placeholder="Tell us a bit about yourself..."
rows={4}
/>
</div>
<Button type="submit" className="w-full" size="lg">
<Save className="w-4 h-4 mr-2" />
Salvar Perfil
Save profile
</Button>
</form>
</CardContent>
</Card>
{/* Debug: Dados salvos */}
{/* Debug: Saved data */}
{profileData && (
<Card className="mt-8">
<CardHeader>
<CardTitle className="text-lg">Dados Salvos no Banco Local</CardTitle>
<CardTitle className="text-lg">Saved local data</CardTitle>
</CardHeader>
<CardContent>
<pre className="text-xs bg-gray-100 p-4 rounded overflow-auto">
{JSON.stringify({
...profileData,
profileImage: profileData.profileImage ? "✅ Imagem salva" : "❌ Sem imagem"
profileImage: profileData.profileImage ? "✅ Image saved" : "❌ No image"
}, null, 2)}
</pre>
</CardContent>
@ -213,4 +213,4 @@ export default function ProfilePage() {
</div>
</div>
)
}
}

View file

@ -540,11 +540,11 @@ export default function CandidateRegisterPage() {
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{t("register.candidate.acceptTerms.prefix")}{" "}
<Link href="/termos" className="text-primary hover:underline">
<Link href="/terms" className="text-primary hover:underline">
{t("register.candidate.acceptTerms.terms")}
</Link>{" "}
{t("register.candidate.acceptTerms.and")}{" "}
<Link href="/privacidade" className="text-primary hover:underline">
<Link href="/privacy" className="text-primary hover:underline">
{t("register.candidate.acceptTerms.privacy")}
</Link>
</label>

View file

@ -43,26 +43,26 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const companySchema = z.object({
companyName: z.string().min(2, "Nome da empresa deve ter pelo menos 2 caracteres"),
cnpj: z.string().min(14, "CNPJ deve ter 14 dígitos"),
email: z.string().email("Email inválido"),
password: z.string().min(6, "Senha deve ter pelo menos 6 caracteres"),
companyName: z.string().min(2, "Company name must be at least 2 characters"),
cnpj: z.string().min(14, "CNPJ must have 14 digits"),
email: z.string().email("Invalid email"),
password: z.string().min(6, "Password must be at least 6 characters"),
confirmPassword: z.string(),
phone: z.string().min(10, "Telefone deve ter pelo menos 10 dígitos"),
website: z.string().url("Website deve ser uma URL válida").optional().or(z.literal("")),
address: z.string().min(5, "Endereço deve ter pelo menos 5 caracteres"),
city: z.string().min(2, "Cidade é obrigatória"),
state: z.string().min(2, "Estado é obrigatório"),
zipCode: z.string().min(8, "CEP deve ter 8 dígitos"),
sector: z.string().min(1, "Setor de atuação é obrigatório"),
companySize: z.string().min(1, "Tamanho da empresa é obrigatório"),
description: z.string().min(20, "Descrição deve ter pelo menos 20 caracteres"),
contactPerson: z.string().min(2, "Nome do responsável é obrigatório"),
contactRole: z.string().min(2, "Cargo do responsável é obrigatório"),
acceptTerms: z.boolean().refine(val => val === true, "Você deve aceitar os termos"),
phone: z.string().min(10, "Phone must have at least 10 digits"),
website: z.string().url("Website must be a valid URL").optional().or(z.literal("")),
address: z.string().min(5, "Address must be at least 5 characters"),
city: z.string().min(2, "City is required"),
state: z.string().min(2, "State is required"),
zipCode: z.string().min(8, "ZIP code must have 8 digits"),
sector: z.string().min(1, "Industry is required"),
companySize: z.string().min(1, "Company size is required"),
description: z.string().min(20, "Description must be at least 20 characters"),
contactPerson: z.string().min(2, "Contact name is required"),
contactRole: z.string().min(2, "Contact role is required"),
acceptTerms: z.boolean().refine(val => val === true, "You must accept the terms"),
acceptNewsletter: z.boolean().optional(),
}).refine(data => data.password === data.confirmPassword, {
message: "Senhas não coincidem",
message: "Passwords do not match",
path: ["confirmPassword"],
});
@ -91,14 +91,14 @@ export default function CompanyRegisterPage() {
const onSubmit = async (data: CompanyFormData) => {
setLoading(true);
try {
// Simular cadastro
// Simulate registration
await new Promise(resolve => setTimeout(resolve, 2000));
console.log("Dados da empresa:", data);
console.log("Company data:", data);
// Redirecionar para login após cadastro
router.push("/login?message=Cadastro realizado com sucesso! Faça login para continuar.");
// Redirect to login after registration
router.push("/login?message=Registration completed successfully! Please sign in to continue.");
} catch (error) {
console.error("Erro no cadastro:", error);
console.error("Registration error:", error);
} finally {
setLoading(false);
}
@ -120,7 +120,7 @@ export default function CompanyRegisterPage() {
return (
<div className="min-h-screen bg-gradient-to-br from-background to-muted/20 flex">
{/* Left Panel - Informações */}
{/* Left Panel - Information */}
<div className="hidden lg:flex lg:flex-1 bg-gradient-to-br from-primary to-primary/80 p-8 flex-col justify-center items-center text-primary-foreground">
<motion.div
initial={{ opacity: 0, y: 20 }}
@ -132,36 +132,36 @@ export default function CompanyRegisterPage() {
</div>
<h1 className="text-4xl font-bold mb-4">
Cadastre sua Empresa
Register your company
</h1>
<p className="text-lg opacity-90 leading-relaxed mb-6">
Encontre os melhores talentos para sua empresa.
Publique vagas e conecte-se com candidatos qualificados.
Find top talent for your company.
Post jobs and connect with qualified candidates.
</p>
<div className="space-y-4 text-left">
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-white rounded-full"></div>
<span>Publique vagas gratuitamente</span>
<span>Post jobs for free</span>
</div>
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-white rounded-full"></div>
<span>Acesso a milhares de candidatos</span>
<span>Access thousands of candidates</span>
</div>
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-white rounded-full"></div>
<span>Ferramentas de gestão de candidaturas</span>
<span>Application management tools</span>
</div>
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-white rounded-full"></div>
<span>Dashboard completo de recrutamento</span>
<span>Complete recruiting dashboard</span>
</div>
</div>
</motion.div>
</div>
{/* Right Panel - Formulário */}
{/* Right Panel - Form */}
<div className="flex-1 p-8 flex flex-col justify-center">
<div className="w-full max-w-md mx-auto">
{/* Header */}
@ -171,25 +171,25 @@ export default function CompanyRegisterPage() {
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground mb-4 transition-colors"
>
<ArrowLeft className="w-4 h-4" />
Voltar ao Login
Back to login
</Link>
<h2 className="text-2xl font-bold text-foreground mb-2">
Criar Conta - Empresa
Create account - company
</h2>
<p className="text-muted-foreground">
Preencha os dados da sua empresa
Fill in your company details
</p>
</div>
{/* Progress Indicator */}
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium">Etapa {currentStep} de 3</span>
<span className="text-sm font-medium">Step {currentStep} of 3</span>
<span className="text-sm text-muted-foreground">
{currentStep === 1 && "Dados da Empresa"}
{currentStep === 2 && "Endereço e Contato"}
{currentStep === 3 && "Informações Adicionais"}
{currentStep === 1 && "Company details"}
{currentStep === 2 && "Address & contact"}
{currentStep === 3 && "Additional information"}
</span>
</div>
<div className="w-full bg-muted rounded-full h-2">
@ -201,7 +201,7 @@ export default function CompanyRegisterPage() {
</div>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
{/* Step 1: Dados da Empresa */}
{/* Step 1: Company details */}
{currentStep === 1 && (
<motion.div
key="step1"
@ -212,13 +212,13 @@ export default function CompanyRegisterPage() {
className="space-y-4"
>
<div className="space-y-2">
<Label htmlFor="companyName">Nome da Empresa</Label>
<Label htmlFor="companyName">Company name</Label>
<div className="relative">
<Building2 className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="companyName"
type="text"
placeholder="Nome da sua empresa"
placeholder="Your company name"
className="pl-10"
{...register("companyName")}
/>
@ -246,13 +246,13 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="email">Email Corporativo</Label>
<Label htmlFor="email">Company email</Label>
<div className="relative">
<Mail className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="email"
type="email"
placeholder="contato@empresa.com"
placeholder="hello@company.com"
className="pl-10"
{...register("email")}
/>
@ -263,13 +263,13 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="password">Senha</Label>
<Label htmlFor="password">Password</Label>
<div className="relative">
<Lock className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="password"
type={showPassword ? "text" : "password"}
placeholder="Sua senha"
placeholder="Your password"
className="pl-10 pr-10"
{...register("password")}
/>
@ -293,13 +293,13 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword">Confirmar Senha</Label>
<Label htmlFor="confirmPassword">Confirm password</Label>
<div className="relative">
<Lock className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="confirmPassword"
type={showConfirmPassword ? "text" : "password"}
placeholder="Confirme sua senha"
placeholder="Confirm your password"
className="pl-10 pr-10"
{...register("confirmPassword")}
/>
@ -323,12 +323,12 @@ export default function CompanyRegisterPage() {
</div>
<Button type="button" onClick={nextStep} className="w-full">
Próxima Etapa
Next step
</Button>
</motion.div>
)}
{/* Step 2: Endereço e Contato */}
{/* Step 2: Address & contact */}
{currentStep === 2 && (
<motion.div
key="step2"
@ -339,7 +339,7 @@ export default function CompanyRegisterPage() {
className="space-y-4"
>
<div className="space-y-2">
<Label htmlFor="phone">Telefone</Label>
<Label htmlFor="phone">Phone</Label>
<div className="relative">
<Phone className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
@ -356,13 +356,13 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="website">Website (opcional)</Label>
<Label htmlFor="website">Website (optional)</Label>
<div className="relative">
<Globe className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="website"
type="url"
placeholder="https://www.empresa.com"
placeholder="https://www.company.com"
className="pl-10"
{...register("website")}
/>
@ -373,13 +373,13 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="address">Endereço</Label>
<Label htmlFor="address">Address</Label>
<div className="relative">
<MapPin className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Input
id="address"
type="text"
placeholder="Rua, número, complemento"
placeholder="Street, number, suite"
className="pl-10"
{...register("address")}
/>
@ -391,7 +391,7 @@ export default function CompanyRegisterPage() {
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="city">Cidade</Label>
<Label htmlFor="city">City</Label>
<Input
id="city"
type="text"
@ -404,10 +404,10 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="state">Estado</Label>
<Label htmlFor="state">State</Label>
<Select onValueChange={(value) => setValue("state", value)}>
<SelectTrigger>
<SelectValue placeholder="Estado" />
<SelectValue placeholder="State" />
</SelectTrigger>
<SelectContent>
<SelectItem value="AC">Acre</SelectItem>
@ -446,7 +446,7 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="zipCode">CEP</Label>
<Label htmlFor="zipCode">ZIP code</Label>
<Input
id="zipCode"
type="text"
@ -460,16 +460,16 @@ export default function CompanyRegisterPage() {
<div className="flex gap-4">
<Button type="button" variant="outline" onClick={prevStep} className="flex-1">
Voltar
Back
</Button>
<Button type="button" onClick={nextStep} className="flex-1">
Próxima Etapa
Next step
</Button>
</div>
</motion.div>
)}
{/* Step 3: Informações Adicionais */}
{/* Step 3: Additional information */}
{currentStep === 3 && (
<motion.div
key="step3"
@ -480,26 +480,26 @@ export default function CompanyRegisterPage() {
className="space-y-4"
>
<div className="space-y-2">
<Label htmlFor="sector">Setor de Atuação</Label>
<Label htmlFor="sector">Industry</Label>
<Select onValueChange={(value) => setValue("sector", value)}>
<SelectTrigger>
<SelectValue placeholder="Selecione o setor" />
<SelectValue placeholder="Select an industry" />
</SelectTrigger>
<SelectContent>
<SelectItem value="tecnologia">Tecnologia</SelectItem>
<SelectItem value="financeiro">Financeiro</SelectItem>
<SelectItem value="saude">Saúde</SelectItem>
<SelectItem value="educacao">Educação</SelectItem>
<SelectItem value="varejo">Varejo</SelectItem>
<SelectItem value="construcao">Construção</SelectItem>
<SelectItem value="industria">Indústria</SelectItem>
<SelectItem value="servicos">Serviços</SelectItem>
<SelectItem value="agricultura">Agricultura</SelectItem>
<SelectItem value="transporte">Transporte</SelectItem>
<SelectItem value="energia">Energia</SelectItem>
<SelectItem value="consultoria">Consultoria</SelectItem>
<SelectItem value="technology">Technology</SelectItem>
<SelectItem value="finance">Finance</SelectItem>
<SelectItem value="healthcare">Healthcare</SelectItem>
<SelectItem value="education">Education</SelectItem>
<SelectItem value="retail">Retail</SelectItem>
<SelectItem value="construction">Construction</SelectItem>
<SelectItem value="industry">Industry</SelectItem>
<SelectItem value="services">Services</SelectItem>
<SelectItem value="agriculture">Agriculture</SelectItem>
<SelectItem value="transport">Transportation</SelectItem>
<SelectItem value="energy">Energy</SelectItem>
<SelectItem value="consulting">Consulting</SelectItem>
<SelectItem value="marketing">Marketing</SelectItem>
<SelectItem value="outros">Outros</SelectItem>
<SelectItem value="other">Other</SelectItem>
</SelectContent>
</Select>
{errors.sector && (
@ -508,18 +508,18 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="companySize">Tamanho da Empresa</Label>
<Label htmlFor="companySize">Company size</Label>
<Select onValueChange={(value) => setValue("companySize", value)}>
<SelectTrigger>
<SelectValue placeholder="Número de funcionários" />
<SelectValue placeholder="Number of employees" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1-10">1 a 10 funcionários</SelectItem>
<SelectItem value="11-50">11 a 50 funcionários</SelectItem>
<SelectItem value="51-200">51 a 200 funcionários</SelectItem>
<SelectItem value="201-500">201 a 500 funcionários</SelectItem>
<SelectItem value="501-1000">501 a 1000 funcionários</SelectItem>
<SelectItem value="1000+">Mais de 1000 funcionários</SelectItem>
<SelectItem value="1-10">1 to 10 employees</SelectItem>
<SelectItem value="11-50">11 to 50 employees</SelectItem>
<SelectItem value="51-200">51 to 200 employees</SelectItem>
<SelectItem value="201-500">201 to 500 employees</SelectItem>
<SelectItem value="501-1000">501 to 1000 employees</SelectItem>
<SelectItem value="1000+">More than 1000 employees</SelectItem>
</SelectContent>
</Select>
{errors.companySize && (
@ -528,10 +528,10 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="description">Descrição da Empresa</Label>
<Label htmlFor="description">Company description</Label>
<Textarea
id="description"
placeholder="Descreva sua empresa, cultura, valores e o que oferece aos funcionários..."
placeholder="Describe your company, culture, values, and what you offer employees..."
className="min-h-[100px]"
{...register("description")}
/>
@ -542,11 +542,11 @@ export default function CompanyRegisterPage() {
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="contactPerson">Nome do Responsável</Label>
<Label htmlFor="contactPerson">Contact name</Label>
<Input
id="contactPerson"
type="text"
placeholder="Nome completo"
placeholder="Full name"
{...register("contactPerson")}
/>
{errors.contactPerson && (
@ -555,11 +555,11 @@ export default function CompanyRegisterPage() {
</div>
<div className="space-y-2">
<Label htmlFor="contactRole">Cargo</Label>
<Label htmlFor="contactRole">Role</Label>
<Input
id="contactRole"
type="text"
placeholder="Ex: RH, Gerente"
placeholder="e.g. HR, Manager"
{...register("contactRole")}
/>
{errors.contactRole && (
@ -580,13 +580,13 @@ export default function CompanyRegisterPage() {
htmlFor="acceptTerms"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Aceito os{" "}
<Link href="/termos" className="text-primary hover:underline">
Termos de Uso
I accept the{" "}
<Link href="/terms" className="text-primary hover:underline">
Terms of Use
</Link>{" "}
e{" "}
<Link href="/privacidade" className="text-primary hover:underline">
Política de Privacidade
and{" "}
<Link href="/privacy" className="text-primary hover:underline">
Privacy Policy
</Link>
</label>
</div>
@ -606,7 +606,7 @@ export default function CompanyRegisterPage() {
htmlFor="acceptNewsletter"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Quero receber dicas de recrutamento e novidades por email
I want to receive recruiting tips and updates by email
</label>
</div>
</div>
@ -614,10 +614,10 @@ export default function CompanyRegisterPage() {
<div className="flex gap-4">
<Button type="button" variant="outline" onClick={prevStep} className="flex-1">
Voltar
Back
</Button>
<Button type="submit" disabled={loading} className="flex-1">
{loading ? "Criando conta..." : "Criar Conta"}
{loading ? "Creating account..." : "Create account"}
</Button>
</div>
</motion.div>
@ -626,9 +626,9 @@ export default function CompanyRegisterPage() {
<div className="mt-6 text-center">
<p className="text-sm text-muted-foreground">
tem uma conta?{" "}
Already have an account?{" "}
<Link href="/login" className="text-primary hover:underline font-medium">
Faça login
Sign in
</Link>
</p>
</div>
@ -636,4 +636,4 @@ export default function CompanyRegisterPage() {
</div>
</div>
);
}
}

View file

@ -8,13 +8,13 @@ export default function TermsPage() {
<main className="min-h-screen flex flex-col">
<Navbar />
<div className="container mx-auto px-4 py-12 flex-1">
<h1 className="text-3xl font-bold mb-6">Termos de Uso</h1>
<h1 className="text-3xl font-bold mb-6">Terms of Use</h1>
<div className="prose max-w-none">
<p>
Ao utilizar nosso serviço, você concorda com estes termos.
By using our service, you agree to these terms.
</p>
<p className="mt-4">
Em construção...
Coming soon...
</p>
</div>
</div>

View file

@ -1,10 +0,0 @@
import { redirect } from "next/navigation";
export default async function VagasJobApplicationRedirectPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
redirect(`/jobs/${id}/candidatura`);
}

View file

@ -1,10 +0,0 @@
import { redirect } from "next/navigation";
export default async function VagasJobRedirectPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
redirect(`/jobs/${id}`);
}

View file

@ -1,5 +0,0 @@
import { redirect } from "next/navigation";
export default function VagasRedirectPage() {
redirect("/jobs");
}

View file

@ -28,44 +28,44 @@ export function CompanySidebar({ className }: CompanySidebarProps) {
{
label: "Dashboard",
icon: LayoutDashboard,
href: "/dashboard/empresa",
active: pathname === "/dashboard/empresa",
href: "/dashboard/company",
active: pathname === "/dashboard/company",
},
{
label: "Minhas Vagas",
label: "My jobs",
icon: Briefcase,
href: "/dashboard/empresa/vagas",
active: pathname?.startsWith("/dashboard/empresa/vagas"),
href: "/dashboard/company/jobs",
active: pathname?.startsWith("/dashboard/company/jobs"),
},
{
label: "Candidaturas",
label: "Applications",
icon: Users,
href: "/dashboard/empresa/candidaturas",
active: pathname?.startsWith("/dashboard/empresa/candidaturas"),
href: "/dashboard/company/applications",
active: pathname?.startsWith("/dashboard/company/applications"),
},
{
label: "Mensagens",
label: "Messages",
icon: MessageSquare,
href: "/dashboard/empresa/mensagens",
active: pathname?.startsWith("/dashboard/empresa/mensagens"),
href: "/dashboard/company/messages",
active: pathname?.startsWith("/dashboard/company/messages"),
},
{
label: "Relatórios",
label: "Reports",
icon: BarChart3,
href: "/dashboard/empresa/relatorios",
active: pathname?.startsWith("/dashboard/empresa/relatorios"),
href: "/dashboard/company/reports",
active: pathname?.startsWith("/dashboard/company/reports"),
},
{
label: "Perfil da Empresa",
label: "Company profile",
icon: Building2,
href: "/dashboard/empresa/perfil",
active: pathname?.startsWith("/dashboard/empresa/perfil"),
href: "/dashboard/company/profile",
active: pathname?.startsWith("/dashboard/company/profile"),
},
{
label: "Configurações",
label: "Settings",
icon: Settings,
href: "/dashboard/empresa/configuracoes",
active: pathname?.startsWith("/dashboard/empresa/configuracoes"),
href: "/dashboard/company/settings",
active: pathname?.startsWith("/dashboard/company/settings"),
},
];
@ -82,14 +82,14 @@ export function CompanySidebar({ className }: CompanySidebarProps) {
</Avatar>
<div className="flex-1 min-w-0">
<h2 className="text-lg font-semibold truncate">TechCorp</h2>
<p className="text-xs text-muted-foreground truncate">Empresa</p>
<p className="text-xs text-muted-foreground truncate">Company</p>
</div>
</div>
<Link href="/dashboard/empresa/vagas/nova">
<Link href="/dashboard/company/jobs/new">
<Button className="w-full mb-4" size="lg">
<Plus className="h-4 w-4 mr-2" />
Nova Vaga
New job
</Button>
</Link>

View file

@ -11,10 +11,10 @@ import { Briefcase, Users, TrendingUp, FileText, Plus, MoreHorizontal } from "lu
import { motion } from "framer-motion"
const mockCandidates = [
{ id: "1", name: "João Silva", position: "Desenvolvedor Full Stack", status: "active" },
{ id: "2", name: "Maria Santos", position: "Designer UX/UI", status: "active" },
{ id: "1", name: "João Silva", position: "Full Stack Developer", status: "active" },
{ id: "2", name: "Maria Santos", position: "UX/UI Designer", status: "active" },
{ id: "3", name: "Carlos Oliveira", position: "Product Manager", status: "pending" },
{ id: "4", name: "Ana Costa", position: "Engenheiro de Dados", status: "active" },
{ id: "4", name: "Ana Costa", position: "Data Engineer", status: "active" },
{ id: "5", name: "Pedro Alves", position: "DevOps Engineer", status: "inactive" },
]
@ -30,7 +30,7 @@ export function AdminDashboardContent() {
transition={{ duration: 0.5 }}
>
<h1 className="text-3xl font-bold mb-2">Dashboard</h1>
<p className="text-muted-foreground">Visão geral do portal de empregos</p>
<p className="text-muted-foreground">Overview of the jobs portal</p>
</motion.div>
{/* Stats */}
@ -41,24 +41,24 @@ export function AdminDashboardContent() {
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
>
<StatsCard
title="Vagas Ativas"
title="Active jobs"
value={mockStats.activeJobs}
icon={Briefcase}
description="Total de vagas publicadas"
description="Total posted jobs"
/>
<StatsCard
title="Total de Candidatos"
title="Total candidates"
value={mockStats.totalCandidates}
icon={Users}
description="Usuários cadastrados"
description="Registered users"
/>
<StatsCard
title="Novas Candidaturas"
title="New applications"
value={mockStats.newApplications}
icon={FileText}
description="Últimos 7 dias"
description="Last 7 days"
/>
<StatsCard title="Taxa de Conversão" value="12.5%" icon={TrendingUp} description="Candidaturas por vaga" />
<StatsCard title="Conversion rate" value="12.5%" icon={TrendingUp} description="Applications per job" />
</motion.div>
{/* Jobs Management */}
@ -69,22 +69,22 @@ export function AdminDashboardContent() {
>
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>Gestão de Vagas</CardTitle>
<CardTitle>Job management</CardTitle>
<Button onClick={() => router.push('/dashboard/jobs')}>
<Plus className="mr-2 h-4 w-4" />
Adicionar Vaga
Add job
</Button>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Título</TableHead>
<TableHead>Empresa</TableHead>
<TableHead>Localização</TableHead>
<TableHead>Title</TableHead>
<TableHead>Company</TableHead>
<TableHead>Location</TableHead>
<TableHead>Status</TableHead>
<TableHead>Candidaturas</TableHead>
<TableHead className="text-right">Ações</TableHead>
<TableHead>Applications</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -92,11 +92,11 @@ export function AdminDashboardContent() {
<TableRow key={job.id}>
<TableCell className="font-medium">{job.title}</TableCell>
<TableCell>{job.company}</TableCell>
<TableCell>{job.location}</TableCell>
<TableCell>
<Badge variant="secondary">Ativa</Badge>
</TableCell>
<TableCell>{((job.id.charCodeAt(0) * 7) % 50) + 10}</TableCell>
<TableCell>{job.location}</TableCell>
<TableCell>
<Badge variant="secondary">Active</Badge>
</TableCell>
<TableCell>{((job.id.charCodeAt(0) * 7) % 50) + 10}</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="icon">
<MoreHorizontal className="h-4 w-4" />
@ -118,16 +118,16 @@ export function AdminDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>Gestão de Candidatos</CardTitle>
<CardTitle>Candidate management</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Nome</TableHead>
<TableHead>Cargo Pretendido</TableHead>
<TableHead>Name</TableHead>
<TableHead>Desired role</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Ações</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -136,9 +136,9 @@ export function AdminDashboardContent() {
<TableCell className="font-medium">{candidate.name}</TableCell>
<TableCell>{candidate.position}</TableCell>
<TableCell>
{candidate.status === "active" && <Badge className="bg-green-500">Ativo</Badge>}
{candidate.status === "pending" && <Badge variant="secondary">Pendente</Badge>}
{candidate.status === "inactive" && <Badge variant="outline">Inativo</Badge>}
{candidate.status === "active" && <Badge className="bg-green-500">Active</Badge>}
{candidate.status === "pending" && <Badge variant="secondary">Pending</Badge>}
{candidate.status === "inactive" && <Badge variant="outline">Inactive</Badge>}
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="icon">

View file

@ -37,35 +37,35 @@ export function CandidateDashboardContent() {
return (
<Badge variant="secondary">
<Clock className="h-3 w-3 mr-1" />
Em análise
Under review
</Badge>
)
case "reviewing":
return (
<Badge variant="secondary">
<AlertCircle className="h-3 w-3 mr-1" />
Em análise
Under review
</Badge>
)
case "interview":
return (
<Badge className="bg-blue-500 hover:bg-blue-600">
<CheckCircle className="h-3 w-3 mr-1" />
Entrevista
Interview
</Badge>
)
case "accepted":
return (
<Badge className="bg-green-500 hover:bg-green-600">
<CheckCircle className="h-3 w-3 mr-1" />
Aprovado
Accepted
</Badge>
)
case "rejected":
return (
<Badge variant="destructive">
<XCircle className="h-3 w-3 mr-1" />
Rejeitado
Rejected
</Badge>
)
default:
@ -85,12 +85,12 @@ export function CandidateDashboardContent() {
<CardContent className="pt-6">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
<div className="flex-1">
<h1 className="text-2xl font-bold mb-2">Olá, {user?.name || "Candidato"}!</h1>
<p className="text-muted-foreground">{user?.area || "Desenvolvimento"}</p>
<h1 className="text-2xl font-bold mb-2">Hi, {user?.name || "Candidate"}!</h1>
<p className="text-muted-foreground">{user?.area || "Engineering"}</p>
</div>
<Button className="cursor-pointer">
<Edit className="mr-2 h-4 w-4" />
Editar Perfil
Edit profile
</Button>
</div>
</CardContent>
@ -105,26 +105,26 @@ export function CandidateDashboardContent() {
className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"
>
<StatsCard
title="Candidaturas"
title="Applications"
value={mockApplications.length}
icon={FileText}
description="Total de vagas aplicadas"
description="Total jobs applied to"
/>
<StatsCard
title="Em processo"
title="In progress"
value={
mockApplications.filter(
(a) => a.status === "reviewing" || a.status === "interview"
).length
}
icon={Clock}
description="Aguardando resposta"
description="Awaiting a response"
/>
<StatsCard
title="Notificações"
title="Notifications"
value={unreadNotifications.length}
icon={Bell}
description="Novas atualizações"
description="New updates"
/>
</motion.div>
@ -139,7 +139,7 @@ export function CandidateDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>Vagas recomendadas</CardTitle>
<CardTitle>Recommended jobs</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{recommendedJobs.map((job) => (
@ -157,16 +157,16 @@ export function CandidateDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>Minhas candidaturas</CardTitle>
<CardTitle>My applications</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Vaga</TableHead>
<TableHead>Empresa</TableHead>
<TableHead>Role</TableHead>
<TableHead>Company</TableHead>
<TableHead>Status</TableHead>
<TableHead>Data</TableHead>
<TableHead>Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -181,7 +181,7 @@ export function CandidateDashboardContent() {
</TableCell>
<TableCell className="text-muted-foreground">
{new Date(application.appliedAt).toLocaleDateString(
"pt-BR"
"en-US"
)}
</TableCell>
</TableRow>

View file

@ -36,35 +36,35 @@ export function CompanyDashboardContent() {
const recentJobs = [
{
id: "1",
title: "Desenvolvedor Full Stack Sênior",
type: "Tempo Integral",
title: "Senior Full Stack Developer",
type: "Full Time",
location: "São Paulo, SP",
salary: "R$ 12.000 - R$ 18.000",
salary: "R$ 12,000 - R$ 18,000",
applications: 45,
views: 320,
postedAt: "2 dias atrás",
postedAt: "2 days ago",
status: "active",
},
{
id: "2",
title: "Designer UX/UI",
type: "Remoto",
location: "Remoto",
salary: "R$ 8.000 - R$ 12.000",
type: "Remote",
location: "Remote",
salary: "R$ 8,000 - R$ 12,000",
applications: 32,
views: 256,
postedAt: "5 dias atrás",
postedAt: "5 days ago",
status: "active",
},
{
id: "3",
title: "Product Manager",
type: "Tempo Integral",
type: "Full Time",
location: "São Paulo, SP",
salary: "R$ 15.000 - R$ 20.000",
salary: "R$ 15,000 - R$ 20,000",
applications: 28,
views: 189,
postedAt: "1 semana atrás",
postedAt: "1 week ago",
status: "active",
},
]
@ -74,8 +74,8 @@ export function CompanyDashboardContent() {
id: "1",
candidateName: "Ana Silva",
candidateAvatar: "",
jobTitle: "Desenvolvedor Full Stack Sênior",
appliedAt: "Há 2 horas",
jobTitle: "Senior Full Stack Developer",
appliedAt: "2 hours ago",
status: "pending",
},
{
@ -83,7 +83,7 @@ export function CompanyDashboardContent() {
candidateName: "Carlos Santos",
candidateAvatar: "",
jobTitle: "Designer UX/UI",
appliedAt: "Há 5 horas",
appliedAt: "5 hours ago",
status: "pending",
},
{
@ -91,7 +91,7 @@ export function CompanyDashboardContent() {
candidateName: "Maria Oliveira",
candidateAvatar: "",
jobTitle: "Product Manager",
appliedAt: "Há 1 dia",
appliedAt: "1 day ago",
status: "reviewing",
},
]
@ -112,13 +112,13 @@ export function CompanyDashboardContent() {
Dashboard
</h1>
<p className="text-muted-foreground">
Bem-vindo de volta, TechCorp! 👋
Welcome back, TechCorp! 👋
</p>
</div>
<Link href="/dashboard/my-jobs">
<Button size="lg" className="w-full sm:w-auto">
<Plus className="h-5 w-5 mr-2" />
Nova Vaga
New job
</Button>
</Link>
</div>
@ -128,7 +128,7 @@ export function CompanyDashboardContent() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Vagas Ativas
Active jobs
</CardTitle>
<Briefcase className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -137,7 +137,7 @@ export function CompanyDashboardContent() {
{companyStats.activeJobs}
</div>
<p className="text-xs text-muted-foreground mt-1">
Publicadas no momento
Live right now
</p>
</CardContent>
</Card>
@ -145,7 +145,7 @@ export function CompanyDashboardContent() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Candidaturas
Applications
</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -154,7 +154,7 @@ export function CompanyDashboardContent() {
{companyStats.totalApplications}
</div>
<p className="text-xs text-muted-foreground mt-1">
+{companyStats.thisMonth} este mês
+{companyStats.thisMonth} this month
</p>
</CardContent>
</Card>
@ -162,7 +162,7 @@ export function CompanyDashboardContent() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Visualizações
Views
</CardTitle>
<Eye className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -171,7 +171,7 @@ export function CompanyDashboardContent() {
{companyStats.totalViews}
</div>
<p className="text-xs text-muted-foreground mt-1">
Nas suas vagas
On your postings
</p>
</CardContent>
</Card>
@ -179,34 +179,34 @@ export function CompanyDashboardContent() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Taxa de Conversão
Conversion rate
</CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">15.2%</div>
<p className="text-xs text-green-600 mt-1">
+2.5% vs mês passado
+2.5% vs last month
</p>
</CardContent>
</Card>
</div>
<div className="grid lg:grid-cols-3 gap-6">
{/* Vagas Recentes */}
{/* Recent jobs */}
<div className="lg:col-span-2">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Vagas Recentes</CardTitle>
<CardTitle>Recent jobs</CardTitle>
<CardDescription>
Suas últimas vagas publicadas
Your latest job postings
</CardDescription>
</div>
<Link href="/dashboard/jobs">
<Button variant="ghost" size="sm">
Ver todas
View all
</Button>
</Link>
</div>
@ -243,11 +243,11 @@ export function CompanyDashboardContent() {
<div className="flex flex-wrap gap-4 text-sm">
<span className="flex items-center gap-1">
<Users className="h-4 w-4" />
{job.applications} candidaturas
{job.applications} applications
</span>
<span className="flex items-center gap-1">
<Eye className="h-4 w-4" />
{job.views} visualizações
{job.views} views
</span>
<span className="flex items-center gap-1">
<Calendar className="h-4 w-4" />
@ -269,18 +269,18 @@ export function CompanyDashboardContent() {
</Card>
</div>
{/* Candidaturas Recentes */}
{/* Recent applications */}
<div>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Candidaturas</CardTitle>
<CardDescription>Novas candidaturas</CardDescription>
<CardTitle>Applications</CardTitle>
<CardDescription>New applications</CardDescription>
</div>
<Link href="/dashboard/candidates">
<Button variant="ghost" size="sm">
Ver todas
View all
</Button>
</Link>
</div>

View file

@ -13,7 +13,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Briefcase, LogOut, User, Settings } from "lucide-react";
import { LogOut, User } from "lucide-react";
import { logout, getCurrentUser } from "@/lib/auth";
import { NotificationDropdown } from "@/components/notification-dropdown";
@ -78,7 +78,7 @@ export function DashboardHeader() {
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">
{user?.name || "Usuário"}
{user?.name || "User"}
</p>
<p className="text-xs leading-none text-muted-foreground">
{user?.email}
@ -88,12 +88,12 @@ export function DashboardHeader() {
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleProfileClick}>
<User className="mr-2 h-4 w-4" />
<span>Perfil</span>
<span>Profile</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleLogout}>
<LogOut className="mr-2 h-4 w-4" />
<span>Sair</span>
<span>Sign out</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View file

@ -25,21 +25,21 @@ export function Footer() {
<ul className="space-y-2 text-sm text-muted-foreground">
<li>
<Link href="/jobs?tech=python" className="hover:text-foreground transition-colors">
Desenvolvedor Python
Python Developer
</Link>
</li>
<li>
<Link href="/jobs?tech=react" className="hover:text-foreground transition-colors">
Desenvolvedor React
React Developer
</Link>
</li>
<li>
<Link href="/jobs?tech=dados" className="hover:text-foreground transition-colors">
Analista de Dados
<Link href="/jobs?tech=data" className="hover:text-foreground transition-colors">
Data Analyst
</Link>
</li>
<li>
<Link href="/jobs?type=remoto" className="hover:text-foreground transition-colors">
<Link href="/jobs?type=remote" className="hover:text-foreground transition-colors">
{t('workMode.remote')}
</Link>
</li>
@ -50,12 +50,12 @@ export function Footer() {
<h3 className="font-semibold mb-4">{t('footer.company')}</h3>
<ul className="space-y-2 text-sm text-muted-foreground">
<li>
<Link href="/sobre" className="hover:text-foreground transition-colors">
<Link href="/about" className="hover:text-foreground transition-colors">
{t('footer.about')}
</Link>
</li>
<li>
<Link href="/contato" className="hover:text-foreground transition-colors">
<Link href="/contact" className="hover:text-foreground transition-colors">
{t('nav.contact')}
</Link>
</li>
@ -71,12 +71,12 @@ export function Footer() {
<h3 className="font-semibold mb-4">{t('footer.legal')}</h3>
<ul className="space-y-2 text-sm text-muted-foreground">
<li>
<Link href="/privacidade" className="hover:text-foreground transition-colors">
<Link href="/privacy" className="hover:text-foreground transition-colors">
{t('footer.privacy')}
</Link>
</li>
<li>
<Link href="/termos" className="hover:text-foreground transition-colors">
<Link href="/terms" className="hover:text-foreground transition-colors">
{t('footer.terms')}
</Link>
</li>
@ -91,4 +91,3 @@ export function Footer() {
</footer>
)
}

View file

@ -58,7 +58,7 @@ export function JobCard({ job }: JobCardProps) {
return "secondary";
case "contract":
return "outline";
case "Remoto":
case "remote":
return "default";
default:
return "outline";
@ -81,7 +81,7 @@ export function JobCard({ job }: JobCardProps) {
t('jobs.favorites.added.title'),
t('jobs.favorites.added.desc', { title: job.title }),
{
actionUrl: "/dashboard/candidato/favoritos",
actionUrl: "/dashboard/favorites",
actionLabel: t('jobs.favorites.action'),
}
);
@ -201,7 +201,7 @@ export function JobCard({ job }: JobCardProps) {
{t('jobs.card.viewDetails')}
</Button>
</Link>
<Link href={`/jobs/${job.id}/candidatura`} className="flex-1">
<Link href={`/jobs/${job.id}/apply`} className="flex-1">
<Button className="w-full cursor-pointer">{t('jobs.card.apply')}</Button>
</Link>
</div>

View file

@ -17,8 +17,8 @@ export function Navbar() {
const navigationItems = [
{ href: "/jobs", label: t('nav.jobs') },
{ href: "/sobre", label: t('nav.about') },
{ href: "/contato", label: t('nav.contact') },
{ href: "/about", label: t('nav.about') },
{ href: "/contact", label: t('nav.contact') },
]
return (
@ -63,7 +63,7 @@ export function Navbar() {
{t('nav.login')}
</Button>
</Link>
<Link href="/cadastro/candidato">
<Link href="/register/candidate">
<Button className="gap-2">
<User className="w-4 h-4" />
{t('nav.register')}
@ -114,7 +114,7 @@ export function Navbar() {
{t('nav.login')}
</Button>
</Link>
<Link href="/cadastro/candidato" onClick={() => setIsOpen(false)}>
<Link href="/register/candidate" onClick={() => setIsOpen(false)}>
<Button className="w-full justify-start gap-2">
<User className="w-4 h-4" />
{t('nav.register')}

View file

@ -26,16 +26,16 @@ import {
AlertTriangle,
} from "lucide-react";
import { formatDistanceToNow } from "date-fns";
import { ptBR } from "date-fns/locale";
import { enUS } from "date-fns/locale";
import Link from "next/link";
import { motion, AnimatePresence } from "framer-motion";
export function NotificationDropdown() {
const pathname = usePathname();
const isCompanyDashboard = pathname?.startsWith("/dashboard/empresa");
const isCompanyDashboard = pathname?.startsWith("/dashboard/company");
const notificationsUrl = isCompanyDashboard
? "/dashboard/empresa/notificacoes"
: "/dashboard/candidato/notificacoes";
? "/dashboard/company/notifications"
: "/dashboard/candidate/notifications";
const {
notifications,
unreadCount,
@ -89,7 +89,7 @@ export function NotificationDropdown() {
<DropdownMenuContent align="end" className="w-80">
<DropdownMenuLabel className="flex items-center justify-between">
<span>Notificações</span>
<span>Notifications</span>
{unreadCount > 0 && (
<div className="flex items-center gap-2">
<Button
@ -99,7 +99,7 @@ export function NotificationDropdown() {
className="h-6 px-2 text-xs"
>
<CheckCheck className="h-3 w-3 mr-1" />
Marcar todas
Mark all
</Button>
</div>
)}
@ -110,7 +110,7 @@ export function NotificationDropdown() {
{recentNotifications.length === 0 ? (
<div className="p-4 text-center text-sm text-muted-foreground">
<Bell className="h-8 w-8 mx-auto mb-2 opacity-50" />
Nenhuma notificação
No notifications
</div>
) : (
<ScrollArea className="h-80">
@ -194,7 +194,7 @@ export function NotificationDropdown() {
new Date(notification.createdAt),
{
addSuffix: true,
locale: ptBR,
locale: enUS,
}
)}
</span>
@ -243,7 +243,7 @@ export function NotificationDropdown() {
className="flex-1 text-xs"
>
<Trash2 className="h-3 w-3 mr-1" />
Limpar todas
Clear all
</Button>
<Link href={notificationsUrl} className="flex-1">
<Button
@ -252,7 +252,7 @@ export function NotificationDropdown() {
className="w-full text-xs"
onClick={() => setIsOpen(false)}
>
Ver todas
View all
</Button>
</Link>
</div>

View file

@ -115,7 +115,7 @@ export function ProfilePictureUpload({
<CardContent className="p-6">
<div className="flex flex-col items-center space-y-4">
<div className="text-sm font-medium text-center">
Foto de Perfil
Profile Photo
</div>
<div
@ -199,4 +199,4 @@ export function ProfilePictureUpload({
</CardContent>
</Card>
)
}
}

View file

@ -133,7 +133,7 @@ export function ProfilePictureUpload({
<Card className={cn("w-fit", className)}>
<CardContent className="p-6">
<div className="flex flex-col items-center space-y-4">
<div className="text-sm font-medium text-center">Foto de Perfil</div>
<div className="text-sm font-medium text-center">Profile Photo</div>
<div
className={cn(

View file

@ -14,27 +14,27 @@ const adminItems = [
icon: LayoutDashboard,
},
{
title: "Vagas",
title: "Jobs",
href: "/dashboard/jobs",
icon: Briefcase,
},
{
title: "Candidatos",
title: "Candidates",
href: "/dashboard/candidates",
icon: Users,
},
{
title: "Usuários",
title: "Users",
href: "/dashboard/users",
icon: Users,
},
{
title: "Empresas",
title: "Companies",
href: "/dashboard/companies",
icon: Building2,
},
{
title: "Mensagens",
title: "Messages",
href: "/dashboard/messages",
icon: MessageSquare,
},
@ -47,12 +47,12 @@ const companyItems = [
icon: LayoutDashboard,
},
{
title: "Minhas Vagas",
title: "My jobs",
href: "/dashboard/my-jobs",
icon: Briefcase,
},
{
title: "Candidaturas",
title: "Applications",
href: "/dashboard/applications",
icon: Users,
},
@ -65,12 +65,12 @@ const candidateItems = [
icon: LayoutDashboard,
},
{
title: "Vagas",
title: "Jobs",
href: "/jobs", // Public search
icon: Briefcase,
},
{
title: "Minhas Candidaturas",
title: "My applications",
href: "/dashboard/my-applications",
icon: FileText,
},

View file

@ -47,7 +47,7 @@
"loading": "Loading jobs...",
"error": "Could not load jobs right now. Showing examples.",
"card": { "viewDetails": "View details", "apply": "Apply now", "perMonth": "/month", "postedAgo": "Posted {time} ago" },
"types": { "full-time": "Full Time", "part-time": "Part Time", "contract": "Contract", "freelance": "Freelance" },
"types": { "full-time": "Full Time", "part-time": "Part Time", "contract": "Contract", "freelance": "Freelance", "remote": "Remote" },
"confidential": "Confidential Company",
"salary": { "negotiable": "Negotiable" },
"posted": { "today": "Today", "yesterday": "Yesterday", "daysAgo": "{count} days ago", "weeksAgo": "{count} weeks ago", "monthsAgo": "{count} months ago" },

View file

@ -47,7 +47,7 @@
"loading": "Cargando empleos...",
"error": "No se pudieron cargar los empleos ahora. Mostrando ejemplos.",
"card": { "viewDetails": "Ver detalles", "apply": "Postularse", "perMonth": "/mes", "postedAgo": "Publicado hace {time}" },
"types": { "full-time": "Tiempo completo", "part-time": "Medio tiempo", "contract": "Contrato", "freelance": "Freelance" },
"types": { "full-time": "Tiempo completo", "part-time": "Medio tiempo", "contract": "Contrato", "freelance": "Freelance", "remote": "Remoto" },
"confidential": "Empresa Confidencial",
"salary": { "negotiable": "A convenir" },
"posted": { "today": "Hoy", "yesterday": "Ayer", "daysAgo": "hace {count} días", "weeksAgo": "hace {count} semanas", "monthsAgo": "hace {count} meses" },

View file

@ -47,7 +47,7 @@
"loading": "Carregando vagas...",
"error": "Não foi possível carregar as vagas agora. Exibindo exemplos.",
"card": { "viewDetails": "Ver detalhes", "apply": "Candidatar-se", "perMonth": "/mês", "postedAgo": "Publicada há {time}" },
"types": { "full-time": "Tempo Integral", "part-time": "Meio Período", "contract": "Contrato", "freelance": "Freelance" },
"types": { "full-time": "Tempo Integral", "part-time": "Meio Período", "contract": "Contrato", "freelance": "Freelance", "remote": "Remoto" },
"confidential": "Empresa Confidencial",
"salary": { "negotiable": "A combinar" },
"posted": { "today": "Hoje", "yesterday": "Ontem", "daysAgo": "{count} dias atrás", "weeksAgo": "{count} semanas atrás", "monthsAgo": "{count} meses atrás" },

View file

@ -152,20 +152,22 @@ export function transformApiJobToFrontend(apiJob: ApiJob): import('./types').Job
// Format salary
let salary: string | undefined;
if (apiJob.salaryMin && apiJob.salaryMax) {
salary = `R$ ${apiJob.salaryMin.toLocaleString('pt-BR')} - R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`;
salary = `R$ ${apiJob.salaryMin.toLocaleString('en-US')} - R$ ${apiJob.salaryMax.toLocaleString('en-US')}`;
} else if (apiJob.salaryMin) {
salary = `A partir de R$ ${apiJob.salaryMin.toLocaleString('pt-BR')}`;
salary = `From R$ ${apiJob.salaryMin.toLocaleString('en-US')}`;
} else if (apiJob.salaryMax) {
salary = `Até R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`;
salary = `Up to R$ ${apiJob.salaryMax.toLocaleString('en-US')}`;
}
// Determine type
type JobType = 'full-time' | 'part-time' | 'contract' | 'Remoto' | 'Tempo Integral';
let type: JobType = 'Tempo Integral';
if (apiJob.employmentType === 'full-time') type = 'Tempo Integral';
type JobType = 'full-time' | 'part-time' | 'contract' | 'remote';
let type: JobType = 'full-time';
if (apiJob.employmentType === 'full-time') type = 'full-time';
else if (apiJob.employmentType === 'part-time') type = 'part-time';
else if (apiJob.employmentType === 'contract') type = 'contract';
else if (apiJob.location?.toLowerCase().includes('remoto')) type = 'Remoto';
else if (apiJob.workMode === 'remote' || apiJob.location?.toLowerCase().includes('remote') || apiJob.location?.toLowerCase().includes('remoto')) {
type = 'remote';
}
// Extract requirements
const requirements: string[] = [];
@ -180,13 +182,13 @@ export function transformApiJobToFrontend(apiJob: ApiJob): import('./types').Job
return {
id: String(apiJob.id),
title: apiJob.title,
company: apiJob.companyName || 'Empresa',
location: apiJob.location || apiJob.cityName || 'Localização não informada',
company: apiJob.companyName || 'Company',
location: apiJob.location || apiJob.cityName || 'Location not provided',
type,
workMode: apiJob.workMode as any,
salary,
description: apiJob.description,
requirements: requirements.length > 0 ? requirements : ['Ver detalhes'],
requirements: requirements.length > 0 ? requirements : ['View details'],
postedAt: apiJob.createdAt?.split('T')[0] || new Date().toISOString().split('T')[0],
};
}

View file

@ -10,41 +10,41 @@ import type {
export const mockJobs: Job[] = [
{
id: "1",
title: "Desenvolvedor Full Stack Sênior",
title: "Senior Full Stack Developer",
company: "TechCorp",
location: "São Paulo, SP",
type: "Tempo Integral",
salary: "R$ 12.000 - R$ 18.000",
type: "full-time",
salary: "R$ 12,000 - R$ 18,000",
description:
"Buscamos um desenvolvedor full stack experiente para liderar projetos inovadores.",
requirements: ["React", "Node.js", "TypeScript", "5+ anos de experiência"],
"We are looking for an experienced full stack developer to lead innovative projects.",
requirements: ["React", "Node.js", "TypeScript", "5+ years of experience"],
postedAt: "2025-10-15",
},
{
id: "2",
title: "Designer UX/UI",
title: "UX/UI Designer",
company: "DesignHub",
location: "São Paulo, SP",
type: "Remoto",
salary: "R$ 8.000 - R$ 12.000",
type: "remote",
salary: "R$ 8,000 - R$ 12,000",
description:
"Procuramos designer criativo para criar experiências incríveis.",
"We are looking for a creative designer to craft incredible experiences.",
requirements: [
"Figma",
"Adobe XD",
"Portfolio forte",
"3+ anos de experiência",
"Strong portfolio",
"3+ years of experience",
],
postedAt: "2025-10-14",
},
{
id: "3",
title: "Engenheiro de Dados",
title: "Data Engineer",
company: "DataFlow",
location: "Rio de Janeiro, RJ",
type: "Tempo Integral",
salary: "R$ 15.000 - R$ 22.000",
description: "Oportunidade para trabalhar com big data e machine learning.",
type: "full-time",
salary: "R$ 15,000 - R$ 22,000",
description: "Opportunity to work with big data and machine learning.",
requirements: ["Python", "SQL", "Spark", "AWS"],
postedAt: "2025-10-13",
},
@ -53,21 +53,21 @@ export const mockJobs: Job[] = [
title: "Product Manager",
company: "InnovateLab",
location: "Belo Horizonte, MG",
type: "Tempo Integral",
salary: "R$ 10.000 - R$ 16.000",
description: "Lidere o desenvolvimento de produtos digitais inovadores.",
requirements: ["Gestão de produtos", "Agile", "Análise de dados"],
type: "full-time",
salary: "R$ 10,000 - R$ 16,000",
description: "Lead the development of innovative digital products.",
requirements: ["Product management", "Agile", "Data analysis"],
postedAt: "2025-10-12",
},
{
id: "5",
title: "Desenvolvedor Mobile",
title: "Mobile Developer",
company: "AppMakers",
location: "São Paulo, SP",
type: "Remoto",
salary: "R$ 9.000 - R$ 14.000",
description: "Desenvolva aplicativos mobile de alta qualidade.",
requirements: ["React Native", "iOS", "Android", "3+ anos"],
type: "remote",
salary: "R$ 9,000 - R$ 14,000",
description: "Build high-quality mobile applications.",
requirements: ["React Native", "iOS", "Android", "3+ years"],
postedAt: "2025-10-11",
},
{
@ -75,9 +75,9 @@ export const mockJobs: Job[] = [
title: "DevOps Engineer",
company: "CloudTech",
location: "São Paulo, SP",
type: "Tempo Integral",
salary: "R$ 13.000 - R$ 19.000",
description: "Gerencie infraestrutura cloud e pipelines de CI/CD.",
type: "full-time",
salary: "R$ 13,000 - R$ 19,000",
description: "Manage cloud infrastructure and CI/CD pipelines.",
requirements: ["Docker", "Kubernetes", "AWS", "Terraform"],
postedAt: "2025-10-10",
},
@ -87,7 +87,7 @@ export const mockApplications: Application[] = [
{
id: "1",
jobId: "1",
jobTitle: "Desenvolvedor Full Stack Sênior",
jobTitle: "Senior Full Stack Developer",
company: "TechCorp",
status: "reviewing",
appliedAt: "2025-10-16",
@ -95,7 +95,7 @@ export const mockApplications: Application[] = [
{
id: "2",
jobId: "2",
jobTitle: "Designer UX/UI",
jobTitle: "UX/UI Designer",
company: "DesignHub",
status: "interview",
appliedAt: "2025-10-15",
@ -123,16 +123,16 @@ export const mockUser: User = {
name: "João Silva",
email: "joao@example.com",
role: "candidate",
area: "Desenvolvimento Full Stack",
area: "Full Stack Development",
profileComplete: 85,
};
export const mockNotifications: Notification[] = [
{
id: "1",
title: "Nova vaga recomendada",
title: "New recommended job",
message:
"Encontramos uma vaga que combina com seu perfil: Desenvolvedor Full Stack Sênior",
"We found a role that matches your profile: Senior Full Stack Developer",
type: "info",
read: false,
createdAt: "2025-11-19T10:30:00",
@ -140,8 +140,8 @@ export const mockNotifications: Notification[] = [
},
{
id: "2",
title: "Atualização de candidatura",
message: "Sua candidatura para Designer UX/UI foi movida para entrevista",
title: "Application update",
message: "Your application for UX/UI Designer moved to interview",
type: "success",
read: false,
createdAt: "2025-11-18T14:20:00",
@ -149,9 +149,9 @@ export const mockNotifications: Notification[] = [
},
{
id: "3",
title: "Candidatura aprovada!",
title: "Application approved!",
message:
"Parabéns! Sua candidatura para Product Manager foi aprovada pela empresa InnovateLab",
"Congratulations! Your application for Product Manager was approved by InnovateLab",
type: "success",
read: false,
createdAt: "2025-11-18T09:15:00",
@ -159,8 +159,8 @@ export const mockNotifications: Notification[] = [
},
{
id: "4",
title: "Nova mensagem",
message: "A empresa TechCorp enviou uma mensagem sobre sua candidatura",
title: "New message",
message: "TechCorp sent you a message about your application",
type: "info",
read: true,
createdAt: "2025-11-17T16:45:00",
@ -168,9 +168,9 @@ export const mockNotifications: Notification[] = [
},
{
id: "5",
title: "Complete seu perfil",
title: "Complete your profile",
message:
"Adicione mais informações ao seu perfil para aumentar suas chances em 40%",
"Add more information to your profile to boost your chances by 40%",
type: "warning",
read: true,
createdAt: "2025-11-17T08:00:00",
@ -178,9 +178,9 @@ export const mockNotifications: Notification[] = [
},
{
id: "6",
title: "Lembrete de entrevista",
title: "Interview reminder",
message:
"Você tem uma entrevista agendada para amanhã às 15h com a DesignHub",
"You have an interview scheduled tomorrow at 3 PM with DesignHub",
type: "warning",
read: false,
createdAt: "2025-11-16T11:00:00",
@ -188,9 +188,9 @@ export const mockNotifications: Notification[] = [
},
{
id: "7",
title: "Nova vaga disponível",
title: "New job available",
message:
"5 novas vagas de Desenvolvedor foram publicadas hoje na sua região",
"5 new developer jobs were posted today in your area",
type: "info",
read: true,
createdAt: "2025-11-15T07:30:00",
@ -198,8 +198,8 @@ export const mockNotifications: Notification[] = [
},
{
id: "8",
title: "Perfil visualizado",
message: "3 empresas visualizaram seu perfil nas últimas 24 horas",
title: "Profile viewed",
message: "3 companies viewed your profile in the last 24 hours",
type: "success",
read: true,
createdAt: "2025-11-14T18:20:00",
@ -210,9 +210,9 @@ export const mockNotifications: Notification[] = [
export const mockCompanyNotifications: Notification[] = [
{
id: "1",
title: "Nova candidatura recebida",
title: "New application received",
message:
"Ana Silva se candidatou para a vaga de Desenvolvedor Full Stack Sênior",
"Ana Silva applied for the Senior Full Stack Developer role",
type: "info",
read: false,
createdAt: "2025-11-19T10:30:00",
@ -220,8 +220,8 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "2",
title: "Candidato aceitou entrevista",
message: "Carlos Santos confirmou presença na entrevista de amanhã às 14h",
title: "Candidate accepted interview",
message: "Carlos Santos confirmed attendance for tomorrow at 2 PM",
type: "success",
read: false,
createdAt: "2025-11-18T14:20:00",
@ -229,9 +229,9 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "3",
title: "Vaga com alta demanda",
title: "High-demand role",
message:
"A vaga de Designer UX/UI recebeu 15 novas candidaturas nas últimas 24h",
"The UX/UI Designer role received 15 new applications in the last 24 hours",
type: "success",
read: false,
createdAt: "2025-11-18T09:15:00",
@ -239,8 +239,8 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "4",
title: "Nova mensagem de candidato",
message: "Maria Oliveira enviou uma mensagem sobre a vaga de Product Manager",
title: "New candidate message",
message: "Maria Oliveira sent a message about the Product Manager role",
type: "info",
read: true,
createdAt: "2025-11-17T16:45:00",
@ -248,9 +248,9 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "5",
title: "Complete o perfil da empresa",
title: "Complete your company profile",
message:
"Adicione logo e informações sobre benefícios para atrair mais candidatos",
"Add a logo and benefits details to attract more candidates",
type: "warning",
read: true,
createdAt: "2025-11-17T08:00:00",
@ -258,9 +258,9 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "6",
title: "Lembrete: Entrevista agendada",
title: "Reminder: interviews scheduled",
message:
"Você tem 2 entrevistas agendadas para amanhã. Confira os horários",
"You have 2 interviews scheduled for tomorrow. Check the times",
type: "warning",
read: false,
createdAt: "2025-11-16T11:00:00",
@ -268,9 +268,9 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "7",
title: "Vaga próxima de expirar",
title: "Job expiring soon",
message:
"A vaga de DevOps Engineer expira em 3 dias. Renove para continuar recebendo candidaturas",
"The DevOps Engineer role expires in 3 days. Renew it to keep receiving applications",
type: "warning",
read: true,
createdAt: "2025-11-15T07:30:00",
@ -278,8 +278,8 @@ export const mockCompanyNotifications: Notification[] = [
},
{
id: "8",
title: "Perfil empresarial visualizado",
message: "45 candidatos visualizaram o perfil da sua empresa esta semana",
title: "Company profile viewed",
message: "45 candidates viewed your company profile this week",
type: "success",
read: true,
createdAt: "2025-11-14T18:20:00",
@ -298,9 +298,9 @@ export const mockTestimonials = [
{
id: "1",
name: "Maria Santos",
role: "Desenvolvedora",
role: "Developer",
content:
"Encontrei meu emprego dos sonhos em apenas 2 semanas. Plataforma incrível!",
"I found my dream job in just 2 weeks. Incredible platform!",
avatar: "/professional-woman-diverse.png",
},
{
@ -308,7 +308,7 @@ export const mockTestimonials = [
name: "Carlos Oliveira",
role: "Designer",
content:
"Interface simples e vagas de qualidade. Recomendo para todos os profissionais.",
"Simple interface and high-quality roles. I recommend it to every professional.",
avatar: "/professional-man.jpg",
},
{
@ -316,7 +316,7 @@ export const mockTestimonials = [
name: "Ana Costa",
role: "Product Manager",
content:
"O processo foi rápido e transparente. Consegui várias entrevistas rapidamente.",
"The process was fast and transparent. I landed several interviews quickly.",
avatar: "/professional-woman-smiling.png",
},
];
@ -328,16 +328,16 @@ export const mockCandidates: Candidate[] = [
email: "ana.silva@example.com",
phone: "+55 11 98765-4321",
location: "São Paulo, SP",
title: "Desenvolvedora Full Stack",
experience: "5 anos de experiência",
title: "Full Stack Developer",
experience: "5 years of experience",
avatar: "/professional-woman-diverse.png",
bio: "Desenvolvedora apaixonada por criar soluções inovadoras. Experiência em React, Node.js e cloud computing.",
bio: "Developer passionate about building innovative solutions. Experience in React, Node.js, and cloud computing.",
skills: ["React", "Node.js", "TypeScript", "AWS", "Docker"],
applications: [
{
id: "1",
jobId: "1",
jobTitle: "Desenvolvedor Full Stack Sênior",
jobTitle: "Senior Full Stack Developer",
company: "TechCorp",
status: "pending",
appliedAt: "2025-10-16",
@ -345,7 +345,7 @@ export const mockCandidates: Candidate[] = [
{
id: "2",
jobId: "2",
jobTitle: "Desenvolvedor Mobile",
jobTitle: "Mobile Developer",
company: "AppMakers",
status: "accepted",
appliedAt: "2025-10-10",
@ -358,16 +358,16 @@ export const mockCandidates: Candidate[] = [
email: "carlos.santos@example.com",
phone: "+55 11 91234-5678",
location: "Rio de Janeiro, RJ",
title: "Designer UX/UI",
experience: "3 anos de experiência",
title: "UX/UI Designer",
experience: "3 years of experience",
avatar: "/professional-man.jpg",
bio: "Designer focado em criar experiências memoráveis. Especialista em design systems e prototipagem.",
bio: "Designer focused on creating memorable experiences. Specialist in design systems and prototyping.",
skills: ["Figma", "Adobe XD", "UI Design", "Prototyping", "Design Systems"],
applications: [
{
id: "3",
jobId: "3",
jobTitle: "Designer UX/UI",
jobTitle: "UX/UI Designer",
company: "DesignHub",
status: "pending",
appliedAt: "2025-10-15",
@ -380,10 +380,10 @@ export const mockCandidates: Candidate[] = [
email: "maria.oliveira@example.com",
phone: "+55 21 99876-5432",
location: "Belo Horizonte, MG",
title: "Engenheira de Dados",
experience: "7 anos de experiência",
title: "Data Engineer",
experience: "7 years of experience",
avatar: "/professional-woman-smiling.png",
bio: "Engenheira de dados com forte background em machine learning e big data. Apaixonada por transformar dados em insights.",
bio: "Data engineer with a strong background in machine learning and big data. Passionate about turning data into insights.",
skills: [
"Python",
"SQL",
@ -395,7 +395,7 @@ export const mockCandidates: Candidate[] = [
{
id: "4",
jobId: "4",
jobTitle: "Engenheiro de Dados",
jobTitle: "Data Engineer",
company: "DataFlow",
status: "accepted",
appliedAt: "2025-10-13",
@ -417,9 +417,9 @@ export const mockCandidates: Candidate[] = [
phone: "+55 31 98765-1234",
location: "Curitiba, PR",
title: "Product Manager",
experience: "6 anos de experiência",
experience: "6 years of experience",
avatar: "/placeholder.svg?height=100&width=100",
bio: "Product Manager com experiência em produtos digitais e metodologias ágeis. Focado em entregar valor ao usuário.",
bio: "Product Manager with experience in digital products and agile methodologies. Focused on delivering user value.",
skills: [
"Product Management",
"Agile",
@ -445,9 +445,9 @@ export const mockCandidates: Candidate[] = [
phone: "+55 41 91234-8765",
location: "Porto Alegre, RS",
title: "DevOps Engineer",
experience: "4 anos de experiência",
experience: "4 years of experience",
avatar: "/placeholder.svg?height=100&width=100",
bio: "DevOps engineer especializada em automação e infraestrutura cloud. Experiência com Kubernetes e CI/CD.",
bio: "DevOps engineer specialized in automation and cloud infrastructure. Experience with Kubernetes and CI/CD.",
skills: ["Docker", "Kubernetes", "AWS", "Terraform", "CI/CD"],
applications: [
{
@ -468,9 +468,9 @@ export const mockAdminUser = {
email: "admin@portal.com",
role: "admin" as const,
phone: "+55 11 99999-9999",
department: "Recursos Humanos",
position: "Gerente de RH",
department: "Human Resources",
position: "HR Manager",
joinedAt: "2023-06-15",
avatar: "/placeholder.svg?height=200&width=200",
bio: "Gerente de RH com mais de 10 anos de experiência em recrutamento e seleção. Apaixonado por conectar talentos com oportunidades.",
bio: "HR manager with over 10 years of experience in recruiting and selection. Passionate about connecting talent with opportunities.",
};

View file

@ -3,7 +3,7 @@ export interface Job {
title: string;
company: string;
location: string;
type: "full-time" | "part-time" | "contract" | "Remoto" | "Tempo Integral";
type: "full-time" | "part-time" | "contract" | "remote";
workMode?: "onsite" | "hybrid" | "remote";
salary?: string;
description: string;