diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index 0f24bd7..ec74881 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -9,6 +9,7 @@ import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import Paper, { paperClasses } from "@mui/material/Paper"; import { adminList, deleteAdmin } from "../../redux/slices/adminSlice"; +import { deleteManager } from "../../redux/slices/managerSlice"; import { useDispatch } from "react-redux"; import { Box, @@ -72,6 +73,7 @@ interface CustomTableProps { handleStatusToggle: (id: string, currentStatus: number) => void; tableType: string; // Adding tableType prop to change header text dynamically handleClickOpen: () => void; + handleDeleteButton: (id: string | number | undefined) => void; } const CustomTable: React.FC = ({ @@ -98,10 +100,26 @@ const CustomTable: React.FC = ({ const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent, row: Row) => { + setAnchorEl(event.currentTarget); - setSelectedRow(row); // Ensure the row data is set + setSelectedRow(row); setRowData(row); + + }; + const handleViewButton = (id: string | undefined) => { + if (!id) { + console.error("ID not found for viewing."); + return; + } + setViewModal(true); + }; + + + + + + const handleClose = () => { setAnchorEl(null); @@ -114,38 +132,23 @@ const CustomTable: React.FC = ({ return false; }; - const handleDeleteButton = (id: string | undefined) => { - if (!id) console.error("ID not found", id); - - dispatch(deleteAdmin(id || "")); - setDeleteModal(false); // Close the modal only after deletion - handleClose(); - }; - - const handleViewButton = (id: string | undefined) => { - if (!id) console.error("ID not found", id); - - dispatch(adminList()); - setViewModal(false); - }; - - const handleToggleStatus = () => { - if (selectedRow) { - // Toggle the opposite of current status - const newStatus = selectedRow.statusValue === 1 ? 0 : 1; - handleStatusToggle(selectedRow.id, newStatus); + const handleDeleteButton = (id: string | undefined) => { + if (!id) { + console.error("ID not found for viewing."); + return; } - handleClose(); + setViewModal(true); }; - + + + const filteredRows = rows.filter( - (row) => - (row.name && - row.name.toLowerCase().includes(searchQuery.toLowerCase())) || - false + (row) => + (row.name && + row.name.toLowerCase().includes(searchQuery.toLowerCase())) || + false ); - const indexOfLastRow = currentPage * usersPerPage; const indexOfFirstRow = indexOfLastRow - usersPerPage; const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow); @@ -249,8 +252,8 @@ const filteredRows = rows.filter( ? "Role" : tableType === "user" ? "User" - : tableType === "manager" - ? "Manager" + : tableType === "managers" + ? "Managers" : tableType === "vehicle" ? "Vehicle" : "Item"} @@ -421,6 +424,7 @@ const filteredRows = rows.filter( variant="text" onClick={(e) => { e.stopPropagation(); + // setSelectedRow(row); setViewModal(true); }} color="primary" @@ -434,15 +438,15 @@ const filteredRows = rows.filter( View {viewModal && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} + handleViewButton(selectedRow?.id)} + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + tableType={tableType} + /> +)} + - - - - - ); + useEffect(() => { + if (!id || !tableType) return; + + const dataList: User[] = tableType === "admin" ? adminList : managerList; + const item = dataList.find((user) => user.id === id); + setSelectedItem(item || null); + }, [id, tableType, adminList, managerList]); + + + + + + const handleDelete = async () => { + if (!id || !tableType) { + return; + } + + setLoading(true); + try { + let deleteResult; + if (tableType === "managers") { + deleteResult = await dispatch(deleteManager(id)).unwrap(); + } + + if (deleteResult) { + await dispatch(fetchManagerList()); // Refresh list only if deletion is successful + } + } catch (error) { + console.error("❌ Error while deleting:", error); + } finally { + setLoading(false); + setDeleteModal(false); + } +}; + + + return ( + setDeleteModal(false)} aria-labelledby="modal-title"> + + + Delete {tableType ? tableType.charAt(0).toUpperCase() + tableType.slice(1) : "User"} + + + + + {selectedItem ? ( + + Are you sure you want to delete {selectedItem.name}? + + ) : ( + No {tableType} found with this ID + )} + + + + + + + + ); } diff --git a/src/components/Modals/ViewModal/ViewModal.tsx b/src/components/Modals/ViewModal/ViewModal.tsx deleted file mode 100644 index 8a1df15..0000000 --- a/src/components/Modals/ViewModal/ViewModal.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { Box, Button, Modal, Typography } from "@mui/material"; -import { AppDispatch, RootState } from "../../../redux/store/store"; -import { useDispatch, useSelector } from "react-redux"; -import { useEffect, useState } from "react"; - -type Props = { - open: boolean; - setViewModal: Function; - handleView: (id: string | undefined) => void; - id?: string | undefined; -}; -; - -const style = { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 330, - bgcolor: "background.paper", - borderRadius: 1.5, - boxShadow: 24, - p: 3, -}; - -const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize", alignItems: "center" }; - -export default function ViewModal({ - open, - setViewModal, - id, // Selected user's ID -}: Props) { - const { admins } = useSelector((state: RootState) => state.adminReducer); - const [selectedAdmin, setSelectedAdmin] = useState(null); - - useEffect(() => { - if (id) { - const admin = admins.find((admin) => admin.id === id); - setSelectedAdmin(admin || null); - } - }, [id, admins]); - - return ( - - - - Details of {selectedAdmin?.name} - - {selectedAdmin ? ( - <> - Name: {selectedAdmin?.name} - Email: {selectedAdmin?.email} - Phone: {selectedAdmin?.phone} - Address: {selectedAdmin?.registeredAddress} - - ) : ( - No admin found with this ID - )} - - - - - - ); -} \ No newline at end of file diff --git a/src/components/Modals/ViewModal/index.tsx b/src/components/Modals/ViewModal/index.tsx index 4fafe9b..474065d 100644 --- a/src/components/Modals/ViewModal/index.tsx +++ b/src/components/Modals/ViewModal/index.tsx @@ -1,126 +1,95 @@ -import { Box, Button, Modal, Typography, Divider } from "@mui/material"; -import { RootState } from "../../../redux/store/store"; -import { useSelector } from "react-redux"; -import { useEffect, useState } from "react"; +import { Box, Modal, Typography, Divider } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../redux/store/store"; + +type User = { + id: number; + name: string; + email?: string; + phone: string; + registeredAddress?: string; + action?: any; +}; type Props = { - open: boolean; - setViewModal: Function; - id?: string; + open: boolean; + setViewModal: (value: boolean) => void; + id?: number; + tableType?: "admin" | "manager"; }; const style = { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: 400, - bgcolor: "background.paper", - borderRadius: 2, - boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)", - p: 4, - display: "flex", - flexDirection: "column", - alignItems: "center", - gap: 2, + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 400, + bgcolor: "background.paper", + borderRadius: 2, + boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)", + p: 4, + display: "flex", + flexDirection: "column", + alignItems: "center", + gap: 2, }; -const btnStyle = { - mt: 2, - px: 5, - py: 1.2, - width: "100%", - textTransform: "capitalize", -}; +export default function ViewModal({ open, setViewModal, id, tableType }: Props) { + const adminList = useSelector((state: RootState) => state.adminReducer.admins) || []; + const managerList = useSelector((state: RootState) => state.managerReducer.managers) || []; -export default function ViewModal({ open, setViewModal, id }: Props) { - const { admins } = useSelector((state: RootState) => state.adminReducer); - const [selectedAdmin, setSelectedAdmin] = useState(null); + const [selectedItem, setSelectedItem] = useState(null); - useEffect(() => { - if (id) { - const admin = admins.find((admin) => admin.id === id); - setSelectedAdmin(admin); - } - }, [id, admins]); + useEffect(() => { + if (!id || !tableType) return; - return ( - - - - - - {selectedAdmin?.name}'s Details - - setViewModal(false)} - sx={{ - cursor: "pointer", - display: "flex", - alignItems: "center", - }} - > - - - - + const dataList: User[] = tableType === "admin" ? adminList : managerList; + const item = dataList.find((user) => user.id === id); - + if (item) { + setSelectedItem(item); // ✅ Updating selected item properly + } + }, [id, tableType, adminList, managerList]); - {selectedAdmin ? ( - - - Name: {selectedAdmin.name} - - - Email: {selectedAdmin.email} - - - Phone: {selectedAdmin.phone} - - - Address:{" "} - {selectedAdmin.registeredAddress ?? "N/A"} - - - ) : ( - - No admin found with this ID - - )} + const formattedType = tableType ? tableType.charAt(0).toUpperCase() + tableType.slice(1) : "User"; - {/* */} - - - ); + return ( + setViewModal(false)} aria-labelledby="modal-title"> + + + + + {selectedItem?.name ? `${selectedItem.name}'s Details` : `${formattedType} Details`} + + setViewModal(false)} sx={{ cursor: "pointer", display: "flex" }}> + + + + + + + + {selectedItem ? ( + + + Name: {selectedItem.name || "N/A"} + + + Email: {selectedItem.email || "N/A"} + + + Phone: {selectedItem.phone || "N/A"} + + + Address: {selectedItem.registeredAddress || "N/A"} + + + ) : ( + No {formattedType} found with this ID + )} + + + ); } diff --git a/src/pages/AddManagerModal/index.tsx b/src/pages/AddManagerModal/index.tsx new file mode 100644 index 0000000..64ac133 --- /dev/null +++ b/src/pages/AddManagerModal/index.tsx @@ -0,0 +1,150 @@ +import React, { useState } from "react"; +import { Box, Button, Typography, Modal, IconButton, TextField } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useDispatch } from "react-redux"; +import { toast } from "sonner"; +import { addManager } from "../../redux/slices/managerSlice"; + +interface AddManagerModalProps { + open: boolean; + handleClose: () => void; +} + +const AddManagerModal: React.FC = ({ open, handleClose }) => { + // State for input fields + const [name, setName] = useState(""); + const [stationLocation, setStationLocation] = useState(""); + const [phone, setPhone] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const roleId = 5; // Fixed role ID + const roleName = "Peon"; // Required role name + + const dispatch = useDispatch(); + + // Function to validate form inputs + const validateInputs = () => { + if (!name || !stationLocation || !phone || !email || !password) { + toast.error("All fields are required."); + return false; + } + + const phoneRegex = /^[0-9]{6,14}$/; + if (!phoneRegex.test(phone)) { + toast.error("Phone number must be between 6 to 14 digits."); + return false; + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + toast.error("Enter a valid email address."); + return false; + } + + if (password.length < 6) { + toast.error("Password must be at least 6 characters long."); + return false; + } + + return true; + }; + + // Handle form submission + const handleSubmit = async () => { + if (!validateInputs()) return; + + const managerData = { + name, + registeredAddress: stationLocation, + phone, + email, + password, + roleId, + roleName, // ✅ Ensure roleName is correctly included + }; + + try { + const response = await dispatch(addManager(managerData)).unwrap(); + + + + // ✅ Ensure response contains expected data + if (!response || !response.id) { + throw new Error("Invalid response from server. ID is missing."); + } + + // ✅ Show success message from API if available, fallback if not + toast.success(response.message || "Manager added successfully!"); + + resetForm(); + handleClose(); + } catch (error: any) { + console.error("API Error:", error); // ✅ Log error for debugging + + // ✅ Handle both API errors and unexpected errors + toast.error( + error?.response?.data?.message || error.message || "Failed to add manager" + ); + } + }; + + + + // Function to reset form fields + const resetForm = () => { + setName(""); + setStationLocation(""); + setPhone(""); + setEmail(""); + setPassword(""); + }; + + return ( + + + {/* Modal Header */} + + + Add Manager + + + + + + + + + {/* Input Fields */} + + setName(e.target.value)} /> + setStationLocation(e.target.value)} /> + setPhone(e.target.value)} /> + setEmail(e.target.value)} /> + setPassword(e.target.value)} /> + + + {/* Submit Button */} + + + + + + ); +}; + +export default AddManagerModal; diff --git a/src/pages/EditUserModal/index.tsx b/src/pages/EditUserModal/index.tsx new file mode 100644 index 0000000..0747aad --- /dev/null +++ b/src/pages/EditUserModal/index.tsx @@ -0,0 +1,191 @@ +import React, { useEffect } from "react"; +import { + Box, + Button, + Typography, + TextField, + Modal, + IconButton, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useForm, Controller } from "react-hook-form"; + +interface EditModalProps { + open: boolean; + handleClose: () => void; + handleCreate: (data: FormData) => void; + handleUpdate: (id: string, data: FormData) => void; + editRow: any | null; +} + +interface FormData { + managerName: string; + stationName: string; + stationLocation: string; + phoneNumber: string; +} + +const EditModal: React.FC = ({ + open, + handleClose, + handleCreate, + handleUpdate, + editRow, +}) => { + const { + control, + handleSubmit, + formState: { errors }, + setValue, + reset, + } = useForm({ + defaultValues: { + managerName: "", + stationName: "", + stationLocation: "", + phoneNumber: "", + }, + }); + + // Populate form fields when `editRow` changes + useEffect(() => { + if (editRow) { + setValue("managerName", editRow.name || ""); + setValue("stationName", editRow.stationName || ""); + setValue("stationLocation", editRow.registeredAddress || ""); + setValue("phoneNumber", editRow.phone || ""); + } else { + reset({ // ✅ Ensure default values are reset when adding a new manager + managerName: "", + stationName: "", + stationLocation: "", + phoneNumber: "", + }); + } + }, [editRow, setValue, reset]); + + + const onSubmit = (data: FormData) => { + if (editRow) { + handleUpdate({ + id: editRow.id, + managerName: data.managerName, + stationLocation: data.stationLocation, + phoneNumber: data.phoneNumber + }); + } else { + handleCreate(data); + } + handleClose(); + reset(); + }; + + + + return ( + + + {/* Header */} + + + {editRow ? "Edit Manager" : "Add Manager"} + + + + + + + {/* Horizontal Line */} + + + {/* Input Fields */} + + + {/* Manager Name */} + + Manager Name + ( + + )} + /> + + + {/* Station Name */} + + Station Name + ( + + )} + /> + + + + + {/* Station Location */} + + Station Location + ( + + )} + /> + + + {/* Phone Number */} + + Phone Number + ( + + )} + /> + + + + + {/* Submit Button */} + + + + + + ); +}; + +export default EditModal; diff --git a/src/pages/ManagerList/index.tsx b/src/pages/ManagerList/index.tsx new file mode 100644 index 0000000..3514684 --- /dev/null +++ b/src/pages/ManagerList/index.tsx @@ -0,0 +1,240 @@ +import React, { useEffect, useState } from "react"; +import { Box, Button, Typography, TextField, InputAdornment, IconButton } from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import EqualizerIcon from "@mui/icons-material/Tune"; +import CustomTable, { Column } from "../../components/CustomTable"; +import AddManagerModal from "../../pages/AddManagerModal"; +import EditUserModal from "../EditUserModal"; +import { useDispatch, useSelector } from "react-redux"; +import { fetchManagerList, addManager,updateManager,deleteManager } from "../../redux/slices/managerSlice"; +import { RootState, AppDispatch } from "../../redux/store/store"; + + +export default function ManagerList() { + const dispatch = useDispatch(); + const { managers, isLoading } = useSelector((state: RootState) => state.managerReducer); + + const [search, setSearch] = useState(""); + const [addButtonModal, setAddButtonModal] = useState(false); + const [modalOpen, setModalOpen] = useState(false); + const [rowData, setRowData] = useState(null); + const [viewModal, setViewModal] = useState(false); + const [deleteModal, setDeleteModal] = useState(false); + + useEffect(() => { + dispatch(fetchManagerList()); // Fetch data when component mounts + }, [dispatch]); + + + + // Function to handle adding a manager + const handleAddManager = async (newManager: { + name: string; + registeredAddress: string; + phone: string; + email: string; + password: string; + }) => { + await dispatch(addManager(newManager)); + dispatch(fetchManagerList()); // Refresh list after adding + handleCloseModal(); // Close modal after adding + }; + + + + // Function to handle updating a + const handleUpdateManager = async (updatedManager) => { + if (!updatedManager || typeof updatedManager !== "object") { + return; + } + + if (!rowData) { + return; + } + + const managerId = rowData.id; + + if (!updatedManager.managerName || !updatedManager.stationLocation || !updatedManager.phoneNumber) { + return; + } + + try { + const response = await dispatch(updateManager({ + id: managerId, + name: updatedManager.managerName, + registeredAddress: updatedManager.stationLocation, + phone: updatedManager.phoneNumber, + })); + + if (response?.payload?.statusCode === 200) { + await dispatch(fetchManagerList()); // ✅ Refresh list after update + } + + handleCloseModal(); + } catch (error) { + console.error("❌ Update failed:", error); + } + }; + + + // Function to handle deleting a manager + + + const handleDeleteManager = async () => { + if (!rowData?.id) { + console.error("❌ No manager ID found for deletion!"); + return; + } + + try { + + const response = await dispatch(deleteManager(rowData.id)); + + if (response?.payload) { + + dispatch(fetchManagerList()); // Refresh list after deletion + } else { + console.error("❌ Deletion failed!", response); + } + } catch (error) { + console.error("❌ Error deleting manager:", error); + } + + setDeleteModal(false); // Close delete modal + + handleCloseModal(); + }; + + + + + + + + // Function to handle search input change + const handleSearchChange = (event: React.ChangeEvent) => { + setSearch(event.target.value); + }; + + // Open the Add Manager Modal + const handleClickOpen = () => { + setRowData(null); + setAddButtonModal(true); + }; + + // Close all modals + const handleCloseModal = () => { + setAddButtonModal(false); + setModalOpen(false); + setRowData(null); + }; + + // Table columns definition + const managerColumns: Column[] = [ + { id: "name", label: "Manager Name" }, + { id: "registeredAddress", label: "Station Location" }, + { id: "phone", label: "Phone Number" }, + { id: "action", label: "Action", align: "center" }, + ]; + + // Filtered manager list based on search input + const filteredManagers = managers.filter((manager) => + Object.values(manager).some((value) => + typeof value === "string" && value.toLowerCase().includes(search.toLowerCase()) + ) + ); + + return ( + <> + {/* Header Section */} + + + Managers + + + + + + ), + }} + /> + + + + + + + + + + {/* Table Section */} + {isLoading ? ( + Loading managers... + ) : ( + ({ + id: manager.id, + name: manager.name, + registeredAddress: manager.registeredAddress, + phone: manager.phone, + action: ( + + + + + + + ), + + }))} + tableType="managers" + setRowData={setRowData} + setModalOpen={setModalOpen} + setViewModal={setViewModal} + viewModal={viewModal} + setDeleteModal={setDeleteModal} + deleteModal={deleteModal} + handleDeleteButton={handleDeleteManager} + /> + )} + + {/* Modals */} + + + + + + + handleAddManager({ + name: data.managerName, + registeredAddress: data.stationLocation, + phone: data.phoneNumber, + email: data.email, + password: data.password, + }) + } + /> + + ); +} diff --git a/src/redux/reducers.ts b/src/redux/reducers.ts index 3092653..4b847ca 100644 --- a/src/redux/reducers.ts +++ b/src/redux/reducers.ts @@ -6,6 +6,7 @@ import profileReducer from "./slices/profileSlice"; import userReducer from "./slices/userSlice.ts"; import roleReducer from "./slices/roleSlice.ts"; import vehicleReducer from "./slices/VehicleSlice.ts"; +import managerReducer from "../redux/slices/managerSlice.ts"; const rootReducer = combineReducers({ @@ -14,7 +15,9 @@ const rootReducer = combineReducers({ profileReducer, userReducer, roleReducer, - vehicleReducer + vehicleReducer, + managerReducer, + // Add other reducers here... }); export type RootState = ReturnType; diff --git a/src/redux/slices/managerSlice.ts b/src/redux/slices/managerSlice.ts new file mode 100644 index 0000000..4d1f415 --- /dev/null +++ b/src/redux/slices/managerSlice.ts @@ -0,0 +1,189 @@ +import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; +import http from "../../lib/https"; +import { toast } from "sonner"; + +// Define TypeScript types +interface Manager { + id: string; + name: string; + email: string; + phone?: string; + role: string; +} + +interface ManagerState { + managers: Manager[]; + loading: boolean; + error: string | null; +} + +// Initial state +const initialState: ManagerState = { + managers: [], + loading: false, + error: null, +}; + +// Fetch Manager List +export const fetchManagerList = createAsyncThunk( + "fetchManagers", + async (_, { rejectWithValue }) => { + try { + const response = await http.get("manager-list"); + + if (!response.data?.data) throw new Error("Invalid API response"); + + return response.data.data; + } catch (error: any) { + toast.error("Error Fetching Managers: " + error); + return rejectWithValue( + error?.response?.data?.message || "An error occurred" + ); + } + } +); + +// Create Manager +export const addManager = createAsyncThunk( + "addManager", + async (data, { rejectWithValue }) => { + try { + + const response = await http.post("create-manager", data); + toast.success("Manager created successfully"); + + // ✅ Ensure the response contains the expected data + if (!response.data || !response.data.data || !response.data.data.id) { + console.error("❌ ERROR: Missing manager ID in response", response.data); + throw new Error("Invalid API response: Missing manager ID"); + } + + return response.data.data; + } catch (error: any) { + console.error("❌ API Error:", error?.response?.data || error); + return rejectWithValue(error?.response?.data?.message || "An error occurred"); + } + } +); + +export const updateManager = createAsyncThunk< + Manager, + { id: string; name?: string; email?: string; phone?: string; role?: string; registeredAddress?: string }, + { rejectValue: string } +>( + "updateManager", + async ({ id, ...managerData }, { rejectWithValue }) => { + try { + + + const response = await http.put(`${id}/update-manager`, managerData); + + + toast.success("Manager updated successfully!"); + return response.data.data; // ✅ Extracting correct response data + } catch (error: any) { + + toast.error("Error updating manager: " + error); + return rejectWithValue( + error.response?.data?.message || "An error occurred" + ); + } + } +); + +// Delete Manager +export const deleteManager = createAsyncThunk< + string, + string, + { rejectValue: string } +>( + "deleteManager", + async (id, { rejectWithValue }) => { + try { + await http.delete(`/${id}/delete-manager`); + toast.success("Manager deleted successfully!"); + return id; // Return the ID of the deleted manager + } catch (error: any) { + toast.error("Error deleting manager: " + error); + return rejectWithValue( + error.response?.data?.message || "An error occurred" + ); + } + } +); + + + + + +// Create Slice +const managerSlice = createSlice({ + name: "fetchManagers", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + // Fetch Managers + .addCase(fetchManagerList.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase( + fetchManagerList.fulfilled, + (state, action: PayloadAction) => { + state.loading = false; + state.managers = action.payload; + } + ) + .addCase(fetchManagerList.rejected, (state, action) => { + state.loading = false; + state.error = action.payload || "Failed to fetch managers"; + }) + + // Add Manager + .addCase(addManager.pending, (state) => { + state.loading = true; + }) + .addCase(addManager.fulfilled, (state, action: PayloadAction) => { + state.loading = false; + state.managers.push(action.payload); + }) + .addCase(addManager.rejected, (state, action) => { + state.loading = false; + }) + + // Update Manager + .addCase(updateManager.pending, (state) => { + state.loading = true; + }) + .addCase(updateManager.fulfilled, (state, action) => { + state.loading = false; + const updatedManager = action.payload; + const index = state.managers.findIndex(m => m.id === updatedManager.id); + if (index !== -1) { + state.managers[index] = { ...state.managers[index], ...updatedManager }; // 🔥 Merge updated fields + } + }) + + .addCase(updateManager.rejected, (state) => { + state.loading = false; + }) + + // Delete Manager + // Delete Manager +.addCase(deleteManager.pending, (state) => { + state.loading = true; +}) +.addCase(deleteManager.fulfilled, (state, action) => { + state.loading = false; + state.managers = state.managers.filter(manager => manager.id !== action.payload); +}) +.addCase(deleteManager.rejected, (state) => { + state.loading = false; +}); + + + }, +}); + +export default managerSlice.reducer; diff --git a/src/router.tsx b/src/router.tsx index 9af14d0..b1b40a5 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -16,6 +16,8 @@ const ProfilePage = lazy(() => import("./pages/ProfilePage")); const NotFoundPage = lazy(() => import("./pages/NotFound")); const UserList = lazy(() => import("./pages/UserList")); const PermissionsTable = lazy(() => import("./pages/PermissionTable")); +const ManagerList = lazy(() => import("./pages/ManagerList")); + interface ProtectedRouteProps { caps: string[]; @@ -87,6 +89,18 @@ export default function AppRouter() { /> } /> + + } + /> + } + /> + +