441 lines
15 KiB
TypeScript
441 lines
15 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { DashboardHeader } from "@/components/dashboard-header";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
CardDescription,
|
|
} from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import {
|
|
TrendingUp,
|
|
TrendingDown,
|
|
Users,
|
|
Briefcase,
|
|
Eye,
|
|
UserCheck,
|
|
Clock,
|
|
Target,
|
|
Download,
|
|
Calendar,
|
|
} from "lucide-react";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Progress } from "@/components/ui/progress";
|
|
|
|
export default function CompanyReportsPage() {
|
|
const [period, setPeriod] = useState("30");
|
|
|
|
const stats = [
|
|
{
|
|
title: "Total de Vagas Ativas",
|
|
value: "8",
|
|
change: "+2",
|
|
changeType: "positive" as const,
|
|
icon: Briefcase,
|
|
description: "2 novas vagas este mês",
|
|
},
|
|
{
|
|
title: "Candidaturas Recebidas",
|
|
value: "156",
|
|
change: "+23%",
|
|
changeType: "positive" as const,
|
|
icon: Users,
|
|
description: "vs. mês anterior",
|
|
},
|
|
{
|
|
title: "Taxa de Conversão",
|
|
value: "12.8%",
|
|
change: "+3.2%",
|
|
changeType: "positive" as const,
|
|
icon: Target,
|
|
description: "candidatos contratados",
|
|
},
|
|
{
|
|
title: "Tempo Médio de Contratação",
|
|
value: "18 dias",
|
|
change: "-3 dias",
|
|
changeType: "positive" as const,
|
|
icon: Clock,
|
|
description: "vs. mês anterior",
|
|
},
|
|
];
|
|
|
|
const jobPerformance = [
|
|
{
|
|
title: "Desenvolvedor Full Stack Sênior",
|
|
views: 234,
|
|
applications: 45,
|
|
conversionRate: 19.2,
|
|
status: "Ativa",
|
|
daysOpen: 12,
|
|
},
|
|
{
|
|
title: "Designer UX/UI",
|
|
views: 189,
|
|
applications: 38,
|
|
conversionRate: 20.1,
|
|
status: "Ativa",
|
|
daysOpen: 8,
|
|
},
|
|
{
|
|
title: "Product Manager",
|
|
views: 167,
|
|
applications: 29,
|
|
conversionRate: 17.4,
|
|
status: "Ativa",
|
|
daysOpen: 15,
|
|
},
|
|
{
|
|
title: "DevOps Engineer",
|
|
views: 145,
|
|
applications: 22,
|
|
conversionRate: 15.2,
|
|
status: "Pausada",
|
|
daysOpen: 20,
|
|
},
|
|
{
|
|
title: "Data Scientist",
|
|
views: 198,
|
|
applications: 34,
|
|
conversionRate: 17.2,
|
|
status: "Ativa",
|
|
daysOpen: 10,
|
|
},
|
|
];
|
|
|
|
const funnelData = [
|
|
{
|
|
stage: "Visualizações",
|
|
count: 1250,
|
|
percentage: 100,
|
|
color: "bg-blue-500",
|
|
},
|
|
{
|
|
stage: "Candidaturas",
|
|
count: 156,
|
|
percentage: 12.5,
|
|
color: "bg-green-500",
|
|
},
|
|
{ stage: "Em Análise", count: 89, percentage: 7.1, color: "bg-yellow-500" },
|
|
{
|
|
stage: "Entrevistas",
|
|
count: 34,
|
|
percentage: 2.7,
|
|
color: "bg-orange-500",
|
|
},
|
|
{
|
|
stage: "Contratados",
|
|
count: 20,
|
|
percentage: 1.6,
|
|
color: "bg-purple-500",
|
|
},
|
|
];
|
|
|
|
const topSources = [
|
|
{ source: "LinkedIn", applications: 67, percentage: 43 },
|
|
{ source: "Busca Orgânica", applications: 45, percentage: 29 },
|
|
{ source: "Indeed", applications: 28, percentage: 18 },
|
|
{ source: "Indicações", applications: 16, percentage: 10 },
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
<DashboardHeader />
|
|
|
|
<main className="container mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
|
|
<div className="max-w-7xl mx-auto space-y-6">
|
|
{/* Header */}
|
|
<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">
|
|
Relatórios e Analytics
|
|
</h1>
|
|
<p className="text-muted-foreground">
|
|
Acompanhe o desempenho das suas vagas
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-2">
|
|
<Select value={period} onValueChange={setPeriod}>
|
|
<SelectTrigger className="w-full sm:w-[180px]">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="7">Últimos 7 dias</SelectItem>
|
|
<SelectItem value="30">Últimos 30 dias</SelectItem>
|
|
<SelectItem value="90">Últimos 90 dias</SelectItem>
|
|
<SelectItem value="365">Último ano</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
<Button variant="outline" className="gap-2">
|
|
<Download className="h-4 w-4" />
|
|
Exportar
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats Grid */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{stats.map((stat, index) => {
|
|
const Icon = stat.icon;
|
|
return (
|
|
<Card key={index}>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div className="p-2 bg-primary/10 rounded-lg">
|
|
<Icon className="h-5 w-5 text-primary" />
|
|
</div>
|
|
<Badge
|
|
variant={
|
|
stat.changeType === "positive"
|
|
? "default"
|
|
: "destructive"
|
|
}
|
|
>
|
|
{stat.changeType === "positive" ? (
|
|
<TrendingUp className="h-3 w-3 mr-1" />
|
|
) : (
|
|
<TrendingDown className="h-3 w-3 mr-1" />
|
|
)}
|
|
{stat.change}
|
|
</Badge>
|
|
</div>
|
|
<h3 className="text-2xl font-bold mb-1">{stat.value}</h3>
|
|
<p className="text-sm font-medium text-foreground mb-1">
|
|
{stat.title}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
{stat.description}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Funnel */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Funil de Conversão</CardTitle>
|
|
<CardDescription>
|
|
Acompanhe o fluxo de candidatos em cada etapa do processo
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{funnelData.map((item, index) => (
|
|
<div key={index} className="space-y-2">
|
|
<div className="flex items-center justify-between text-sm">
|
|
<div className="flex items-center gap-2">
|
|
<div className={`h-3 w-3 rounded-full ${item.color}`} />
|
|
<span className="font-medium">{item.stage}</span>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-muted-foreground">
|
|
{item.count} candidatos
|
|
</span>
|
|
<span className="font-medium min-w-[60px] text-right">
|
|
{item.percentage}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<Progress value={item.percentage} className="h-2" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-6 p-4 bg-muted/50 rounded-lg">
|
|
<div className="flex items-start gap-2">
|
|
<Target className="h-5 w-5 text-primary mt-0.5" />
|
|
<div>
|
|
<p className="font-medium text-sm mb-1">Taxa de Sucesso</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
1.6% dos visitantes se tornam contratações. A média do
|
|
mercado é 1.2%.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div className="grid lg:grid-cols-2 gap-6">
|
|
{/* Job Performance */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Desempenho por Vaga</CardTitle>
|
|
<CardDescription>
|
|
Métricas das vagas mais visualizadas
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{jobPerformance.map((job, index) => (
|
|
<div
|
|
key={index}
|
|
className="p-4 border rounded-lg space-y-3"
|
|
>
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="flex-1 min-w-0">
|
|
<h4 className="font-medium text-sm mb-1 truncate">
|
|
{job.title}
|
|
</h4>
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
<Badge
|
|
variant={
|
|
job.status === "Ativa" ? "default" : "secondary"
|
|
}
|
|
className="text-xs"
|
|
>
|
|
{job.status}
|
|
</Badge>
|
|
<span className="flex items-center gap-1">
|
|
<Calendar className="h-3 w-3" />
|
|
{job.daysOpen} dias
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-3 gap-2">
|
|
<div>
|
|
<div className="flex items-center gap-1 text-muted-foreground mb-1">
|
|
<Eye className="h-3 w-3" />
|
|
<span className="text-xs">Visualizações</span>
|
|
</div>
|
|
<p className="text-lg font-semibold">{job.views}</p>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-muted-foreground mb-1">
|
|
<Users className="h-3 w-3" />
|
|
<span className="text-xs">Candidaturas</span>
|
|
</div>
|
|
<p className="text-lg font-semibold">
|
|
{job.applications}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-muted-foreground mb-1">
|
|
<Target className="h-3 w-3" />
|
|
<span className="text-xs">Taxa</span>
|
|
</div>
|
|
<p className="text-lg font-semibold">
|
|
{job.conversionRate}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Top Sources */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Principais Fontes</CardTitle>
|
|
<CardDescription>De onde vêm suas candidaturas</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{topSources.map((source, index) => (
|
|
<div key={index} className="space-y-2">
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="font-medium">{source.source}</span>
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-muted-foreground">
|
|
{source.applications} candidaturas
|
|
</span>
|
|
<span className="font-medium min-w-[50px] text-right">
|
|
{source.percentage}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<Progress value={source.percentage} className="h-2" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-6 p-4 bg-muted/50 rounded-lg">
|
|
<p className="text-sm text-muted-foreground">
|
|
<strong className="text-foreground">LinkedIn</strong> é sua
|
|
fonte mais efetiva, gerando 43% de todas as candidaturas.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Time to Hire Chart */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Tempo de Contratação por Cargo</CardTitle>
|
|
<CardDescription>
|
|
Tempo médio desde a publicação até a contratação
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{[
|
|
{ role: "Desenvolvedor Full Stack", days: 15, maxDays: 30 },
|
|
{ role: "Designer UX/UI", days: 12, maxDays: 30 },
|
|
{ role: "Product Manager", days: 22, maxDays: 30 },
|
|
{ role: "DevOps Engineer", days: 18, maxDays: 30 },
|
|
{ role: "Data Scientist", days: 20, maxDays: 30 },
|
|
].map((item, index) => (
|
|
<div key={index} className="space-y-2">
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="font-medium">{item.role}</span>
|
|
<span className="text-muted-foreground">
|
|
{item.days} dias
|
|
</span>
|
|
</div>
|
|
<Progress
|
|
value={(item.days / item.maxDays) * 100}
|
|
className="h-2"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-6 grid grid-cols-2 gap-4">
|
|
<div className="p-4 bg-green-500/10 border border-green-500/20 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<Clock className="h-4 w-4 text-green-600" />
|
|
<span className="text-sm font-medium">Mais Rápido</span>
|
|
</div>
|
|
<p className="text-2xl font-bold">12 dias</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Designer UX/UI
|
|
</p>
|
|
</div>
|
|
|
|
<div className="p-4 bg-orange-500/10 border border-orange-500/20 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<Clock className="h-4 w-4 text-orange-600" />
|
|
<span className="text-sm font-medium">Mais Lento</span>
|
|
</div>
|
|
<p className="text-2xl font-bold">22 dias</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Product Manager
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|