-
Notifications
You must be signed in to change notification settings - Fork 11
Update Staff Dashboard columns and add location filter #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: kc-pit-2026-test
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||
| import { useEffect, useState } from 'react'; | ||||||||||||||||||
|
|
||||||||||||||||||
| import { useAuthContext } from '@/contexts'; | ||||||||||||||||||
| import { useApi } from '@/hooks'; | ||||||||||||||||||
|
|
@@ -15,18 +15,20 @@ import { | |||||||||||||||||
|
|
||||||||||||||||||
| interface StaffMember { | ||||||||||||||||||
| id: string; | ||||||||||||||||||
| employeeId: string; | ||||||||||||||||||
| name: string; | ||||||||||||||||||
| position: string; | ||||||||||||||||||
| locationObjectId: string; | ||||||||||||||||||
| phone: string; | ||||||||||||||||||
| approvalStatus: string; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export default function StaffDashboard() { | ||||||||||||||||||
| const navigate = useNavigate(); | ||||||||||||||||||
| const { userService } = useApi(); | ||||||||||||||||||
| const { userService, locationService } = useApi(); | ||||||||||||||||||
| const { userObjectId } = useAuthContext(); | ||||||||||||||||||
| const [searchTerm, setSearchTerm] = useState(''); | ||||||||||||||||||
| const [filterRole, setFilterRole] = useState(''); | ||||||||||||||||||
| const [filterLocation, setFilterLocation] = useState(''); | ||||||||||||||||||
| const [currentPage, setCurrentPage] = useState(0); // MUI uses 0-based index | ||||||||||||||||||
| const [itemsPerPage, setItemsPerPage] = useState(10); | ||||||||||||||||||
| const [sortConfig, setSortConfig] = useState<{ | ||||||||||||||||||
|
|
@@ -38,6 +40,12 @@ export default function StaffDashboard() { | |||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| const { data: users, mutate } = userService.useUsers() || {}; | ||||||||||||||||||
| const { data: locations } = locationService.useLocations() || {}; | ||||||||||||||||||
|
Comment on lines
40
to
+43
|
||||||||||||||||||
|
|
||||||||||||||||||
| // Reset to first page when filters change | ||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||
| setCurrentPage(0); | ||||||||||||||||||
| }, [searchTerm, filterRole, filterLocation]); | ||||||||||||||||||
|
|
||||||||||||||||||
| // Handlers | ||||||||||||||||||
| const handleApproval = async (id: string, status: string) => { | ||||||||||||||||||
|
|
@@ -75,9 +83,19 @@ export default function StaffDashboard() { | |||||||||||||||||
| } | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Create location lookup map | ||||||||||||||||||
| const locationMap = new Map( | ||||||||||||||||||
| locations?.map(loc => [loc._id, loc.hubName]) ?? [] | ||||||||||||||||||
|
Comment on lines
+86
to
+88
|
||||||||||||||||||
| // Create location lookup map | |
| const locationMap = new Map( | |
| locations?.map(loc => [loc._id, loc.hubName]) ?? [] | |
| // Create location lookup map, excluding empty/whitespace-only hub names | |
| const locationMap = new Map( | |
| (locations ?? []) | |
| .filter(loc => loc.hubName && loc.hubName.trim() !== '') | |
| .map(loc => [loc._id, loc.hubName]) |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential issue: The locationMap and uniqueLocations are computed on every render, even when the locations data hasn't changed. While the performance impact is likely minimal for typical datasets, consider wrapping these computations in useMemo hooks to avoid unnecessary recalculations:
const locationMap = useMemo(() => new Map(locations?.map(loc => [loc._id, loc.hubName]) ?? []), [locations]);
const uniqueLocations = useMemo(() => Array.from(new Set(locations?.map(loc => loc.hubName).filter(Boolean))).sort(), [locations]);
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,9 +2,10 @@ import { UserDocument } from '@/types/User'; | |||||
|
|
||||||
| interface StaffMember { | ||||||
| id: string; | ||||||
| employeeId: string; | ||||||
| name: string; | ||||||
| position: string; | ||||||
| locationObjectId: string; | ||||||
|
||||||
| phone: string; | ||||||
| approvalStatus: string; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -27,17 +28,19 @@ export const getStatusColor = ( | |||||
| }; | ||||||
|
|
||||||
| /** | ||||||
| * Filter staff members by search term and role | ||||||
| * Filter staff members by search term, role, and location | ||||||
| */ | ||||||
| export const filterStaff = ( | ||||||
| staffMembers: StaffMember[], | ||||||
| searchTerm: string, | ||||||
| filterRole: string | ||||||
| filterRole: string, | ||||||
| filterLocation: string | ||||||
| ): StaffMember[] => { | ||||||
| return staffMembers.filter( | ||||||
| (staff: StaffMember) => | ||||||
| staff.name.toLowerCase().includes(searchTerm.toLowerCase()) && | ||||||
| (filterRole ? staff.position === filterRole : true) | ||||||
| (filterRole ? staff.position === filterRole : true) && | ||||||
| (filterLocation ? staff.locationObjectId === filterLocation : true) | ||||||
| ); | ||||||
| }; | ||||||
|
|
||||||
|
|
@@ -84,15 +87,17 @@ export const paginateStaff = ( | |||||
| * Transform users data to staff members format | ||||||
| */ | ||||||
| export const transformUsersToStaff = ( | ||||||
| users: UserDocument[] | undefined | ||||||
| users: UserDocument[] | undefined, | ||||||
| locationMap: Map<string, string> | ||||||
| ): StaffMember[] => { | ||||||
| if (!users) return []; | ||||||
|
|
||||||
| return users.map((user: UserDocument) => ({ | ||||||
| id: user._id, | ||||||
| employeeId: user._id ?? 'N/A', | ||||||
| name: `${user.firstName} ${user.lastName}`, | ||||||
| position: user.role, | ||||||
| locationObjectId: locationMap.get(user.locationObjectId?.toString() ?? '') ?? 'N/A', | ||||||
|
||||||
| phone: user.phone ?? 'N/A', | ||||||
|
||||||
| phone: user.phone ?? 'N/A', | |
| phone: user.phone || 'N/A', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code duplication: The StaffMember interface is duplicated across four files (StaffDashboardUtils.tsx, StaffDashboardTable.tsx, StaffDashboardRow.tsx, and StaffDashboard.tsx). This violates the DRY principle and makes maintenance difficult - any future changes to the interface structure would need to be made in four places.
Recommendation: Create a shared type definition file (e.g., client/src/pages/StaffDashboard/types.ts) and export the StaffMember interface from there, then import it in all files that need it. This is a common pattern for shared types within a feature module.