feat: Implement Staff View

This commit is contained in:
dhinesh-m24
2026-02-02 16:58:48 +05:30
parent fa208f2838
commit 1ac38311ee
3 changed files with 142 additions and 496 deletions

View File

@@ -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);

View File

@@ -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}