feat(i18n): internationalize sidebar and dashboards

This commit is contained in:
Tiago Yamamoto 2025-12-31 13:20:04 -03:00
parent 3e81ae40e7
commit 33e7bbb334
9 changed files with 508 additions and 268 deletions

View file

@ -1,110 +0,0 @@
export const dashboardTranslations = {
pt: {
title: "Dashboard",
subtitle: "Visão geral do portal de vagas",
stats: {
activeJobs: "Vagas Ativas",
activeJobsDesc: "Total de vagas publicadas",
candidates: "Candidatos",
candidatesDesc: "Usuários registrados",
applications: "Candidaturas",
applicationsDesc: "Em andamento",
hiringRate: "Taxa de Contratação",
hiringRateDesc: "Candidaturas por vaga"
},
jobs: {
title: "Gerenciamento de Vagas",
add: "Nova Vaga",
table: {
title: "Título",
company: "Empresa",
status: "Status",
created: "Criado em",
actions: "Ações"
},
empty: "Nenhuma vaga encontrada."
},
candidates: {
title: "Gerenciamento de Candidatos",
table: {
name: "Nome",
email: "Email",
location: "Localização",
actions: "Ações"
},
empty: "Nenhum candidato encontrado."
}
},
en: {
title: "Dashboard",
subtitle: "Overview of the jobs portal",
stats: {
activeJobs: "Active Jobs",
activeJobsDesc: "Total posted jobs",
candidates: "Total Candidates",
candidatesDesc: "Registered users",
applications: "Active Applications",
applicationsDesc: "Current pipeline",
hiringRate: "Hiring Rate",
hiringRateDesc: "Applications per job"
},
jobs: {
title: "Job Management",
add: "Add Job",
table: {
title: "Title",
company: "Company",
status: "Status",
created: "Created At",
actions: "Actions"
},
empty: "No jobs found."
},
candidates: {
title: "Candidate Management",
table: {
name: "Name",
email: "Email",
location: "Location",
actions: "Actions"
},
empty: "No candidates found."
}
},
es: {
title: "Panel de Control",
subtitle: "Visión general del portal de empleos",
stats: {
activeJobs: "Empleos Activos",
activeJobsDesc: "Total publicados",
candidates: "Candidatos Total",
candidatesDesc: "Usuarios registrados",
applications: "Aplicaciones Activas",
applicationsDesc: "En proceso",
hiringRate: "Tasa de Contratación",
hiringRateDesc: "Aplicaciones por empleo"
},
jobs: {
title: "Gestión de Empleos",
add: "Nuevo Empleo",
table: {
title: "Título",
company: "Empresa",
status: "Estado",
created: "Creado en",
actions: "Acciones"
},
empty: "No se encontraron empleos."
},
candidates: {
title: "Gestión de Candidatos",
table: {
name: "Nombre",
email: "Correo",
location: "Ubicación",
actions: "Acciones"
},
empty: "No se encontraron candidatos."
}
}
}

View file

