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:
commit
360393ba15
39 changed files with 729 additions and 758 deletions
|
|
@ -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}>
|
||||
|
|
@ -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 já
|
||||
esteja respondida lá.
|
||||
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>
|
||||
|
|
@ -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>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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">Ní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">
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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">
|
||||
Já 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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`);
|
||||
}
|
||||
|
|
@ -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}`);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function VagasRedirectPage() {
|
||||
redirect("/jobs");
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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')}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue