385 lines
18 KiB
TypeScript
385 lines
18 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
import { Input } from "@/components/ui/input"
|
|
import {
|
|
Search,
|
|
Plus,
|
|
Edit,
|
|
Trash2,
|
|
Eye,
|
|
Users,
|
|
Calendar,
|
|
MapPin,
|
|
DollarSign,
|
|
MoreVertical,
|
|
Briefcase,
|
|
Clock,
|
|
TrendingUp,
|
|
Copy,
|
|
ExternalLink,
|
|
Pause,
|
|
Play,
|
|
Zap,
|
|
} from "lucide-react"
|
|
import Link from "next/link"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
|
|
// Mock data - in production this would come from API
|
|
const mockJobs = [
|
|
{
|
|
id: "1",
|
|
title: "Senior Full Stack Developer",
|
|
type: "Full Time",
|
|
location: "São Paulo, SP",
|
|
workMode: "hybrid",
|
|
salary: "R$ 12,000 - R$ 18,000",
|
|
applications: 45,
|
|
views: 320,
|
|
postedAt: "2 days ago",
|
|
expiresAt: "28 days left",
|
|
status: "active",
|
|
},
|
|
{
|
|
id: "2",
|
|
title: "Designer UX/UI",
|
|
type: "Full Time",
|
|
location: "Remote",
|
|
workMode: "remote",
|
|
salary: "R$ 8,000 - R$ 12,000",
|
|
applications: 32,
|
|
views: 256,
|
|
postedAt: "5 days ago",
|
|
expiresAt: "25 days left",
|
|
status: "active",
|
|
},
|
|
{
|
|
id: "3",
|
|
title: "Product Manager",
|
|
type: "Full Time",
|
|
location: "São Paulo, SP",
|
|
workMode: "onsite",
|
|
salary: "R$ 15,000 - R$ 20,000",
|
|
applications: 28,
|
|
views: 189,
|
|
postedAt: "1 week ago",
|
|
expiresAt: "21 days left",
|
|
status: "active",
|
|
},
|
|
{
|
|
id: "4",
|
|
title: "DevOps Engineer",
|
|
type: "Full Time",
|
|
location: "São Paulo, SP",
|
|
workMode: "hybrid",
|
|
salary: "R$ 14,000 - R$ 20,000",
|
|
applications: 15,
|
|
views: 98,
|
|
postedAt: "2 weeks ago",
|
|
expiresAt: "14 days left",
|
|
status: "paused",
|
|
},
|
|
{
|
|
id: "5",
|
|
title: "Junior Frontend Developer",
|
|
type: "Full Time",
|
|
location: "São Paulo, SP",
|
|
workMode: "onsite",
|
|
salary: "R$ 4,000 - R$ 6,000",
|
|
applications: 120,
|
|
views: 450,
|
|
postedAt: "1 month ago",
|
|
expiresAt: "Expired",
|
|
status: "closed",
|
|
},
|
|
]
|
|
|
|
const statusConfig = {
|
|
active: { label: "Active", color: "bg-green-100 text-green-800 border-green-200" },
|
|
paused: { label: "Paused", color: "bg-yellow-100 text-yellow-800 border-yellow-200" },
|
|
closed: { label: "Closed", color: "bg-gray-100 text-gray-800 border-gray-200" },
|
|
draft: { label: "Draft", color: "bg-blue-100 text-blue-800 border-blue-200" },
|
|
}
|
|
|
|
const workModeConfig = {
|
|
remote: { label: "Remote", color: "bg-cyan-100 text-cyan-800" },
|
|
hybrid: { label: "Hybrid", color: "bg-violet-100 text-violet-800" },
|
|
onsite: { label: "On-site", color: "bg-orange-100 text-orange-800" },
|
|
}
|
|
|
|
export default function MyJobsPage() {
|
|
const [jobs] = useState(mockJobs)
|
|
const [statusFilter, setStatusFilter] = useState("all")
|
|
const [searchTerm, setSearchTerm] = useState("")
|
|
|
|
const filteredJobs = jobs.filter((job) => {
|
|
const matchesStatus = statusFilter === "all" || job.status === statusFilter
|
|
const matchesSearch =
|
|
job.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
job.location.toLowerCase().includes(searchTerm.toLowerCase())
|
|
return matchesStatus && matchesSearch
|
|
})
|
|
|
|
const stats = {
|
|
total: jobs.length,
|
|
active: jobs.filter((j) => j.status === "active").length,
|
|
applications: jobs.reduce((acc, j) => acc + j.applications, 0),
|
|
views: jobs.reduce((acc, j) => acc + j.views, 0),
|
|
}
|
|
|
|
return (
|
|
<div className="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">
|
|
My Jobs
|
|
</h1>
|
|
<p className="text-muted-foreground">
|
|
Manage your job postings
|
|
</p>
|
|
</div>
|
|
<Button size="lg" className="w-full sm:w-auto">
|
|
<Plus className="h-5 w-5 mr-2" />
|
|
Post New Job
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Stats */}
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2">
|
|
<Briefcase className="h-5 w-5 text-muted-foreground" />
|
|
<div>
|
|
<div className="text-2xl font-bold">{stats.total}</div>
|
|
<p className="text-xs text-muted-foreground">Total Jobs</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2">
|
|
<TrendingUp className="h-5 w-5 text-green-500" />
|
|
<div>
|
|
<div className="text-2xl font-bold text-green-600">{stats.active}</div>
|
|
<p className="text-xs text-muted-foreground">Active</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2">
|
|
<Users className="h-5 w-5 text-blue-500" />
|
|
<div>
|
|
<div className="text-2xl font-bold text-blue-600">{stats.applications}</div>
|
|
<p className="text-xs text-muted-foreground">Applications</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2">
|
|
<Eye className="h-5 w-5 text-purple-500" />
|
|
<div>
|
|
<div className="text-2xl font-bold text-purple-600">{stats.views}</div>
|
|
<p className="text-xs text-muted-foreground">Total Views</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Filters */}
|
|
<div className="flex flex-col sm:flex-row gap-4">
|
|
<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="Search jobs..."
|
|
className="pl-10"
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
|
<SelectTrigger className="w-full sm:w-[180px]">
|
|
<SelectValue placeholder="Filter by status" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="all">All Status</SelectItem>
|
|
<SelectItem value="active">Active</SelectItem>
|
|
<SelectItem value="paused">Paused</SelectItem>
|
|
<SelectItem value="closed">Closed</SelectItem>
|
|
<SelectItem value="draft">Draft</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* Jobs List */}
|
|
<div className="space-y-4">
|
|
{filteredJobs.length === 0 ? (
|
|
<Card>
|
|
<CardContent className="p-8 text-center text-muted-foreground">
|
|
<Briefcase className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
|
<h3 className="font-semibold text-lg mb-2">No jobs found</h3>
|
|
<p>Start by posting your first job.</p>
|
|
<Button className="mt-4">
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Post New Job
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
) : (
|
|
filteredJobs.map((job) => (
|
|
<Card key={job.id} className="hover:border-primary/50 transition-colors">
|
|
<CardContent className="p-4 sm:p-6">
|
|
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
|
{/* Job Info */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-start justify-between gap-2 mb-2">
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
<h3 className="font-semibold text-lg">{job.title}</h3>
|
|
<Badge
|
|
variant="outline"
|
|
className={statusConfig[job.status as keyof typeof statusConfig].color}
|
|
>
|
|
{statusConfig[job.status as keyof typeof statusConfig].label}
|
|
</Badge>
|
|
<Badge
|
|
variant="secondary"
|
|
className={workModeConfig[job.workMode as keyof typeof workModeConfig].color}
|
|
>
|
|
{workModeConfig[job.workMode as keyof typeof workModeConfig].label}
|
|
</Badge>
|
|
</div>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="icon" className="shrink-0">
|
|
<MoreVertical className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuItem>
|
|
<Eye className="h-4 w-4 mr-2" />
|
|
View
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Edit className="h-4 w-4 mr-2" />
|
|
Edit
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Copy className="h-4 w-4 mr-2" />
|
|
Duplicate
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<ExternalLink className="h-4 w-4 mr-2" />
|
|
Preview
|
|
</DropdownMenuItem>
|
|
|
|
{/* Boost Feature (Roadmap) */}
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="text-amber-600 font-medium cursor-pointer">
|
|
<Zap className="h-4 w-4 mr-2" />
|
|
Boost Job
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
|
|
{job.status === "active" ? (
|
|
<DropdownMenuItem>
|
|
<Pause className="h-4 w-4 mr-2" />
|
|
Pause
|
|
</DropdownMenuItem>
|
|
) : job.status === "paused" ? (
|
|
<DropdownMenuItem>
|
|
<Play className="h-4 w-4 mr-2" />
|
|
Activate
|
|
</DropdownMenuItem>
|
|
) : null}
|
|
<DropdownMenuItem className="text-destructive">
|
|
<Trash2 className="h-4 w-4 mr-2" />
|
|
Delete
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-4 text-sm text-muted-foreground mb-3">
|
|
<span className="flex items-center gap-1">
|
|
<MapPin className="h-4 w-4" />
|
|
{job.location}
|
|
</span>
|
|
<span className="flex items-center gap-1">
|
|
<DollarSign className="h-4 w-4" />
|
|
{job.salary}
|
|
</span>
|
|
<span className="flex items-center gap-1">
|
|
<Clock className="h-4 w-4" />
|
|
{job.expiresAt}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-6 text-sm">
|
|
<div className="flex items-center gap-2">
|
|
<Users className="h-4 w-4 text-blue-500" />
|
|
<span className="font-medium">{job.applications}</span>
|
|
<span className="text-muted-foreground">applications</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Eye className="h-4 w-4 text-purple-500" />
|
|
<span className="font-medium">{job.views}</span>
|
|
<span className="text-muted-foreground">views</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Calendar className="h-4 w-4 text-gray-500" />
|
|
<span className="text-muted-foreground">Posted {job.postedAt}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex flex-row lg:flex-col gap-2">
|
|
<Link href={`/dashboard/applications?job=${job.id}`} className="flex-1 lg:flex-none">
|
|
<Button variant="outline" size="sm" className="w-full">
|
|
<Users className="h-4 w-4 mr-2" />
|
|
Applications
|
|
</Button>
|
|
</Link>
|
|
<Button variant="outline" size="sm" className="flex-1 lg:flex-none">
|
|
<Edit className="h-4 w-4 mr-2" />
|
|
Edit
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|