Files
krow_talent_app/src/pages/Positions.jsx
2026-08-10 19:36:45 +05:30

97 lines
4.2 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Search, Plus } from 'lucide-react';
import { useJobPostings, useApplications } from '@/lib/krowHooks';
import { base44 } from '@/api/base44Client';
import KROWJobPostingCard from '@/components/krow/KROWJobPostingCard';
import CertificationManager from '@/components/krow/CertificationManager';
import KrowAiChat from '@/components/krow/KrowAiChat';
export default function Positions() {
const navigate = useNavigate();
const { data: postings = [], isLoading } = useJobPostings();
const { data: applications = [] } = useApplications();
const [search, setSearch] = useState('');
const [statusFilter, setStatusFilter] = useState('all');
const [isAdmin, setIsAdmin] = useState(false);
useEffect(() => {
base44.auth.me().then(u => setIsAdmin(u?.role === 'admin')).catch(() => {});
}, []);
const filtered = postings.filter(p => {
const matchesSearch = !search || p.title.toLowerCase().includes(search.toLowerCase()) || p.role_category?.toLowerCase().includes(search.toLowerCase());
const matchesStatus = statusFilter === 'all' || p.status === statusFilter;
return matchesSearch && matchesStatus;
});
const statsForPosting = (postingId) => {
const apps = applications.filter(a => a.job_posting_id === postingId);
return {
applied: apps.length,
screened: apps.filter(a => a.status !== 'applied').length,
hired: apps.filter(a => a.status === 'hired').length,
};
};
return (
<div className="space-y-5">
<div>
<h1 className="text-[22px] font-bold text-[#111827]">Positions</h1>
<p className="text-[13px] text-[#6B7280] mt-0.5">{postings.length} job postings</p>
</div>
{/* Action Bar */}
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[#9CA3AF]" />
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search positions..."
className="w-full h-10 pl-10 pr-4 rounded-lg border border-[#E5E7EB] bg-white text-[13px] focus:border-[#2563EB] focus:outline-none"
/>
</div>
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="h-10 px-4 rounded-lg border border-[#E5E7EB] bg-white text-[13px] focus:border-[#2563EB] focus:outline-none"
>
<option value="all">All Statuses</option>
<option value="active">Hiring</option>
<option value="draft">Draft</option>
<option value="paused">Paused</option>
<option value="closed">Closed</option>
</select>
{isAdmin && <CertificationManager />}
<button
onClick={() => navigate('/positions/new')}
className="inline-flex items-center justify-center gap-1.5 bg-[#111827] hover:bg-[#1F2937] text-white text-[13px] font-medium px-4 h-10 rounded-lg transition-colors whitespace-nowrap"
>
<Plus className="w-4 h-4" /> New Position
</button>
</div>
{/* Grid */}
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{[...Array(6)].map((_, i) => <div key={i} className="h-48 bg-white border border-[#E5E7EB] rounded-xl animate-pulse" />)}
</div>
) : filtered.length === 0 ? (
<div className="text-center py-16">
<p className="text-[14px] text-[#6B7280] mb-3">No positions found</p>
<button onClick={() => navigate('/positions/new')} className="inline-flex items-center gap-1.5 bg-[#2563EB] text-white text-[13px] font-medium px-4 py-2 rounded-lg">
<Plus className="w-4 h-4" /> Create your first position
</button>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filtered.map(posting => (
<KROWJobPostingCard key={posting.id} posting={posting} stats={statsForPosting(posting.id)} />
))}
</div>
)}
<KrowAiChat page="positions" />
</div>
);
}