@ -249,7 +249,7 @@ export default function AdminUsersPage() {
{/* Header */}
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">{t('admin.users.title')} (v2)</h1>
<h1 className="text-2xl sm:text-3xl font-bold text-foreground">{t('admin.users.title')}</h1>
<p className="text-sm sm:text-base text-muted-foreground mt-1">{t('admin.users.subtitle')}</p>
</div>
<div className="flex gap-2">

View file

@ -23,14 +23,12 @@ import { Briefcase, Users, TrendingUp, FileText, Plus, MoreHorizontal, Loader2 }
import { motion } from "framer-motion"
import { adminJobsApi, adminCandidatesApi, type AdminJob, type AdminCandidate, type AdminCandidateStats } from "@/lib/api"
import { toast } from "sonner"
import { useLanguageStore } from "@/lib/store/language-store"
import { dashboardTranslations } from "@/app/dashboard/translations"
import { useTranslation } from "@/lib/i18n"
export function AdminDashboardContent() {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const { language } = useLanguageStore()
const t = dashboardTranslations[language]
const { t } = useTranslation()
const [stats, setStats] = useState({
activeJobs: 0,
@ -104,8 +102,8 @@ export function AdminDashboardContent() {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<h1 className="text-3xl font-bold mb-2">{t.title}</h1>
<p className="text-muted-foreground">{t.subtitle}</p>
<h1 className="text-3xl font-bold mb-2">{t('admin.dashboard.title')}</h1>
<p className="text-muted-foreground">{t('admin.dashboard.subtitle')}</p>
</motion.div>
{/* Stats */}
@ -116,28 +114,28 @@ export function AdminDashboardContent() {
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
>
<StatsCard
title={t.stats.activeJobs}
title={t('admin.dashboard.stats.activeJobs')}
value={stats.activeJobs}
icon={Briefcase}
description={t.stats.activeJobsDesc}
description={t('admin.dashboard.stats.activeJobsDesc')}
/>
<StatsCard
title={t.stats.candidates}
title={t('admin.dashboard.stats.candidates')}
value={stats.totalCandidates}
icon={Users}
description={t.stats.candidatesDesc}
description={t('admin.dashboard.stats.candidatesDesc')}
/>
<StatsCard
title={t.stats.applications}
title={t('admin.dashboard.stats.applications')}
value={stats.newApplications}
icon={FileText}
description={t.stats.applicationsDesc}
description={t('admin.dashboard.stats.applicationsDesc')}
/>
<StatsCard
title={t.stats.hiringRate}
title={t('admin.dashboard.stats.hiringRate')}
value={`${stats.conversionRate.toFixed(1)}%`}
icon={TrendingUp}
description={t.stats.hiringRateDesc}
description={t('admin.dashboard.stats.hiringRateDesc')}
/>
</motion.div>
@ -149,12 +147,12 @@ export function AdminDashboardContent() {
>
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>{t.jobs.title}</CardTitle>
<CardTitle>{t('admin.dashboard.jobs.title')}</CardTitle>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button>
<Plus className="mr-2 h-4 w-4" />
{t.jobs.add}
{t('admin.dashboard.jobs.add')}
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
@ -199,17 +197,17 @@ export function AdminDashboardContent() {
<Table>
<TableHeader>
<TableRow>
<TableHead>{t.jobs.table.title}</TableHead>
<TableHead>{t.jobs.table.company}</TableHead>
<TableHead>{t.jobs.table.status}</TableHead>
<TableHead>{t.jobs.table.created}</TableHead>
<TableHead className="text-right">{t.jobs.table.actions}</TableHead>
<TableHead>{t('admin.dashboard.jobs.table.title')}</TableHead>
<TableHead>{t('admin.dashboard.jobs.table.company')}</TableHead>
<TableHead>{t('admin.dashboard.jobs.table.status')}</TableHead>
<TableHead>{t('admin.dashboard.jobs.table.created')}</TableHead>
<TableHead className="text-right">{t('admin.dashboard.jobs.table.actions')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{recentJobs.length === 0 ? (
<TableRow>
<TableCell colSpan={5} className="text-center text-muted-foreground">{t.jobs.empty}</TableCell>
<TableCell colSpan={5} className="text-center text-muted-foreground">{t('admin.dashboard.jobs.empty')}</TableCell>
</TableRow>
) : (
recentJobs.map((job) => (
@ -242,22 +240,22 @@ export function AdminDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>{t.candidates.title}</CardTitle>
<CardTitle>{t('admin.dashboard.candidates.title')}</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>{t.candidates.table.name}</TableHead>
<TableHead>{t.candidates.table.email}</TableHead>
<TableHead>{t.candidates.table.location}</TableHead>
<TableHead className="text-right">{t.candidates.table.actions}</TableHead>
<TableHead>{t('admin.dashboard.candidates.table.name')}</TableHead>
<TableHead>{t('admin.dashboard.candidates.table.email')}</TableHead>
<TableHead>{t('admin.dashboard.candidates.table.location')}</TableHead>
<TableHead className="text-right">{t('admin.dashboard.candidates.table.actions')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{recentCandidates.length === 0 ? (
<TableRow>
<TableCell colSpan={4} className="text-center text-muted-foreground">No candidates found.</TableCell>
<TableCell colSpan={4} className="text-center text-muted-foreground">{t('admin.dashboard.candidates.empty')}</TableCell>
</TableRow>
) : (
recentCandidates.map((candidate) => (

View file

@ -25,8 +25,10 @@ import {
} from "lucide-react"
import { motion } from "framer-motion"
import { getCurrentUser } from "@/lib/auth"
import { useTranslation } from "@/lib/i18n"
export function CandidateDashboardContent() {
const { t } = useTranslation()
const user = getCurrentUser()
const recommendedJobs = mockJobs.slice(0, 3)
const unreadNotifications = mockNotifications.filter((n) => !n.read)
@ -37,35 +39,35 @@ export function CandidateDashboardContent() {
return (
<Badge variant="secondary">
<Clock className="h-3 w-3 mr-1" />
Under review
{t('candidate.dashboard.status.under_review')}
</Badge>
)
case "reviewing":
return (
<Badge variant="secondary">
<AlertCircle className="h-3 w-3 mr-1" />
Under review
{t('candidate.dashboard.status.under_review')}
</Badge>
)
case "interview":
return (
<Badge className="bg-blue-500 hover:bg-blue-600">
<CheckCircle className="h-3 w-3 mr-1" />
Interview
{t('candidate.dashboard.status.interview')}
</Badge>
)
case "accepted":
return (
<Badge className="bg-green-500 hover:bg-green-600">
<CheckCircle className="h-3 w-3 mr-1" />
Accepted
{t('candidate.dashboard.status.accepted')}
</Badge>
)
case "rejected":
return (
<Badge variant="destructive">
<XCircle className="h-3 w-3 mr-1" />
Rejected
{t('candidate.dashboard.status.rejected')}
</Badge>
)
default:
@ -85,12 +87,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">Hi, {user?.name || "Candidate"}!</h1>
<h1 className="text-2xl font-bold mb-2">{t('candidate.dashboard.welcome', { name: 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" />
Edit profile
{t('candidate.dashboard.edit_profile')}
</Button>
</div>
</CardContent>
@ -105,26 +107,26 @@ export function CandidateDashboardContent() {
className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"
>
<StatsCard
title="Applications"
title={t('candidate.dashboard.stats.applications')}
value={mockApplications.length}
icon={FileText}
description="Total jobs applied to"
description={t('candidate.dashboard.stats.applications_desc')}
/>
<StatsCard
title="In progress"
title={t('candidate.dashboard.stats.in_progress')}
value={
mockApplications.filter(
(a) => a.status === "reviewing" || a.status === "interview"
).length
}
icon={Clock}
description="Awaiting a response"
description={t('candidate.dashboard.stats.in_progress_desc')}
/>
<StatsCard
title="Notifications"
title={t('candidate.dashboard.stats.notifications')}
value={unreadNotifications.length}
icon={Bell}
description="New updates"
description={t('candidate.dashboard.stats.notifications_desc')}
/>
</motion.div>
@ -139,7 +141,7 @@ export function CandidateDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>Recommended jobs</CardTitle>
<CardTitle>{t('candidate.dashboard.recommended.title')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{recommendedJobs.map((job) => (
@ -157,16 +159,16 @@ export function CandidateDashboardContent() {
>
<Card>
<CardHeader>
<CardTitle>My applications</CardTitle>
<CardTitle>{t('candidate.dashboard.applications.title')}</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Role</TableHead>
<TableHead>Company</TableHead>
<TableHead>Status</TableHead>
<TableHead>Date</TableHead>
<TableHead>{t('candidate.dashboard.applications.table.role')}</TableHead>
<TableHead>{t('candidate.dashboard.applications.table.company')}</TableHead>
<TableHead>{t('candidate.dashboard.applications.table.status')}</TableHead>
<TableHead>{t('candidate.dashboard.applications.table.date')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>

View file

@ -25,12 +25,14 @@ import { jobsApi, applicationsApi, ApiJob } from "@/lib/api"
import { User } from "@/lib/auth"
import { formatDistanceToNow } from "date-fns"
import { ptBR } from "date-fns/locale"
import { useTranslation } from "@/lib/i18n"
interface CompanyDashboardContentProps {
user: User
}
export function CompanyDashboardContent({ user }: CompanyDashboardContentProps) {
const { t } = useTranslation()
const [stats, setStats] = useState({
activeJobs: 0,
totalApplications: 0,
@ -102,16 +104,16 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-foreground mb-2">
Dashboard
{t('company.dashboard.title')}
</h1>
<p className="text-muted-foreground">
Olá, {user.name}! 👋
{t('company.dashboard.welcome', { name: user.name })} 👋
</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
{t('company.dashboard.new_job')}
</Button>
</Link>
</div>
@ -121,7 +123,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Vagas Ativas
{t('company.dashboard.stats.active_jobs')}
</CardTitle>
<Briefcase className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -130,7 +132,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
{stats.activeJobs}
</div>
<p className="text-xs text-muted-foreground mt-1">
Publicadas
{t('company.dashboard.stats.posted')}
</p>
</CardContent>
</Card>
@ -138,7 +140,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Candidaturas
{t('company.dashboard.stats.applications')}
</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -147,7 +149,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
{stats.totalApplications}
</div>
<p className="text-xs text-muted-foreground mt-1">
+{stats.thisMonth} este mês
{t('company.dashboard.stats.this_month', { count: stats.thisMonth })}
</p>
</CardContent>
</Card>
@ -155,7 +157,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Visualizações
{t('company.dashboard.stats.views')}
</CardTitle>
<Eye className="h-4 w-4 text-muted-foreground" />
</CardHeader>
@ -164,7 +166,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
-
</div>
<p className="text-xs text-muted-foreground mt-1">
Em breve
{t('company.dashboard.stats.soon')}
</p>
</CardContent>
</Card>
@ -172,14 +174,14 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Conversão
{t('company.dashboard.stats.conversion')}
</CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">-</div>
<p className="text-xs text-muted-foreground mt-1">
Em breve
{t('company.dashboard.stats.soon')}
</p>
</CardContent>
</Card>
@ -192,21 +194,21 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Vagas Recentes</CardTitle>
<CardTitle>{t('company.dashboard.recent_jobs.title')}</CardTitle>
<CardDescription>
Suas últimas vagas publicadas
{t('company.dashboard.recent_jobs.subtitle')}
</CardDescription>
</div>
<Link href="/dashboard/my-jobs">
<Button variant="ghost" size="sm">
Ver todas
{t('company.dashboard.recent_jobs.view_all')}
</Button>
</Link>
</div>
</CardHeader>
<CardContent className="space-y-4">
{recentJobs.length === 0 ? (
<p className="text-muted-foreground text-sm">Nenhuma vaga encontrada.</p>
<p className="text-muted-foreground text-sm">{t('company.dashboard.recent_jobs.empty')}</p>
) : recentJobs.map((job) => (
<div
key={job.id}
@ -239,7 +241,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<span className="flex items-center gap-1">
<Users className="h-4 w-4" />
{/* Mocking app count if not available */}
{job.applicationCount || 0} applications
{t('company.dashboard.recent_jobs.applications_count', { count: job.applicationCount || 0 })}
</span>
<span className="flex items-center gap-1">
<Calendar className="h-4 w-4" />
@ -267,19 +269,19 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Candidaturas</CardTitle>
<CardDescription>Candidatos recentes</CardDescription>
<CardTitle>{t('company.dashboard.recent_applications.title')}</CardTitle>
<CardDescription>{t('company.dashboard.recent_applications.subtitle')}</CardDescription>
</div>
<Link href="/dashboard/candidates">
<Button variant="ghost" size="sm">
Ver todas
{t('company.dashboard.recent_applications.view_all')}
</Button>
</Link>
</div>
</CardHeader>
<CardContent className="space-y-4">
{recentApplications.length === 0 ? (
<p className="text-muted-foreground text-sm">Nenhuma candidatura recente.</p>
<p className="text-muted-foreground text-sm">{t('company.dashboard.recent_applications.empty')}</p>
) : recentApplications.map((application) => (
<div
key={application.id}
@ -308,7 +310,7 @@ export function CompanyDashboardContent({ user }: CompanyDashboardContentProps)
{application.name}
</p>
<p className="text-xs text-muted-foreground truncate">
{application.jobTitle || "Vaga desconhecida"}
{application.jobTitle || t('company.dashboard.recent_applications.unknown_job')}
</p>
<p className="text-xs text-muted-foreground mt-1">
{formatDistanceToNow(new Date(application.created_at), { addSuffix: true, locale: ptBR })}

View file

@ -6,99 +6,110 @@ import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"
import { LayoutDashboard, Briefcase, Users, MessageSquare, Building2, FileText, HelpCircle, Ticket } from "lucide-react"
import { getCurrentUser, isAdminUser } from "@/lib/auth"
import { useTranslation } from "@/lib/i18n"
const adminItems = [
{
title: "Dashboard",
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: "Jobs",
href: "/dashboard/jobs",
icon: Briefcase,
},
{
title: "Candidates",
href: "/dashboard/candidates",
icon: Users,
},
{
title: "Users",
href: "/dashboard/users",
icon: Users,
},
{
title: "Companies",
href: "/dashboard/companies",
icon: Building2,
},
{
title: "Backoffice",
href: "/dashboard/backoffice",
icon: FileText,
},
{
title: "Messages",
href: "/dashboard/messages",
icon: MessageSquare,
},
{
title: "Tickets",
href: "/dashboard/tickets",
icon: Ticket,
},
]
const companyItems = [
{
title: "Dashboard",
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: "My jobs",
href: "/dashboard/my-jobs",
icon: Briefcase,
},
{
title: "Applications",
href: "/dashboard/applications",
icon: Users,
},
]
const candidateItems = [
{
title: "Dashboard",
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: "Jobs",
href: "/jobs", // Public search
icon: Briefcase,
},
{
title: "My applications",
href: "/dashboard/my-applications",
icon: FileText,
},
{
title: "Support",
href: "/dashboard/support/tickets",
icon: HelpCircle,
},
]
export function Sidebar() {
const Sidebar = () => {
const { t } = useTranslation()
const pathname = usePathname()
const user = getCurrentUser()
const isSuperadmin = user?.role === "superadmin"
const adminItems = [
{
title: t('sidebar.dashboard'),
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: t('sidebar.jobs'),
href: "/dashboard/jobs",
icon: Briefcase,
},
{
title: t('sidebar.candidates'),
href: "/dashboard/candidates",
icon: Users,
},
{
title: t('sidebar.users'),
href: "/dashboard/users",
icon: Users,
},
{
title: t('sidebar.companies'),
href: "/dashboard/companies",
icon: Building2,
},
{
title: t('sidebar.backoffice'),
href: "/dashboard/backoffice",
icon: FileText,
},
{
title: t('sidebar.messages'),
href: "/dashboard/messages",
icon: MessageSquare,
},
{
title: t('sidebar.tickets'),
href: "/dashboard/tickets",
icon: Ticket,
},
]
const companyItems = [
{
title: t('sidebar.dashboard'),
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: t('sidebar.my_jobs'),
href: "/dashboard/my-jobs",
icon: Briefcase,
},
{
title: t('sidebar.applications'),
href: "/dashboard/applications",
icon: Users,
},
{
title: t('sidebar.messages'),
href: "/dashboard/messages",
icon: MessageSquare,
},
{
title: t('sidebar.support'),
href: "/dashboard/support",
icon: HelpCircle,
},
]
const candidateItems = [
{
title: t('sidebar.dashboard'),
href: "/dashboard",
icon: LayoutDashboard,
},
{
title: t('sidebar.jobs'),
href: "/jobs",
icon: Briefcase,
},
{
title: t('sidebar.my_applications'),
href: "/dashboard/my-applications",
icon: FileText,
},
{
title: t('sidebar.support'),
href: "/dashboard/support/tickets",
icon: HelpCircle,
},
]
let items = candidateItems
if (isAdminUser(user)) {
// For Admin (not Superadmin), filter out Backoffice
items = isSuperadmin
? adminItems
: adminItems.filter(item => item.href !== "/dashboard/backoffice" && item.href !== "/dashboard/companies")
@ -108,7 +119,6 @@ export function Sidebar() {
return (
<aside className="w-64 shrink-0 border-r border-border bg-muted/30 min-h-screen flex flex-col">
{/* Branding Header with CORRECT Padding (pl-6) */}
<div className="flex h-16 shrink-0 items-center px-6 gap-3 border-b border-border">
<Link href="/" className="flex items-center gap-3 hover:opacity-80 transition-opacity">
<Image
@ -122,7 +132,6 @@ export function Sidebar() {
</Link>
</div>
{/* Navigation */}
<nav className="flex-1 p-4 space-y-2 overflow-y-auto">
{items.map((item) => {
const Icon = item.icon
@ -146,7 +155,6 @@ export function Sidebar() {
})}
</nav>
{/* Footer / User Info could go here */}
<div className="p-4 border-t border-border mt-auto">
<div className="text-xs text-muted-foreground text-center">
v1.0.0
@ -155,3 +163,6 @@ export function Sidebar() {
</aside>
)
}
export { Sidebar }

View file

@ -1,4 +1,18 @@
{
"sidebar": {
"dashboard": "Dashboard",
"jobs": "Jobs",
"candidates": "Candidates",
"users": "Users",
"companies": "Companies",
"backoffice": "Backoffice",
"messages": "Messages",
"tickets": "Tickets",
"my_jobs": "My Jobs",
"applications": "Applications",
"my_applications": "My Applications",
"support": "Support"
},
"nav": {
"jobs": "Jobs",
"about": "About",
@ -662,6 +676,42 @@
}
},
"admin": {
"dashboard": {
"title": "Dashboard",
"subtitle": "Overview of the jobs portal",
"stats": {
"activeJobs": "Active Jobs",
"activeJobsDesc": "Total posted jobs",
"candidates": "Total Candidates",
"candidatesDesc": "Registered users",
"applications": "Active Applications",
"applicationsDesc": "Current pipeline",
"hiringRate": "Hiring Rate",
"hiringRateDesc": "Applications per job"
},
"jobs": {
"title": "Job Management",
"add": "Add Job",
"table": {
"title": "Title",
"company": "Company",
"status": "Status",
"created": "Created At",
"actions": "Actions"
},
"empty": "No jobs found."
},
"candidates": {
"title": "Candidate Management",
"table": {
"name": "Name",
"email": "Email",
"location": "Location",
"actions": "Actions"
},
"empty": "No candidates found."
}
},
"users": {
"title": "User management",
"subtitle": "Manage all platform users",
@ -718,5 +768,67 @@
"load_error": "Failed to load users"
}
}
},
"company": {
"dashboard": {
"title": "Dashboard",
"welcome": "Hello, {name}!",
"new_job": "New Job",
"stats": {
"active_jobs": "Active Jobs",
"posted": "Posted",
"applications": "Applications",
"this_month": "+{count} this month",
"views": "Views",
"conversion": "Conversion",
"soon": "Coming soon"
},
"recent_jobs": {
"title": "Recent Jobs",
"subtitle": "Your latest posted jobs",
"view_all": "View all",
"empty": "No jobs found.",
"applications_count": "{count} applications"
},
"recent_applications": {
"title": "Applications",
"subtitle": "Recent candidates",
"view_all": "View all",
"empty": "No recent applications.",
"unknown_job": "Unknown job"
}
}
},
"candidate": {
"dashboard": {
"welcome": "Hi, {name}!",
"edit_profile": "Edit profile",
"stats": {
"applications": "Applications",
"applications_desc": "Total jobs applied to",
"in_progress": "In progress",
"in_progress_desc": "Awaiting a response",
"notifications": "Notifications",
"notifications_desc": "New updates"
},
"recommended": {
"title": "Recommended jobs"
},
"applications": {
"title": "My applications",
"table": {
"role": "Role",
"company": "Company",
"status": "Status",
"date": "Date"
}
},
"status": {
"under_review": "Under review",
"interview": "Interview",
"accepted": "Accepted",
"rejected": "Rejected"
}
}
}
}

View file

@ -1,4 +1,18 @@
{
"sidebar": {
"dashboard": "Panel de Control",
"jobs": "Empleos",
"candidates": "Candidatos",
"users": "Usuarios",
"companies": "Empresas",
"backoffice": "Backoffice",
"messages": "Mensajes",
"tickets": "Tickets",
"my_jobs": "Mis Empleos",
"applications": "Postulaciones",
"my_applications": "Mis Postulaciones",
"support": "Soporte"
},
"nav": {
"jobs": "Empleos",
"about": "Sobre",
@ -662,6 +676,43 @@
}
},
"admin": {
"dashboard": {
"title": "Panel de Control",
"subtitle": "Visión general del portal de empleos",
"stats": {
"activeJobs": "Empleos Activos",
"activeJobsDesc": "Total publicados",
"candidates": "Candidatos Total",
"candidatesDesc": "Usuarios registrados",
"applications": "Aplicaciones Activas",
"applicationsDesc": "En proceso",
"hiringRate": "Tasa de Contratación",
"hiringRateDesc": "Aplicaciones por empleo"
},
"jobs": {
"title": "Gestión de Empleos",
"add": "Nuevo Empleo",
"table": {
"title": "Título",
"company": "Empresa",
"status": "Estado",
"created": "Creado en",
"actions": "Acciones"
},
"empty": "No se encontraron empleos."
},
"candidates": {
"title": "Gestión de Candidatos",
"table": {
"title": "Nombre",
"name": "Nombre",
"email": "Correo",
"location": "Ubicación",
"actions": "Acciones"
},
"empty": "No se encontraron candidatos."
}
},
"users": {
"title": "Gestión de Usuarios",
"subtitle": "Gestione todos los usuarios de la plataforma",
@ -718,5 +769,67 @@
"load_error": "Error al cargar usuarios"
}
}
},
"company": {
"dashboard": {
"title": "Panel de Control",
"welcome": "¡Hola, {name}!",
"new_job": "Nuevo Empleo",
"stats": {
"active_jobs": "Empleos Activos",
"posted": "Publicados",
"applications": "Postulaciones",
"this_month": "+{count} este mes",
"views": "Vistas",
"conversion": "Conversión",
"soon": "Próximamente"
},
"recent_jobs": {
"title": "Empleos Recientes",
"subtitle": "Tus últimos empleos publicados",
"view_all": "Ver todos",
"empty": "No se encontraron empleos.",
"applications_count": "{count} postulaciones"
},
"recent_applications": {
"title": "Postulaciones",
"subtitle": "Candidatos recientes",
"view_all": "Ver todas",
"empty": "No hay postulaciones recientes.",
"unknown_job": "Empleo desconocido"
}
}
},
"candidate": {
"dashboard": {
"welcome": "¡Hola, {name}!",
"edit_profile": "Editar perfil",
"stats": {
"applications": "Postulaciones",
"applications_desc": "Total de empleos aplicados",
"in_progress": "En proceso",
"in_progress_desc": "Esperando respuesta",
"notifications": "Notificaciones",
"notifications_desc": "Nuevas actualizaciones"
},
"recommended": {
"title": "Empleos recomendados"
},
"applications": {
"title": "Mis postulaciones",
"table": {
"role": "Puesto",
"company": "Empresa",
"status": "Estado",
"date": "Fecha"
}
},
"status": {
"under_review": "En revisión",
"interview": "Entrevista",
"accepted": "Aceptado",
"rejected": "Rechazado"
}
}
}
}

View file

@ -1,4 +1,18 @@
{
"sidebar": {
"dashboard": "Dashboard",
"jobs": "Vagas",
"candidates": "Candidatos",
"users": "Usuários",
"companies": "Empresas",
"backoffice": "Backoffice",
"messages": "Mensagens",
"tickets": "Tickets",
"my_jobs": "Minhas Vagas",
"applications": "Candidaturas",
"my_applications": "Minhas Candidaturas",
"support": "Suporte"
},
"nav": {
"jobs": "Vagas",
"about": "Sobre",
@ -662,6 +676,42 @@
}
},
"admin": {
"dashboard": {
"title": "Dashboard",
"subtitle": "Visão geral do portal de vagas",
"stats": {
"activeJobs": "Vagas Ativas",
"activeJobsDesc": "Total de vagas publicadas",
"candidates": "Candidatos",
"candidatesDesc": "Usuários registrados",
"applications": "Candidaturas",
"applicationsDesc": "Em andamento",
"hiringRate": "Taxa de Contratação",
"hiringRateDesc": "Candidaturas por vaga"
},
"jobs": {
"title": "Gerenciamento de Vagas",
"add": "Nova Vaga",
"table": {
"title": "Título",
"company": "Empresa",
"status": "Status",
"created": "Criado em",
"actions": "Ações"
},
"empty": "Nenhuma vaga encontrada."
},
"candidates": {
"title": "Gerenciamento de Candidatos",
"table": {
"name": "Nome",
"email": "Email",
"location": "Localização",
"actions": "Ações"
},
"empty": "Nenhum candidato encontrado."
}
},
"users": {
"title": "Gestão de Usuários",
"subtitle": "Gerencie todos os usuários da plataforma",
@ -718,5 +768,67 @@
"load_error": "Falha ao carregar usuários"
}
}
},
"company": {
"dashboard": {
"title": "Dashboard",
"welcome": "Olá, {name}!",
"new_job": "Nova Vaga",
"stats": {
"active_jobs": "Vagas Ativas",
"posted": "Publicadas",
"applications": "Candidaturas",
"this_month": "+{count} este mês",
"views": "Visualizações",
"conversion": "Conversão",
"soon": "Em breve"
},
"recent_jobs": {
"title": "Vagas Recentes",
"subtitle": "Suas últimas vagas publicadas",
"view_all": "Ver todas",
"empty": "Nenhuma vaga encontrada.",
"applications_count": "{count} candidaturas"
},
"recent_applications": {
"title": "Candidaturas",
"subtitle": "Candidatos recentes",
"view_all": "Ver todas",
"empty": "Nenhuma candidatura recente.",
"unknown_job": "Vaga desconhecida"
}
}
},
"candidate": {
"dashboard": {
"welcome": "Olá, {name}!",
"edit_profile": "Editar perfil",
"stats": {
"applications": "Candidaturas",
"applications_desc": "Total de vagas aplicadas",
"in_progress": "Em andamento",
"in_progress_desc": "Aguardando resposta",
"notifications": "Notificações",
"notifications_desc": "Novas atualizações"
},
"recommended": {
"title": "Vagas recomendadas"
},
"applications": {
"title": "Minhas candidaturas",
"table": {
"role": "Vaga",
"company": "Empresa",
"status": "Status",
"date": "Data"
}
},
"status": {
"under_review": "Em análise",
"interview": "Entrevista",
"accepted": "Aprovado",
"rejected": "Reprovado"
}
}
}
}