From 11ba0753b5c5d5dd32445f242af38f6117457e67 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Mon, 10 Nov 2025 04:47:27 +0000 Subject: [PATCH] Create Staff Directory page to browse team members cgen-08a324ec531e49cbafa937f8fca1c4aa --- client/pages/staff/StaffDirectory.tsx | 173 ++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 client/pages/staff/StaffDirectory.tsx diff --git a/client/pages/staff/StaffDirectory.tsx b/client/pages/staff/StaffDirectory.tsx new file mode 100644 index 00000000..39b2bdf4 --- /dev/null +++ b/client/pages/staff/StaffDirectory.tsx @@ -0,0 +1,173 @@ +import { useEffect, useState } from "react"; +import Layout from "@/components/Layout"; +import { useAuth } from "@/contexts/AuthContext"; +import { useNavigate } from "react-router-dom"; +import { aethexToast } from "@/lib/aethex-toast"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Search, Mail, Phone, Briefcase } from "lucide-react"; + +interface StaffMember { + id: string; + full_name: string; + email: string; + role: string; + department?: string; + position?: string; + avatar_url?: string; + phone?: string; +} + +export default function StaffDirectory() { + const { user, loading } = useAuth(); + const navigate = useNavigate(); + const [staffMembers, setStaffMembers] = useState([]); + const [filteredMembers, setFilteredMembers] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + if (!loading && !user) { + navigate("/staff/login"); + } + }, [user, loading, navigate]); + + useEffect(() => { + const fetchMembers = async () => { + try { + const response = await fetch("/api/staff/members"); + if (response.ok) { + const data = await response.json(); + setStaffMembers(Array.isArray(data) ? data : []); + setFilteredMembers(Array.isArray(data) ? data : []); + } else { + aethexToast.error({ + title: "Failed to load directory", + description: "Could not fetch staff members", + }); + } + } catch (error) { + console.error("Error fetching staff members:", error); + aethexToast.error({ + title: "Error", + description: "Failed to load staff directory", + }); + } finally { + setIsLoading(false); + } + }; + + if (user) fetchMembers(); + }, [user]); + + useEffect(() => { + if (searchQuery.trim() === "") { + setFilteredMembers(staffMembers); + } else { + const query = searchQuery.toLowerCase(); + const filtered = staffMembers.filter( + (member) => + member.full_name.toLowerCase().includes(query) || + member.email.toLowerCase().includes(query) || + member.department?.toLowerCase().includes(query) + ); + setFilteredMembers(filtered); + } + }, [searchQuery, staffMembers]); + + if (loading) return
Loading...
; + + return ( + +
+
+
+

Team Directory

+

Find and connect with AeThex team members

+
+ + {/* Search Bar */} + + +
+ + setSearchQuery(e.target.value)} + className="pl-10 bg-slate-800 border-slate-700 text-white placeholder-slate-500" + /> +
+
+
+ + {/* Results */} + {isLoading ? ( +
Loading team members...
+ ) : filteredMembers.length === 0 ? ( +
+ No staff members found matching your search +
+ ) : ( +
+ {filteredMembers.map((member) => ( + + +
+
+ {member.full_name} +

{member.position || "Team Member"}

+
+ {member.role && ( + + {member.role} + + )} +
+
+ + + {member.phone && ( + + )} + {member.department && ( +
+ + {member.department} +
+ )} +
+
+ ))} +
+ )} + + {/* Stats */} +
+

+ Showing {filteredMembers.length} of {staffMembers.length} team members +

+
+
+
+
+ ); +}