feat: Implement Staff View
This commit is contained in:
@@ -4,17 +4,36 @@ import { Button } from "@/common/components/ui/button";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import StaffForm from "./components/StaffForm";
|
||||
import DashboardLayout from "@/features/layouts/DashboardLayout";
|
||||
import { workforceService } from "@/services/workforceService";
|
||||
import { useCreateStaff } from "@/dataconnect-generated/react";
|
||||
import { dataConnect } from "@/features/auth/firebase";
|
||||
import type { Staff } from "../type";
|
||||
|
||||
export default function AddStaff() {
|
||||
const navigate = useNavigate();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { mutateAsync: createStaff } = useCreateStaff(dataConnect);
|
||||
|
||||
const handleSubmit = async (staffData: Omit<Staff, 'id'>) => {
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await workforceService.entities.Staff.create(staffData);
|
||||
await createStaff({
|
||||
userId: `user_${Math.random().toString(36).substr(2, 9)}`, // Placeholder UID
|
||||
fullName: staffData.employee_name,
|
||||
role: staffData.position,
|
||||
level: staffData.profile_type,
|
||||
email: staffData.email,
|
||||
phone: staffData.contact_number,
|
||||
photoUrl: staffData.photo,
|
||||
initial: staffData.initial,
|
||||
bio: staffData.notes,
|
||||
skills: staffData.skills,
|
||||
averageRating: staffData.averageRating,
|
||||
reliabilityScore: staffData.reliability_score,
|
||||
onTimeRate: staffData.shift_coverage_percentage,
|
||||
totalShifts: staffData.total_shifts,
|
||||
city: staffData.city,
|
||||
addres: staffData.address,
|
||||
});
|
||||
navigate("/staff");
|
||||
} catch (error) {
|
||||
console.error("Failed to create staff", error);
|
||||
|
||||
@@ -1,48 +1,75 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Button } from "@/common/components/ui/button";
|
||||
import { ArrowLeft, Loader2 } from "lucide-react";
|
||||
import { ArrowLeft, Loader2, Edit, Save, X, User, FileText, Briefcase, Shield } from "lucide-react";
|
||||
import StaffForm from "./components/StaffForm";
|
||||
import DashboardLayout from "@/features/layouts/DashboardLayout";
|
||||
import { workforceService } from "@/services/workforceService";
|
||||
import { useGetStaffById, useUpdateStaff } from "@/dataconnect-generated/react";
|
||||
import { dataConnect } from "@/features/auth/firebase";
|
||||
import type { Staff } from "../type";
|
||||
import { Badge } from "@/common/components/ui/badge";
|
||||
|
||||
export default function EditStaff() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [staff, setStaff] = useState<Staff | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchStaff = async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const staffList = await workforceService.entities.Staff.list();
|
||||
const foundStaff = staffList.find(s => s.id === id);
|
||||
if (foundStaff) {
|
||||
setStaff(foundStaff);
|
||||
} else {
|
||||
console.error("Staff member not found");
|
||||
navigate("/staff");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch staff", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchStaff();
|
||||
}, [id, navigate]);
|
||||
const { data: staffData, isLoading, refetch } = useGetStaffById(dataConnect, { id: id || "" });
|
||||
const { mutateAsync: updateStaff } = useUpdateStaff(dataConnect);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const staff = useMemo(() => {
|
||||
if (!staffData?.staff) return null;
|
||||
const s = staffData.staff;
|
||||
return {
|
||||
id: s.id,
|
||||
employee_name: s.fullName,
|
||||
initial: s.initial || "",
|
||||
position: s.role || "",
|
||||
position_2: "",
|
||||
profile_type: s.level || "",
|
||||
employment_type: s.employmentType || "",
|
||||
manager: s.manager || "",
|
||||
email: s.email || "",
|
||||
contact_number: s.phone || "",
|
||||
phone: s.phone || "",
|
||||
photo: s.photoUrl || "",
|
||||
status: "Active", // Default for now
|
||||
skills: (s.skills as string[]) || [],
|
||||
averageRating: s.averageRating || 0,
|
||||
reliability_score: s.reliabilityScore || 0,
|
||||
shift_coverage_percentage: s.onTimeRate || 0,
|
||||
total_shifts: s.totalShifts || 0,
|
||||
department: s.department || "",
|
||||
city: s.city || "",
|
||||
address: s.addres || "", // Using misspelled field from schema
|
||||
notes: s.bio || "",
|
||||
} as Staff;
|
||||
}, [staffData]);
|
||||
|
||||
const handleSubmit = async (staffData: Staff) => {
|
||||
if (!id) return;
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await workforceService.entities.Staff.update(id, staffData);
|
||||
setStaff(staffData);
|
||||
await updateStaff({
|
||||
id: id,
|
||||
fullName: staffData.employee_name,
|
||||
role: staffData.position,
|
||||
level: staffData.profile_type,
|
||||
email: staffData.email,
|
||||
phone: staffData.contact_number,
|
||||
photoUrl: staffData.photo,
|
||||
initial: staffData.initial,
|
||||
bio: staffData.notes,
|
||||
skills: staffData.skills,
|
||||
averageRating: staffData.averageRating,
|
||||
reliabilityScore: staffData.reliability_score,
|
||||
onTimeRate: staffData.shift_coverage_percentage,
|
||||
totalShifts: staffData.total_shifts,
|
||||
city: staffData.city,
|
||||
addres: staffData.address,
|
||||
});
|
||||
await refetch();
|
||||
setIsEditing(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to update staff", error);
|
||||
@@ -67,7 +94,7 @@ export default function EditStaff() {
|
||||
return (
|
||||
<DashboardLayout
|
||||
title={isEditing ? `Edit: ${staff?.employee_name || 'Staff Member'}` : staff?.employee_name || 'Staff Member'}
|
||||
subtitle={`Management of ${staff?.employee_name}'s professional records`}
|
||||
subtitle={isEditing ? `Modifying ${staff?.employee_name}'s professional records` : `Management of ${staff?.employee_name}'s professional records`}
|
||||
backAction={
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -80,35 +107,72 @@ export default function EditStaff() {
|
||||
}
|
||||
>
|
||||
{staff && (
|
||||
<div>
|
||||
{!isEditing && (
|
||||
<div className="mb-6 flex justify-end">
|
||||
<Button onClick={() => setIsEditing(true)} variant="secondary">
|
||||
Edit
|
||||
</Button>
|
||||
<div className="space-y-6">
|
||||
{/* Profile Header */}
|
||||
<div className="bg-card/60 backdrop-blur-md border border-border p-6 rounded-3xl flex flex-col md:flex-row items-center justify-between gap-6">
|
||||
<div className="flex flex-col md:flex-row items-center gap-6 text-center md:text-left">
|
||||
<div className="w-24 h-24 bg-primary/10 rounded-2xl flex items-center justify-center text-primary font-black text-3xl border-2 border-primary/20 shadow-xl overflow-hidden group">
|
||||
{staff.photo ? (
|
||||
<img src={staff.photo} alt={staff.employee_name} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
staff.employee_name.charAt(0)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-3xl font-black text-foreground mb-1">{staff.employee_name}</h2>
|
||||
<div className="flex flex-wrap items-center justify-center md:justify-start gap-2">
|
||||
<Badge className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20 font-black text-xs uppercase px-3 py-1">
|
||||
{staff.status || 'Active'}
|
||||
</Badge>
|
||||
<span className="text-muted-foreground font-bold text-sm">•</span>
|
||||
<span className="text-muted-foreground font-bold text-sm">{staff.position || 'No Role Assigned'}</span>
|
||||
{staff.profile_type && (
|
||||
<>
|
||||
<span className="text-muted-foreground font-bold text-sm">•</span>
|
||||
<span className="text-primary font-bold text-sm">{staff.profile_type}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isEditing && (
|
||||
<div className="mb-6 flex justify-end gap-2">
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
variant="outline"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
// Trigger form submission by dispatching event or calling form submit
|
||||
const form = document.querySelector('form');
|
||||
if (form) form.requestSubmit();
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
|
||||
<div className="flex gap-3">
|
||||
{!isEditing ? (
|
||||
<Button
|
||||
onClick={() => setIsEditing(true)}
|
||||
variant="secondary"
|
||||
leadingIcon={<Edit className="w-4 h-4" />}
|
||||
className="rounded-xl px-6 font-bold"
|
||||
>
|
||||
Edit Profile
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
variant="outline"
|
||||
disabled={isSubmitting}
|
||||
leadingIcon={<X className="w-4 h-4" />}
|
||||
className="rounded-xl font-bold"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const form = document.querySelector('form');
|
||||
if (form) form.requestSubmit();
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
leadingIcon={isSubmitting ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
|
||||
className="rounded-xl px-6 font-bold"
|
||||
>
|
||||
{isSubmitting ? 'Saving...' : 'Save Changes'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StaffForm
|
||||
staff={staff}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
@@ -1,437 +0,0 @@
|
||||
import type { Staff, User, Event } from "../features/workforce/type";
|
||||
|
||||
/**
|
||||
* Mock Workforce Service
|
||||
* Provides placeholder data for UI development
|
||||
* Replace with actual API calls when backend is ready
|
||||
*/
|
||||
|
||||
const mockUsers: Record<string, User> = {
|
||||
admin: {
|
||||
id: "admin-001",
|
||||
email: "admin@krow.com",
|
||||
role: "admin",
|
||||
user_role: "admin",
|
||||
name: "Admin User",
|
||||
company_name: "Krow Workforce",
|
||||
},
|
||||
vendor: {
|
||||
id: "vendor-001",
|
||||
email: "vendor@staffagency.com",
|
||||
role: "vendor",
|
||||
user_role: "vendor",
|
||||
name: "Vendor User",
|
||||
company_name: "Staff Agency Pro",
|
||||
},
|
||||
client: {
|
||||
id: "client-001",
|
||||
email: "client@eventco.com",
|
||||
role: "client",
|
||||
user_role: "client",
|
||||
name: "Client User",
|
||||
company_name: "Event Co.",
|
||||
},
|
||||
operator: {
|
||||
id: "operator-001",
|
||||
email: "operator@krow.com",
|
||||
role: "operator",
|
||||
user_role: "operator",
|
||||
name: "Operator User",
|
||||
company_name: "Krow Workforce",
|
||||
},
|
||||
};
|
||||
|
||||
const mockStaff = [
|
||||
{
|
||||
id: "staff-001",
|
||||
employee_name: "Sarah Johnson",
|
||||
position: "Event Coordinator",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-001",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-001",
|
||||
profile_type: "Senior",
|
||||
email: "sarah.johnson@example.com",
|
||||
contact_number: "+1 (555) 123-4567",
|
||||
status: "Active",
|
||||
skills: ["Event Management", "Customer Service", "Coordination"],
|
||||
department: "Events",
|
||||
hub_location: "New York",
|
||||
averageRating: 4.8,
|
||||
reliability_score: 95,
|
||||
shift_coverage_percentage: 98,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 1).toISOString(),
|
||||
employment_type: "Contract",
|
||||
manager: "John Smith",
|
||||
cancellation_count: 0,
|
||||
no_show_count: 0,
|
||||
total_shifts: 145,
|
||||
},
|
||||
{
|
||||
id: "staff-002",
|
||||
employee_name: "Michael Chen",
|
||||
position: "Logistics Manager",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-002",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-002",
|
||||
profile_type: "Intermediate",
|
||||
email: "michael.chen@example.com",
|
||||
contact_number: "+1 (555) 234-5678",
|
||||
status: "Active",
|
||||
skills: ["Logistics", "Operations", "Planning"],
|
||||
department: "Operations",
|
||||
hub_location: "Los Angeles",
|
||||
averageRating: 4.6,
|
||||
reliability_score: 88,
|
||||
shift_coverage_percentage: 85,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3).toISOString(),
|
||||
employment_type: "Full-time",
|
||||
manager: "Jane Williams",
|
||||
cancellation_count: 2,
|
||||
no_show_count: 1,
|
||||
total_shifts: 156,
|
||||
},
|
||||
{
|
||||
id: "staff-003",
|
||||
employee_name: "Emma Rodriguez",
|
||||
position: "Customer Service Rep",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-003",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-003",
|
||||
profile_type: "Junior",
|
||||
email: "emma.rodriguez@example.com",
|
||||
contact_number: "+1 (555) 345-6789",
|
||||
status: "Pending",
|
||||
skills: ["Customer Service", "Communication"],
|
||||
department: "Support",
|
||||
hub_location: "Chicago",
|
||||
averageRating: 4.3,
|
||||
reliability_score: 72,
|
||||
shift_coverage_percentage: 65,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 10).toISOString(),
|
||||
employment_type: "Part-time",
|
||||
manager: "Robert Brown",
|
||||
cancellation_count: 5,
|
||||
no_show_count: 3,
|
||||
total_shifts: 89,
|
||||
},
|
||||
{
|
||||
id: "staff-004",
|
||||
employee_name: "James Wilson",
|
||||
position: "Security Officer",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-004",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-004",
|
||||
profile_type: "Senior",
|
||||
email: "james.wilson@example.com",
|
||||
contact_number: "+1 (555) 456-7890",
|
||||
status: "Active",
|
||||
skills: ["Security", "Safety"],
|
||||
department: "Security",
|
||||
hub_location: "Miami",
|
||||
averageRating: 4.9,
|
||||
reliability_score: 99,
|
||||
shift_coverage_percentage: 100,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 2).toISOString(),
|
||||
employment_type: "Full-time",
|
||||
manager: "Patricia Davis",
|
||||
cancellation_count: 0,
|
||||
no_show_count: 0,
|
||||
total_shifts: 198,
|
||||
},
|
||||
{
|
||||
id: "staff-005",
|
||||
employee_name: "Lisa Anderson",
|
||||
position: "HR Specialist",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-005",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-005",
|
||||
profile_type: "Intermediate",
|
||||
email: "lisa.anderson@example.com",
|
||||
contact_number: "+1 (555) 567-8901",
|
||||
status: "Suspended",
|
||||
skills: ["HR", "Recruitment"],
|
||||
department: "Human Resources",
|
||||
hub_location: "New York",
|
||||
averageRating: 4.5,
|
||||
reliability_score: 91,
|
||||
shift_coverage_percentage: 92,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 30).toISOString(),
|
||||
employment_type: "Full-time",
|
||||
manager: "John Smith",
|
||||
cancellation_count: 1,
|
||||
no_show_count: 0,
|
||||
total_shifts: 167,
|
||||
},
|
||||
{
|
||||
id: "staff-006",
|
||||
employee_name: "David Martinez",
|
||||
position: "Data Analyst",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-006",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-006",
|
||||
profile_type: "Senior",
|
||||
email: "david.martinez@example.com",
|
||||
contact_number: "+1 (555) 678-9012",
|
||||
status: "Active",
|
||||
skills: ["Data Analysis", "Reporting", "SQL"],
|
||||
department: "Analytics",
|
||||
hub_location: "San Francisco",
|
||||
averageRating: 4.7,
|
||||
reliability_score: 93,
|
||||
shift_coverage_percentage: 87,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5).toISOString(),
|
||||
employment_type: "Contract",
|
||||
manager: "Michael Thompson",
|
||||
cancellation_count: 1,
|
||||
no_show_count: 1,
|
||||
total_shifts: 134,
|
||||
},
|
||||
{
|
||||
id: "staff-007",
|
||||
employee_name: "Jessica Lee",
|
||||
position: "Project Manager",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-007",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-007",
|
||||
profile_type: "Senior",
|
||||
email: "jessica.lee@example.com",
|
||||
contact_number: "+1 (555) 789-0123",
|
||||
status: "Active",
|
||||
skills: ["Project Management", "Agile"],
|
||||
department: "Projects",
|
||||
hub_location: "Boston",
|
||||
averageRating: 4.4,
|
||||
reliability_score: 85,
|
||||
shift_coverage_percentage: 79,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(),
|
||||
employment_type: "Full-time",
|
||||
manager: "Sarah Johnson",
|
||||
cancellation_count: 3,
|
||||
no_show_count: 1,
|
||||
total_shifts: 142,
|
||||
},
|
||||
{
|
||||
id: "staff-008",
|
||||
employee_name: "Kevin Thompson",
|
||||
position: "Business Analyst",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-008",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-008",
|
||||
profile_type: "Intermediate",
|
||||
email: "kevin.thompson@example.com",
|
||||
contact_number: "+1 (555) 890-1234",
|
||||
status: "Pending",
|
||||
skills: ["Business Analysis", "Strategy"],
|
||||
department: "Strategy",
|
||||
hub_location: "Austin",
|
||||
averageRating: 4.2,
|
||||
reliability_score: 68,
|
||||
shift_coverage_percentage: 72,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 14).toISOString(),
|
||||
employment_type: "Part-time",
|
||||
manager: "Robert Brown",
|
||||
cancellation_count: 6,
|
||||
no_show_count: 2,
|
||||
total_shifts: 95,
|
||||
},
|
||||
{
|
||||
id: "staff-009",
|
||||
employee_name: "Nicole White",
|
||||
position: "Marketing Manager",
|
||||
photo: "https://i.pravatar.cc/150?u=staff-009",
|
||||
photo_url: "https://i.pravatar.cc/150?u=staff-009",
|
||||
profile_type: "Senior",
|
||||
email: "nicole.white@example.com",
|
||||
contact_number: "+1 (555) 901-2345",
|
||||
status: "Active",
|
||||
skills: ["Marketing", "Branding"],
|
||||
department: "Marketing",
|
||||
hub_location: "Seattle",
|
||||
averageRating: 4.6,
|
||||
reliability_score: 89,
|
||||
shift_coverage_percentage: 86,
|
||||
vendor_id: "vendor-001",
|
||||
vendor_name: "Staff Agency Pro",
|
||||
created_by: "vendor@staffagency.com",
|
||||
created_date: new Date().toISOString(),
|
||||
last_active: new Date(Date.now() - 1000 * 60 * 60 * 24 * 4).toISOString(),
|
||||
employment_type: "Full-time",
|
||||
manager: "Patricia Davis",
|
||||
cancellation_count: 2,
|
||||
no_show_count: 0,
|
||||
total_shifts: 178,
|
||||
},
|
||||
] as unknown as Staff[];
|
||||
|
||||
const mockEvents: Event[] = [
|
||||
{
|
||||
id: "event-001",
|
||||
business_name: "Event Co.",
|
||||
client_email: "client@eventco.com",
|
||||
created_by: "client@eventco.com",
|
||||
assigned_staff: [
|
||||
{ staff_id: "staff-001" },
|
||||
{ staff_id: "staff-004" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "event-002",
|
||||
business_name: "Event Co.",
|
||||
client_email: "client@eventco.com",
|
||||
created_by: "client@eventco.com",
|
||||
assigned_staff: [
|
||||
{ staff_id: "staff-002" },
|
||||
{ staff_id: "staff-009" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Simulates API delay for realistic behavior
|
||||
*/
|
||||
const delay = (ms: number = 500) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Workforce Service - Mock implementation
|
||||
*/
|
||||
export const workforceService = {
|
||||
auth: {
|
||||
/**
|
||||
* Get current user (mocked)
|
||||
* In production, this would verify Firebase auth session
|
||||
*/
|
||||
me: async (): Promise<User> => {
|
||||
await delay(800);
|
||||
// Return a random user for demonstration
|
||||
const users = Object.values(mockUsers);
|
||||
return users[Math.floor(Math.random() * users.length)];
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign out user (mocked)
|
||||
*/
|
||||
logout: async (): Promise<void> => {
|
||||
await delay(300);
|
||||
console.log("User logged out (mock)");
|
||||
},
|
||||
},
|
||||
|
||||
entities: {
|
||||
Staff: {
|
||||
/**
|
||||
* List all staff members
|
||||
* @param sortBy - Sort field (e.g., '-created_date' for descending)
|
||||
*/
|
||||
list: async (sortBy?: string): Promise<Staff[]> => {
|
||||
await delay(1200);
|
||||
|
||||
const staffList = [...mockStaff];
|
||||
|
||||
// Simple sorting logic
|
||||
if (sortBy === "-created_date") {
|
||||
staffList.sort(
|
||||
(a, b) =>
|
||||
new Date(b.created_date || 0).getTime() -
|
||||
new Date(a.created_date || 0).getTime()
|
||||
);
|
||||
} else if (sortBy === "created_date") {
|
||||
staffList.sort(
|
||||
(a, b) =>
|
||||
new Date(a.created_date || 0).getTime() -
|
||||
new Date(b.created_date || 0).getTime()
|
||||
);
|
||||
}
|
||||
|
||||
return staffList;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get single staff member by ID
|
||||
*/
|
||||
get: async (id: string): Promise<Staff | null> => {
|
||||
await delay(600);
|
||||
return mockStaff.find((s) => s.id === id) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create new staff member
|
||||
*/
|
||||
create: async (staff: Partial<Staff>): Promise<Staff> => {
|
||||
await delay(1000);
|
||||
const newStaff: Staff = {
|
||||
...staff,
|
||||
id: `staff-${Date.now()}`,
|
||||
created_date: new Date().toISOString(),
|
||||
} as Staff;
|
||||
mockStaff.push(newStaff);
|
||||
return newStaff;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update staff member
|
||||
*/
|
||||
update: async (id: string, updates: Partial<Staff>): Promise<Staff> => {
|
||||
await delay(800);
|
||||
const index = mockStaff.findIndex((s) => s.id === id);
|
||||
if (index === -1) throw new Error("Staff not found");
|
||||
mockStaff[index] = { ...mockStaff[index], ...updates };
|
||||
return mockStaff[index];
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete staff member
|
||||
*/
|
||||
delete: async (id: string): Promise<void> => {
|
||||
await delay(600);
|
||||
const index = mockStaff.findIndex((s) => s.id === id);
|
||||
if (index === -1) throw new Error("Staff not found");
|
||||
mockStaff.splice(index, 1);
|
||||
},
|
||||
},
|
||||
|
||||
Event: {
|
||||
/**
|
||||
* List all events
|
||||
*/
|
||||
list: async (): Promise<Event[]> => {
|
||||
await delay(1000);
|
||||
return [...mockEvents];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get single event by ID
|
||||
*/
|
||||
get: async (id: string): Promise<Event | null> => {
|
||||
await delay(600);
|
||||
return mockEvents.find((e) => e.id === id) || null;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default workforceService;
|
||||
Reference in New Issue
Block a user