feat: Integrate Data Connect and Implement Staff List View Directory

This commit is contained in:
dhinesh-m24
2026-01-31 16:54:59 +05:30
parent 48bb1c457c
commit cb25b33d04
255 changed files with 21425 additions and 109 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from "react";
import { useState, useMemo} from "react";
import { Link } from "react-router-dom";
import { Button } from "../../../common/components/ui/button";
import { Card, CardContent } from "../../../common/components/ui/card";
@@ -6,11 +6,29 @@ import { Badge } from "../../../common/components/ui/badge";
import { UserPlus, Users, Star, ChevronLeft, ChevronRight, Search } from "lucide-react";
import { motion } from "framer-motion";
import DashboardLayout from "@/features/layouts/DashboardLayout";
import { workforceService } from "@/services/workforceService";
import type { Staff, User } from "../type";
import { useListStaff, useGetStaffById } from "@/dataconnect-generated/react";
import { dataConnect } from "@/features/auth/firebase";
import { formatDistanceToNow } from "date-fns";
const ITEMS_PER_PAGE = 10;
function StaffActiveStatus({ staffId }: { staffId: string }) {
const { data: staffDetail, isLoading } = useGetStaffById(dataConnect, { id: staffId });
const getLastActiveText = (lastActive?: string) => {
if (!lastActive) return 'Never';
try {
const date = new Date(lastActive);
return formatDistanceToNow(date, { addSuffix: true });
} catch (e) {
return 'Invalid date';
}
};
if (isLoading) return <span className="animate-pulse">Loading...</span>;
return <span>{getLastActiveText(staffDetail?.staff?.updatedAt)}</span>;
}
export default function StaffList() {
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = useState("all");
@@ -18,28 +36,11 @@ export default function StaffList() {
const [ratingRange, setRatingRange] = useState<[number, number]>([0, 5]);
const [currentPage, setCurrentPage] = useState(1);
const [user, setUser] = useState<User | null>(null);
const [staff, setStaff] = useState<Staff[]>([]);
const [isLoading, setIsLoading] = useState(true);
const { data: staffData, isLoading } = useListStaff(dataConnect);
console.log(user);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const currentUser = await workforceService.auth.me();
setUser(currentUser);
const staffList = await workforceService.entities.Staff.list('-created_date');
setStaff(staffList);
} catch (error) {
console.error("Failed to fetch data", error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
const staff = useMemo(() => {
return staffData?.staffs || [];
}, [staffData]);
const allSkills = useMemo(() => {
const skillSet = new Set<string>();
@@ -54,10 +55,10 @@ export default function StaffList() {
const filteredStaff = useMemo(() => {
return staff.filter(member => {
const matchesSearch = !searchTerm ||
member.employee_name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
member.fullName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
member.email?.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus = statusFilter === "all" || member.status?.toLowerCase() === statusFilter.toLowerCase();
const matchesStatus = statusFilter === "all"; // status field is missing in current schema
const matchesSkills = skillsFilter.length === 0 ||
(member.skills && skillsFilter.some(skill => member.skills?.includes(skill)));
@@ -97,22 +98,6 @@ export default function StaffList() {
}
};
const getLastActiveText = (lastActive?: string) => {
if (!lastActive) return 'Never';
const date = new Date(lastActive);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);
if (diffMins < 1) return 'Just now';
if (diffMins < 60) return `${diffMins}m ago`;
if (diffHours < 24) return `${diffHours}h ago`;
if (diffDays < 7) return `${diffDays}d ago`;
return date.toLocaleDateString();
};
return (
<DashboardLayout
title="Staff Directory"
@@ -174,7 +159,7 @@ export default function StaffList() {
<div className="flex flex-col gap-2">
<label className="text-xs font-bold uppercase text-muted-foreground">Rating Range</label>
<div className="space-y-2">
<div className="flex gap-2">
<div className="flex gap-2 mt-1">
<input
type="number"
min="0"
@@ -208,7 +193,6 @@ export default function StaffList() {
placeholder="Max"
/>
</div>
<span className="text-xs text-muted-foreground block text-center">{ratingRange[0].toFixed(1)} - {ratingRange[1].toFixed(1)}</span>
</div>
</div>
@@ -306,21 +290,21 @@ export default function StaffList() {
>
<td className="py-4 px-6">
<Link to={`/staff/${member.id}/edit`} className="font-bold text-foreground group-hover:text-primary transition-colors hover:underline">
{member.employee_name || 'N/A'}
{member.fullName || 'N/A'}
</Link>
</td>
<td className="py-4 px-6">
<div className="w-10 h-10 bg-primary/10 rounded-xl flex items-center justify-center text-primary font-black text-sm border border-primary/20 group-hover:scale-110 transition-premium">
{member.photo ? (
<img src={member.photo} alt={member.employee_name} className="w-full h-full rounded-xl object-cover" />
{member.photoUrl ? (
<img src={member.photoUrl} alt={member.fullName} className="w-full h-full rounded-xl object-cover" />
) : (
member.employee_name?.charAt(0) || '?'
member.fullName?.charAt(0) || '?'
)}
</div>
</td>
<td className="py-4 px-6 text-center">
<Badge className={`${getStatusColor(member.status)} font-black text-xs border`}>
{member.status || 'Active'}
<Badge className={`${getStatusColor('Active')} font-black text-xs border`}>
Active
</Badge>
</td>
<td className="py-4 px-6">
@@ -348,9 +332,9 @@ export default function StaffList() {
</div>
</td>
<td className="py-4 px-6 text-sm text-muted-foreground">
{getLastActiveText(member.last_active)}
<StaffActiveStatus staffId={member.id} />
</td>
</motion.tr>
</motion.tr>
))}
</tbody>
</table>