diff --git a/src/components/AddEditCategoryModal/index.tsx b/src/components/AddEditCategoryModal/index.tsx index 521bbba..9802f69 100644 --- a/src/components/AddEditCategoryModal/index.tsx +++ b/src/components/AddEditCategoryModal/index.tsx @@ -1,17 +1,20 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { Box, Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - IconButton, - TextField, + Typography, + Modal, + InputAdornment, + } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; +import Visibility from "@mui/icons-material/Visibility"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; import { useForm, Controller } from "react-hook-form"; -import { Visibility, VisibilityOff } from "@mui/icons-material"; +import { + CustomIconButton, + CustomTextField, +} from "../AddUserModel/styled.css.tsx"; //By Jaanvi : Edit Model :: 11-feb-25 interface AddEditCategoryModalProps { @@ -44,6 +47,7 @@ const AddEditCategoryModal: React.FC = ({ handleUpdate, editRow, }) => { + const [showPassword, setShowPassword] = React.useState(false); const { control, handleSubmit, @@ -59,7 +63,7 @@ const AddEditCategoryModal: React.FC = ({ password: "", }, }); - + const onSubmit = (data: FormData) => { if (editRow) { handleUpdate( @@ -77,6 +81,10 @@ const AddEditCategoryModal: React.FC = ({ reset(); }; + const togglePasswordVisibility = () => { + setShowPassword((prev) => !prev); + }; + useEffect(() => { if (editRow) { setValue("name", editRow.name); @@ -88,210 +96,327 @@ const AddEditCategoryModal: React.FC = ({ } }, [editRow, setValue, reset]); - const [showPassword, setShowPassword] = React.useState(false); + + return ( - - - {editRow ? "Edit Admin" : "Add Admin"} + {/* Header */} - + + {editRow ? "Edit Admin" : "Add Admin"} + + + + - - - ( - - )} - /> + {/* Horizontal Line */} + - ( - - )} - /> - {!editRow && ( - + {/* Input Fields */} + ( - <> - - - + {/* First Row - Two Inputs */} + + + + Admin Name + + - setShowPassword((prev) => !prev) - } - > - {showPassword ? ( - - ) : ( - + render={({ field }) => ( + )} - + /> + + + + + Email + + ( + + )} + /> + + + + {/* Second Row - Two Inputs */} + + {!editRow && ( + + + Password + + ( + + + {showPassword ? ( + + ) : ( + + )} + + + ), + }} + error={!!errors.password} + helperText={ + errors.password?.message + } + /> + )} + /> - - )} - /> - )} - ( - - )} - /> + )} + + + Phone Number + + ( + + )} + /> + + - ( - - )} - /> - + {/* Third Row - One Input */} + + + Address + + ( + + )} + /> + + - - - - - + {/* Submit Button */} + + + + + + ); }; diff --git a/src/components/AddManagerModal/index.tsx b/src/components/AddManagerModal/index.tsx new file mode 100644 index 0000000..8094776 --- /dev/null +++ b/src/components/AddManagerModal/index.tsx @@ -0,0 +1,321 @@ + +import { Controller, useForm } from "react-hook-form"; +import { Box, Button, Typography, Modal, IconButton, InputAdornment } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { Visibility, VisibilityOff } from "@mui/icons-material"; +import { useDispatch } from "react-redux"; +import { addManager } from "../../redux/slices/managerSlice"; +import { + CustomIconButton, + CustomTextField, +} from "../AddUserModel/styled.css.tsx"; +import React from "react"; + +type Props = { + open: boolean; + handleClose: () => void; +}; + +export default function AddManagerModal({ open, handleClose }: Props) { + const dispatch = useDispatch(); + const { + control, + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm(); + const [showPassword, setShowPassword] = React.useState(false); + + const onSubmit = async (data: any) => { + const managerData = { + name: data.name, + email: data.email, + phone: data.phone, + registeredAddress: data.registeredAddress, + roleId: data.role, + password: data.password, + roleName: "Manager", // Add a role name (you can replace this with a dynamic value if needed) + }; + + try { + // Dispatch the addManager action + await dispatch(addManager(managerData)).unwrap(); + handleClose(); // Close modal after successful addition + reset(); // Reset form fields + } catch (error) { + console.error("Error adding manager:", error); + } + }; + + // Toggle password visibility + const togglePasswordVisibility = () => { + setShowPassword((prev) => !prev); + }; + + return ( + + + {/* Header */} + + + Add Manager + + + + + + + {/* Horizontal Line */} + + + {/* Form */} +
+ {/* First Row - Manager Name */} + + {/* Manager Name */} + + + Manager Name + + + + + + {/* Second Row - Email, Password */} + + {/* Email */} + + + Email + + + + + {/* Password */} + + + + Password + + ( + + + {showPassword ? ( + + ) : ( + + )} + + + ), + }} + error={!!errors.password} + helperText={ + errors.password?.message + } + /> + )} + /> + + + + + {/* Third Row - Phone Number, Registered Address */} + + {/* Phone Number */} + + + Phone Number + + + + + {/* Registered Address */} + + + Registered Address + + + + + + {/* Submit Button */} + + + +
+
+
+ ); +} diff --git a/src/components/AddUserModel/index.tsx b/src/components/AddUserModel/index.tsx index d92f9bf..c07240c 100644 --- a/src/components/AddUserModel/index.tsx +++ b/src/components/AddUserModel/index.tsx @@ -1,17 +1,27 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { Box, Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, + Typography, TextField, + Modal, + IconButton, + InputAdornment, + styled, } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; +import Visibility from "@mui/icons-material/Visibility"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; import { useForm, Controller } from "react-hook-form"; +import { CustomIconButton, CustomTextField } from "./styled.css.tsx"; -//By Jaanvi : Edit Model :: 11-feb-25 + +interface FormData { + name: string; + email: string; + password: string; + phone: string; +} interface AddUserModalProps { open: boolean; handleClose: () => void; @@ -20,52 +30,55 @@ interface AddUserModalProps { id: string, name: string, email: string, + password: string, phone: string, - password: string + ) => void; editRow: any; } - -interface FormData { - name: string; - email: string; - phone: string; - password: string; -} const AddUserModal: React.FC = ({ open, handleClose, handleCreate, - + handleUpdate, editRow, }) => { + const [showPassword, setShowPassword] = useState(false); + const { control, handleSubmit, formState: { errors }, - setValue, reset, } = useForm({ defaultValues: { name: "", email: "", + password: "", phone: "", - password: "", }, }); + // useEffect(() => { + // if (editRow) { + // setValue("name", editRow.name); + // setValue("email", editRow.email); + // setValue("password", editRow.password); + // setValue("phone", editRow.phone); + // } else { + // reset(); + // } + // }, [editRow, setValue, reset]); const onSubmit = (data: FormData) => { if (editRow) { - handleUpdate( editRow.id, data.name, data.email, - data.phone, - data.password + data.password, + data.phone ); } else { - handleCreate(data); } @@ -73,156 +86,287 @@ const AddUserModal: React.FC = ({ reset(); }; + const togglePasswordVisibility = () => { + setShowPassword((prev) => !prev); + }; + return ( - - - {editRow ? "Edit User" : "Add User"} + {/* Header */} - + + {editRow ? "Edit User" : "Add User"} + + + + - - - ( - - )} - /> + {/* Horizontal Line */} + - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - + {/* Form */} +
+ {/* Input Fields */} + + {/* First Row - Two Inputs */} + + + + User Name + + ( + + )} + /> + - - - - -
+ + + Email + + ( + + )} + /> + + + + {/* Second Row - Two Inputs */} + + + + Password + + ( + + + {showPassword ? ( + + ) : ( + + )} + + + ), + }} + error={!!errors.password} + helperText={ + errors.password?.message + } + /> + )} + /> + + + + Phone Number + + ( + + )} + /> + + + + + {/* Submit Button */} + + + + + + ); }; export default AddUserModal; -function handleUpdate(id: any, name: string, email: string, phone: string, password: string) { - throw new Error("Function not implemented."); -} diff --git a/src/components/AddUserModel/styled.css.tsx b/src/components/AddUserModel/styled.css.tsx new file mode 100644 index 0000000..7bd3874 --- /dev/null +++ b/src/components/AddUserModel/styled.css.tsx @@ -0,0 +1,26 @@ +import { styled } from "@mui/material/styles"; +import { IconButton, TextField } from "@mui/material"; +export const CustomIconButton = styled(IconButton)({ + backgroundColor: "transparent", + "&:hover": { + backgroundColor: "#272727", + }, + "*:where([data-mui-color-scheme='dark']) &": { + backgroundColor: "transparent", + border: "none", + }, +}); + +// Custom TextField with different placeholder color +export const CustomTextField = styled(TextField)({ + "& .MuiInputBase-input::placeholder": { + color: "#D9D8D8", + opacity: 1, + }, + "& .MuiInputBase-root.Mui-focused .MuiInputBase-input::placeholder": { + color: "darkgray", + }, + +}); + + diff --git a/src/components/AddVehicleModal/index.tsx b/src/components/AddVehicleModal/index.tsx new file mode 100644 index 0000000..e7d86c9 --- /dev/null +++ b/src/components/AddVehicleModal/index.tsx @@ -0,0 +1,252 @@ +import { useForm } from "react-hook-form"; +import { + Box, + Button, + Typography, + Modal, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { CustomIconButton, CustomTextField } from "../AddUserModel/styled.css.tsx"; +export default function AddVehicleModal({ + open, + handleClose, + handleAddVehicle, +}) { + const { + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm(); + + const onSubmit = (data: any) => { + handleAddVehicle(data); // Add vehicle to table + handleClose(); // Close modal after adding + reset(); + }; + + return ( + + + {/* Header */} + + + Add Vehicle + + + + + + + {/* Horizontal Line */} + + + {/* Form */} +
+ {/* Input Fields */} + + {/* First Row - Two Inputs */} + + + + Vehicle Name + + + + + + + Company + + + + + + {/* Second Row - Two Inputs */} + + + + Model Name + + + + + + + Charge Type + + + + + + {/* Third Row - Image URL */} + + + Image URL + + + + + + {/* Submit Button */} + + + +
+
+
+ ); +} diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index ec74881..9e24d2a 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -9,14 +9,16 @@ 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 { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice"; + +import { deleteManager, managerList } from "../../redux/slices/managerSlice"; import { useDispatch } from "react-redux"; import { Box, Button, - IconButton, InputAdornment, Menu, +IconButton, Pagination, TextField, Typography, @@ -25,9 +27,14 @@ import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded"; import DeleteModal from "../Modals/DeleteModal"; import { AppDispatch } from "../../redux/store/store"; import ViewModal from "../Modals/ViewModal"; +import VehicleViewModal from "../Modals/VehicleViewModal"; + import SearchIcon from "@mui/icons-material/Search"; import TuneIcon from "@mui/icons-material/Tune"; - +import { + CustomIconButton, +} from "../AddUserModel/styled.css.tsx"; +import ManagerViewModal from "../Modals/ViewManagerModal"; // Styled components for customization const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.head}`]: { @@ -73,7 +80,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; + //handleDeleteButton: (id: string | number | undefined) => void; } const CustomTable: React.FC = ({ @@ -97,6 +104,8 @@ const CustomTable: React.FC = ({ const [currentPage, setCurrentPage] = React.useState(1); const usersPerPage = 10; + + const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent, row: Row) => { @@ -107,18 +116,13 @@ const CustomTable: React.FC = ({ }; - const handleViewButton = (id: string | undefined) => { - if (!id) { - console.error("ID not found for viewing."); - return; - } - setViewModal(true); - }; - - - - - + // const handleViewButton = (id: string | undefined) => { + // if (!id) { + // console.error("ID not found for viewing."); + // return; + // } + // setViewModal(true); + // }; const handleClose = () => { @@ -132,16 +136,58 @@ const CustomTable: React.FC = ({ return false; }; - const handleDeleteButton = (id: string | undefined) => { - if (!id) { - console.error("ID not found for viewing."); - return; + + + const handleDeleteButton = (id: string | undefined) => { + if (!id) console.error("ID not found", id); + + switch (tableType) { + case "admin": + dispatch(deleteAdmin(id || "")); + break; + case "vehicle": + dispatch(deleteVehicle(id || "")); + break; + case "manager": + dispatch(deleteManager(id || "")); + break; + default: + console.error("Unknown table type:", tableType); + return; } - setViewModal(true); + setDeleteModal(false); // Close the modal only after deletion + handleClose(); }; - - - + + const handleViewButton = (id: string | undefined) => { + if (!id) console.error("ID not found", id); + switch (tableType) { + case "admin": + dispatch(adminList()); + break; + case "vehicle": + dispatch(vehicleList()); + break; + case "manager": + dispatch(managerList()); + break; + default: + console.error("Unknown table type:", tableType); + return; + } + 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); + } + handleClose(); + }; + const filteredRows = rows.filter( (row) => (row.name && @@ -217,11 +263,15 @@ const filteredRows = rows.filter( borderColor: "#52ACDF", }, }, + "& .MuiInputBase-input::placeholder": { + color: "#D9D8D8", + opacity: 1, + }, }} InputProps={{ startAdornment: ( - + ), }} @@ -252,8 +302,8 @@ const filteredRows = rows.filter( ? "Role" : tableType === "user" ? "User" - : tableType === "managers" - ? "Managers" + : tableType === "manager" + ? "Manager" : tableType === "vehicle" ? "Vehicle" : "Item"} @@ -330,7 +380,7 @@ const filteredRows = rows.filter( ) : column.id !== "action" ? ( row[column.id] ) : ( - { handleClick(e, row); setRowData(row); // Store the selected row @@ -340,20 +390,13 @@ const filteredRows = rows.filter( minWidth: 0, width: "auto", height: "auto", - backgroundColor: - "transparent", color: "#FFFFFF", - border: "none", - "&:hover": { - backgroundColor: - "transparent", - }, }} > - + )} ))} @@ -411,6 +454,9 @@ const filteredRows = rows.filter( [`& .${paperClasses.root}`]: { padding: 0, }, + "& .MuiList-root": { + background: "#272727", // Remove any divider under menu items + }, }} > { e.stopPropagation(); - // setSelectedRow(row); + // setSelectedRow(row); setViewModal(true); }} color="primary" @@ -437,16 +483,36 @@ const filteredRows = rows.filter( > View - {viewModal && ( - handleViewButton(selectedRow?.id)} - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - tableType={tableType} - /> -)} - + {viewModal && tableType === "admin" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "vehicle" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "manager" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} +// +// +// +// ); +// }; +// export default EditManagerModal; +import React, { useEffect, useState } from "react"; +import { + Box, + Button, + Typography, + Modal, + CircularProgress, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useForm, Controller } from "react-hook-form"; +import { useDispatch } from "react-redux"; +import { updateManager } from "../../redux/slices/managerSlice"; // Import the updateManager action +import { + CustomIconButton, + CustomTextField, +} from "../AddUserModel/styled.css.tsx"; // Custom styled components + +interface EditManagerModalProps { + open: boolean; + handleClose: () => void; + editRow: any; // Manager data including id +} + +interface FormData { + name: string; + email: string; + registeredAddress: string; + phone: string; +} + +const EditManagerModal: React.FC = ({ + open, + handleClose, + editRow, +}) => { + const dispatch = useDispatch(); // Use dispatch to send Redux actions + const { + control, + handleSubmit, + formState: { errors }, + setValue, + reset, + } = useForm({ + defaultValues: { + name: "", + email: "", + registeredAddress: "", + phone: "", + }, + }); + + const [loading, setLoading] = useState(false); + + // Set values if editRow is provided + useEffect(() => { + if (editRow) { + setValue("name", editRow.name); + setValue("email", editRow.email); + setValue("registeredAddress", editRow.registeredAddress); + setValue("phone", editRow.phone); + } else { + reset(); + } + }, [editRow, setValue, reset]); + + const onSubmit = async (data: FormData) => { + if (editRow) { + setLoading(true); // Start loading state + + // Dispatch the updateManager action from Redux + try { + await dispatch( + updateManager({ + id: editRow.id, // Manager ID + managerData: { + name: data.name, + email: data.email, + registeredAddress: data.registeredAddress, + phone: data.phone, + }, + }) + ).unwrap(); // Ensure that it throws an error if the update fails + + handleClose(); // Close modal on success + reset(); // Reset form fields after submit + } catch (error) { + console.error(error); + // You can handle the error here or show a toast + } finally { + setLoading(false); // Stop loading state + } + } + }; + + return ( + + + {/* Header */} + + + Edit Manager + + + + + + + {/* Horizontal Line */} + + + {/* Input Fields */} + + {/* Manager Name */} + + + Manager Name + + ( + + )} + /> + + + {/* Registered Address */} + + + Registered Address + + ( + + )} + /> + + + {/* Email */} + + + Email + + ( + + )} + /> + + + {/* Phone Number */} + + + Phone Number + + ( + + )} + /> + + + + {/* Submit Button */} + + + + + + ); +}; + +export default EditManagerModal; diff --git a/src/components/EditVehicleModal/index.tsx b/src/components/EditVehicleModal/index.tsx new file mode 100644 index 0000000..3d51f09 --- /dev/null +++ b/src/components/EditVehicleModal/index.tsx @@ -0,0 +1,310 @@ +import React, { useEffect } from "react"; +import { + Box, + Button, + Typography, + Modal, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useForm, Controller } from "react-hook-form"; +import { updateVehicle } from "../../redux/slices/VehicleSlice"; +import { + CustomIconButton, + CustomTextField, +} from "../AddUserModel/styled.css.tsx"; +interface EditVehicleModalProps { + open: boolean; + handleClose: () => void; + handleUpdate: ( + id: string, + name: string, + email: string, + phone: string, + registeredAddress: string, + imageUrl: string + ) => void; + editRow: any; +} + +interface FormData { + name: string; + company: string; + modelName: string; + chargeType: string; + imageUrl: string; +} + +const EditVehicleModal: React.FC = ({ + open, + handleClose, + handleUpdate, + editRow, +}) => { + const { + control, + handleSubmit, + formState: { errors }, + setValue, + reset, + } = useForm({ + defaultValues: { + name: "", + company: "", + modelName: "", + chargeType: "", + imageUrl: "", + }, + }); + + // Set values if editRow is provided + useEffect(() => { + if (editRow) { + setValue("name", editRow.name); + setValue("company", editRow.company); + setValue("modelName", editRow.modelName); + setValue("chargeType", editRow.chargeType); + setValue("imageUrl", editRow.imageUrl); + } else { + reset(); + } + }, [editRow, setValue, reset]); + + const onSubmit = (data: FormData) => { + if (editRow) { + handleUpdate( + editRow.id, + data.name, + data.company, + data.modelName, + data.chargeType, + data.imageUrl + ); + } + handleClose(); // Close the modal + reset(); // Reset the form fields + }; + + return ( + + + {/* Header */} + + + Edit Vehicle + + + + + + + {/* Horizontal Line */} + + + {/* Input Fields */} + + {/* First Row - Two Inputs */} + + + + Vehicle Name + + ( + + )} + /> + + + + + Company + + ( + + )} + /> + + + + {/* Second Row - Two Inputs */} + + + + Model Name + + ( + + )} + /> + + + + + Charge Type + + ( + + )} + /> + + + + {/* Third Row - Image URL Input */} + + + Image URL + + ( + + )} + /> + + + + {/* Submit Button */} + + + + + + ); +}; + +export default EditVehicleModal; diff --git a/src/components/MenuContent/index.tsx b/src/components/MenuContent/index.tsx index 31207fd..25539bb 100644 --- a/src/components/MenuContent/index.tsx +++ b/src/components/MenuContent/index.tsx @@ -49,6 +49,11 @@ export default function MenuContent({ hidden }: PropType) { icon: , url: "/panel/manager-list", // Placeholder for now }, + userRole === "admin" && { + text: "Vehicles", + icon: , + url: "/panel/vehicles", // Placeholder for now + }, ]; const filteredMenuItems = baseMenuItems.filter(Boolean); diff --git a/src/components/Modals/DeleteModal/index.tsx b/src/components/Modals/DeleteModal/index.tsx index b9cd5fa..572f59a 100644 --- a/src/components/Modals/DeleteModal/index.tsx +++ b/src/components/Modals/DeleteModal/index.tsx @@ -1,108 +1,98 @@ -import { Box, Modal, Typography, Divider, Button } from "@mui/material"; -import { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import { RootState, AppDispatch } from "../../../redux/store/store"; -import { deleteManager, fetchManagerList } from "../../../redux/slices/managerSlice"; - -type User = { - id: number; - name: string; - email?: string; - phone: string; - registeredAddress?: string; -}; +import { Box, Button, Modal, Typography } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; type Props = { - open: boolean; - setDeleteModal: (value: boolean) => void; - id?: number; - tableType?: "admin" | "manager"; + open: boolean; + setDeleteModal: Function; + handleDelete: (id: string | undefined) => void; + id?: string | undefined; }; 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: 330, + bgcolor: "background.paper", + borderRadius: 1.5, + boxShadow: 24, + p: 3, }; -export default function DeleteModal({ open, setDeleteModal, id, tableType }: Props) { - const dispatch = useDispatch(); - const adminList = useSelector((state: RootState) => state.adminReducer.admins) || []; - const managerList = useSelector((state: RootState) => state.managerReducer.managers) || []; +const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" }; - const [selectedItem, setSelectedItem] = useState(null); - const [loading, setLoading] = useState(false); +export default function DeleteModal({ + open, + setDeleteModal, + handleDelete, + id, +}: Props) { + // console.log("DeleteModal opened with ID:", id) - 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 - )} - - - - - - - - ); + return ( + + + + Delete Record + + setDeleteModal(false)} + sx={{ + cursor: "pointer", + display: "flex", + alignItems: "center", + justifyContent: "flex-end", // Aligns the close icon to the right + marginTop: -3.5, + }} + > + + + + Are you sure you want to delete this record? + + + + + + + + ); } diff --git a/src/components/Modals/VehicleViewModal/index.tsx b/src/components/Modals/VehicleViewModal/index.tsx new file mode 100644 index 0000000..8995770 --- /dev/null +++ b/src/components/Modals/VehicleViewModal/index.tsx @@ -0,0 +1,133 @@ +import React, { useEffect, useState } from "react"; +import { Box, Modal, Typography, Divider, Grid } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../redux/reducers"; + +type Props = { + open: boolean; + setViewModal: Function; + handleView: (id: string | undefined) => void; + id?: number | undefined; +}; + +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, +}; + +export default function VehicleViewModal({ open, setViewModal, id }: Props) { + const { vehicles } = useSelector( + (state: RootState) => state.vehicleReducer + ); + const [selectedVehicle, setSelectedVehicle] = useState(null); + + useEffect(() => { + if (id) { + const vehicle = vehicles.find((vehicle) => vehicle.id === id); + setSelectedVehicle(vehicle || null); + } + }, [id, vehicles]); + + return ( + + + + + + {selectedVehicle?.name || "Vehicle"}'s Details + + setViewModal(false)} + sx={{ + cursor: "pointer", + display: "flex", + alignItems: "center", + }} + > + + + + + + + + {selectedVehicle ? ( + + + + Name:{" "} + + {selectedVehicle.name} + + + + + + Company:{" "} + + {selectedVehicle.company} + + + + + + Model Name: + + {selectedVehicle.modelName} + + + + + + Charge Type: + + {selectedVehicle.chargeType} + + + + + + Image URL: + + {selectedVehicle.imageUrl ?? "N/A"} + + + + + ) : ( + + No vehicle found with this ID + + )} + + + ); +} diff --git a/src/components/Modals/ViewManagerModal/index.tsx b/src/components/Modals/ViewManagerModal/index.tsx new file mode 100644 index 0000000..4b4b850 --- /dev/null +++ b/src/components/Modals/ViewManagerModal/index.tsx @@ -0,0 +1,133 @@ +import React, { useEffect, useState } from "react"; +import { Box, Modal, Typography, Divider, Grid } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../redux/reducers"; + +type Props = { + open: boolean; + setViewModal: Function; + handleView: (id: string | undefined) => void; + id?: number | undefined; +}; + +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, +}; + +export default function ManagerViewModal({ open, setViewModal, id }: Props) { + const { managers } = useSelector( + (state: RootState) => state.managerReducer + ); + const [selectedManager, setSelectedManager] = useState(null); + + useEffect(() => { + if (id) { + const manager = managers.find((manager) => manager.id === id); + setSelectedManager(manager || null); + } + }, [id, managers]); + + return ( + + + + + + {selectedManager?.name || "Manager"}'s Details + + setViewModal(false)} + sx={{ + cursor: "pointer", + display: "flex", + alignItems: "center", + }} + > + + + + + + + + {selectedManager ? ( + + + + Name: + + {selectedManager.name} + + + + + + Email: + + {selectedManager.email} + + + + + + Phone: + + {selectedManager.phone} + + + + + + Registered Address: + + {selectedManager.registeredAddress} + + + + + + Station Name: + + {selectedManager.stationName} + + + + + ) : ( + + No manager found with this ID + + )} + + + ); +} diff --git a/src/components/Modals/ViewModal/index.tsx b/src/components/Modals/ViewModal/index.tsx index 474065d..502c11c 100644 --- a/src/components/Modals/ViewModal/index.tsx +++ b/src/components/Modals/ViewModal/index.tsx @@ -1,95 +1,123 @@ -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 { Box, Button, Modal, Typography, Divider, Grid } from "@mui/material"; import { RootState } from "../../../redux/store/store"; - -type User = { - id: number; - name: string; - email?: string; - phone: string; - registeredAddress?: string; - action?: any; -}; +import { useSelector } from "react-redux"; +import { useEffect, useState } from "react"; +import CloseIcon from "@mui/icons-material/Close"; type Props = { - open: boolean; - setViewModal: (value: boolean) => void; - id?: number; - tableType?: "admin" | "manager"; + open: boolean; + setViewModal: Function; + id?: string; }; 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, }; -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 || "Admin"}'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} + + + + + + Phone: + + {selectedAdmin.phone} + + + + + + Email: + + {selectedAdmin.email} + + + + + + Address: - 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 - )} - - - ); + + {selectedAdmin.registeredAddress ?? "N/A"} + + + + + ) : ( + + No admin found with this ID + + )} + + + ); } diff --git a/src/components/OptionsMenu/index.tsx b/src/components/OptionsMenu/index.tsx index 42a89f9..4a66055 100644 --- a/src/components/OptionsMenu/index.tsx +++ b/src/components/OptionsMenu/index.tsx @@ -25,7 +25,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) { return ( - + { return config; }); +http.interceptors.response.use( + (response) => response, + (error) => { + if (error.response && error.response.status === 401) { + + window.location.href = "/login"; + + // const history = useHistory(); + // history.push("/login"); + } + return Promise.reject(error); + } +); export default http; diff --git a/src/pages/AddEditRolePage/index.tsx b/src/pages/AddEditRolePage/index.tsx index 9a50015..7641930 100644 --- a/src/pages/AddEditRolePage/index.tsx +++ b/src/pages/AddEditRolePage/index.tsx @@ -11,9 +11,8 @@ import { Typography, Box, Grid, - FormControlLabel, Button, - TextField, + FormControlLabel, Snackbar, } from "@mui/material"; import { useNavigate } from "react-router-dom"; // Import useNavigate @@ -21,6 +20,9 @@ import { useDispatch, useSelector } from "react-redux"; import { createRole } from "../../redux/slices/roleSlice"; // Import the createRole action import { AppDispatch, RootState } from "../../redux/store/store"; // Assuming this is the path to your store file import { toast } from "sonner"; +import { + CustomTextField, +} from "../../components/AddUserModel/styled.css.tsx"; // Define the data structure for permission interface Permission { module: string; @@ -155,7 +157,7 @@ const AddEditRolePage: React.FC = () => { {/* Role Name Input */} - { {/* Submit Button */} diff --git a/src/pages/AddManagerModal/index.tsx b/src/pages/AddManagerModal/index.tsx deleted file mode 100644 index 64ac133..0000000 --- a/src/pages/AddManagerModal/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -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/Auth/Login/ForgotPassword.tsx b/src/pages/Auth/Login/ForgotPassword.tsx index 83448f9..d2d9d1c 100644 --- a/src/pages/Auth/Login/ForgotPassword.tsx +++ b/src/pages/Auth/Login/ForgotPassword.tsx @@ -14,44 +14,49 @@ interface ForgotPasswordProps { export default function ForgotPassword({ open, handleClose }: ForgotPasswordProps) { return ( - ) => { - event.preventDefault(); - handleClose(); - }, - sx: { backgroundImage: 'none' }, - }} - > - Reset password - - - Enter your account's email address, and we'll send you a link to - reset your password. - - - - - - - - + ) => { + event.preventDefault(); + handleClose(); + }, + sx: { backgroundImage: "none" }, + }} + > + Reset password + + + Enter your account's email address, and we'll send + you a link to reset your password. + + + + + + + + ); } diff --git a/src/pages/Auth/Login/index.tsx b/src/pages/Auth/Login/index.tsx index 827e6e3..45cadd2 100644 --- a/src/pages/Auth/Login/index.tsx +++ b/src/pages/Auth/Login/index.tsx @@ -50,15 +50,15 @@ export default function Login(props: { disableCustomTheme?: boolean }) { }; const onSubmit: SubmitHandler = async (data: ILoginForm) => { - try { - const response = await dispatch(loginUser(data)).unwrap(); - if (response?.data?.token) { - router("/panel/dashboard"); - } - } catch (error: any) { - console.log("Login failed:", error); + try { + const response = await dispatch(loginUser(data)).unwrap(); + if (response?.data?.token) { + router("/panel/dashboard"); } - }; + } catch (error: any) { + console.log("Login failed:", error); + } +}; return ( diff --git a/src/pages/EditUserModal/index.tsx b/src/pages/EditUserModal/index.tsx deleted file mode 100644 index 0747aad..0000000 --- a/src/pages/EditUserModal/index.tsx +++ /dev/null @@ -1,191 +0,0 @@ -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 index 3514684..8f84d70 100644 --- a/src/pages/ManagerList/index.tsx +++ b/src/pages/ManagerList/index.tsx @@ -1,240 +1,157 @@ import React, { useEffect, useState } from "react"; -import { Box, Button, Typography, TextField, InputAdornment, IconButton } from "@mui/material"; +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 AddManagerModal from "../../components/AddManagerModal"; import { useDispatch, useSelector } from "react-redux"; -import { fetchManagerList, addManager,updateManager,deleteManager } from "../../redux/slices/managerSlice"; +import DeleteModal from "../../components/Modals/DeleteModal"; +import { + managerList, + addManager, + updateManager, + deleteManager, +} from "../../redux/slices/managerSlice"; import { RootState, AppDispatch } from "../../redux/store/store"; - +import { useForm } from "react-hook-form"; +import EditManagerModal from "../../components/EditManagerModal"; export default function ManagerList() { - const dispatch = useDispatch(); - const { managers, isLoading } = useSelector((state: RootState) => state.managerReducer); + const [addModalOpen, setAddModalOpen] = useState(false); + const [editModalOpen, setEditModalOpen] = useState(false); + const [editRow, setEditRow] = useState(null); + const { reset } = useForm(); - 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); + const [deleteModal, setDeleteModal] = useState(false); + const [viewModal, setViewModal] = useState(false); + const [rowData, setRowData] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); + const dispatch = useDispatch(); + const managers = useSelector( + (state: RootState) => state.managerReducer.managers + ); - useEffect(() => { - dispatch(fetchManagerList()); // Fetch data when component mounts - }, [dispatch]); + useEffect(() => { + dispatch(managerList()); + }, [dispatch]); - + const handleClickOpen = () => { + setRowData(null); // Reset row data when opening for new admin + setAddModalOpen(true); + }; - // 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 - }; + const handleCloseModal = () => { + setAddModalOpen(false); + setEditModalOpen(false); + setRowData(null); + reset(); + }; + + const handleAddManager = async (data: { + name: string; + email: string; + phone: string; + registeredAddress: string; + }) => { + try { + await dispatch(addManager(data)); // Dispatch action to add manager + await dispatch(managerList()); // Fetch the updated list + handleCloseModal(); // Close the modal + } catch (error) { + console.error("Error adding manager", error); + } + }; + + const handleUpdate = async ( + id: number, + name: string, + email: string, + phone: string, + registeredAddress: string + ) => { + try { + // Creating the managerData object to match the expected payload structure + const managerData = { + name, + email, + phone, + registeredAddress, + }; + + // Dispatching the updateManager action with the correct payload structure + await dispatch(updateManager({ id, managerData })); + await dispatch(managerList()); // Refresh the manager list after updating + handleCloseModal(); // Close the modal after updating + } catch (error) { + console.error("Update failed", error); + } + }; + // Remove 'stationName' from columns + const categoryColumns: Column[] = [ + { id: "srno", label: "Sr No" }, + { id: "name", label: "Name" }, + { id: "email", label: "Email" }, + { id: "phone", label: "Phone" }, + { id: "registeredAddress", label: "Station Location" }, - // 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 + { id: "action", label: "Action", align: "center" }, + ]; + // Update rows to remove 'stationName' + const categoryRows = managers?.map( + ( + manager: { + id: number; + name: string; + email: string; + phone: string; + registeredAddress: string; + }, + index: number + ) => ({ + id: manager?.id, + srno: index + 1, + name: manager?.name, + email: manager?.email, + phone: manager.phone ?? "NA", + registeredAddress: manager?.registeredAddress ?? "NA", + }) + ); - 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, - }) - } - /> - - ); + return ( + <> + setEditModalOpen(true)} + tableType="manager" + handleClickOpen={handleClickOpen} + /> + + + + ); } diff --git a/src/pages/VehicleList/index.tsx b/src/pages/VehicleList/index.tsx index 2f49cec..6f240f5 100644 --- a/src/pages/VehicleList/index.tsx +++ b/src/pages/VehicleList/index.tsx @@ -12,55 +12,18 @@ import { updateVehicle, vehicleList, } from "../../redux/slices/VehicleSlice"; -import SearchIcon from "@mui/icons-material/Search"; -const categoryRows = [ - { - srno: 1, - id: 1, // Using a number for 'id' - name: "Tesla Model S", - brand: "Tesla", - imageUrl: - "https://example.com/https://imgd-ct.aeplcdn.com/1056x660/n/cw/ec/93821/model-s-exterior-front-view.jpeg?q=80.jpg", - }, - { - srno: 2, - id: 2, - name: "BMW X5", - brand: "BMW", - imageUrl: "https://example.com/bmw_x5.jpg", - }, - { - srno: 3, - id: 3, - name: "Audi A6", - brand: "Audi", - imageUrl: "https://example.com/audi_a6.jpg", - }, - { - srno: 4, - id: 4, - name: "Mercedes-Benz S-Class", - brand: "Mercedes-Benz", - imageUrl: "https://example.com/mercedes_s_class.jpg", - }, - { - srno: 5, - id: 5, - name: "Ford Mustang", - brand: "Ford", - imageUrl: "https://example.com/ford_mustang.jpg", - }, -]; - +import AddVehicleModal from "../../components/AddVehicleModal"; +import EditVehicleModal from "../../components/EditVehicleModal"; export default function VehicleList() { - const [modalOpen, setModalOpen] = useState(false); + const [addModalOpen, setAddModalOpen] = useState(false); + const [editModalOpen, setEditModalOpen] = useState(false); const [editRow, setEditRow] = useState(null); const { reset } = useForm(); - const [deleteModal, setDeleteModal] = React.useState(false); - const [viewModal, setViewModal] = React.useState(false); - const [rowData, setRowData] = React.useState(null); + const [deleteModal, setDeleteModal] = useState(false); + const [viewModal, setViewModal] = useState(false); + const [rowData, setRowData] = useState(null); const [searchTerm, setSearchTerm] = useState(""); const dispatch = useDispatch(); const vehicles = useSelector( @@ -73,32 +36,38 @@ export default function VehicleList() { const handleClickOpen = () => { setRowData(null); // Reset row data when opening for new admin - setModalOpen(true); + setAddModalOpen(true); }; const handleCloseModal = () => { - setModalOpen(false); + setAddModalOpen(false); + setEditModalOpen(false); setRowData(null); reset(); }; - const handleCreate = async (data: { - name: string; - brand: string; + const handleAddVehicle = async (data: { + vehicleName: string; + company: string; + modelName: string; + chargeType: string; imageUrl: string; }) => { try { - await dispatch(addVehicle(data)); - await dispatch(vehicleList()); - handleCloseModal(); + await dispatch(addVehicle(data)); // Dispatch action to add vehicle + await dispatch(vehicleList()); // Fetch the updated list + handleCloseModal(); // Close the modal } catch (error) { - console.error("Creation failed", error); + console.error("Error adding vehicle", error); } }; + const handleUpdate = async ( id: number, name: string, - brand: string, + company: string, + modelName: string, + chargeType: string, imageUrl: string ) => { try { @@ -106,53 +75,70 @@ export default function VehicleList() { updateVehicle({ id, name, - brand, + company, + modelName, + chargeType, imageUrl, }) ); await dispatch(vehicleList()); + handleCloseModal(); } catch (error) { console.error("Update failed", error); } }; + const categoryColumns: Column[] = [ { id: "srno", label: "Sr No" }, { id: "name", label: "Vehicle Name" }, - { id: "brand", label: "Brand" }, + { id: "company", label: "Company" }, + { id: "modelName", label: "Model Name" }, + { id: "chargeType", label: "Charge Type" }, { id: "imageUrl", label: "Image" }, { id: "action", label: "Action", align: "center" }, ]; const filteredVehicles = vehicles?.filter( (vehicle) => - vehicle.name.toLowerCase().includes(searchTerm.toLowerCase()) || - vehicle.brand.toLowerCase().includes(searchTerm.toLowerCase()) + vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) || + vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) || + vehicle.modelName + ?.toLowerCase() + .includes(searchTerm.toLowerCase()) || + vehicle.chargeType + ?.toLowerCase() + .includes(searchTerm.toLowerCase()) || + vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase()) ); -// const categoryRows = filteredVehicles?.length -// ? filteredVehicles?.map( -// ( -// vehicle: { -// id: number; -// name: string; -// brand: string; -// imageUrl: string; -// }, -// index: number -// ) => ({ -// id: vehicle?.id, -// srno: index + 1, -// name: vehicle?.name, -// brand: vehicle?.brand, -// imageUrl: vehicle?.imageUrl, -// }) -// ) -// : []; + const categoryRows = filteredVehicles?.length + ? filteredVehicles?.map( + ( + vehicle: { + id: number; + name: string; + company: string; + modelName: string; + chargeType: string; + imageUrl: string; + }, + index: number + ) => ({ + id: vehicle?.id, + srno: index + 1, + name: vehicle?.name, + company: vehicle?.company, + modelName: vehicle?.modelName, + chargeType: vehicle?.chargeType, + imageUrl: vehicle?.imageUrl, + }) + ) + : []; + + return ( <> - - setEditModalOpen(true)} tableType="vehicle" handleClickOpen={handleClickOpen} /> - {/* - */} + + + ); } diff --git a/src/redux/slices/VehicleSlice.ts b/src/redux/slices/VehicleSlice.ts index 9db0869..e637526 100644 --- a/src/redux/slices/VehicleSlice.ts +++ b/src/redux/slices/VehicleSlice.ts @@ -3,14 +3,15 @@ import http from "../../lib/https"; import { toast } from "sonner"; interface Vehicle { - id:number; - name:string; - brand:string; - imageUrl:string; - + id: number; + name: string; + company: string; + modelName: string; + chargeType: string; + imageUrl: string; } interface VehicleState { - vehicles:Vehicle[]; + vehicles: Vehicle[]; loading: boolean; error: string | null; } @@ -20,39 +21,42 @@ const initialState: VehicleState = { error: null, }; -export const vehicleList = createAsyncThunk( - "fetchVehicles", - async (_, { rejectWithValue }) => { - try { - const token = localStorage?.getItem("authToken"); - if (!token) throw new Error("No token found"); +export const vehicleList = createAsyncThunk< + Vehicle, + void, + { rejectValue: string } +>("fetchVehicles", async (_, { rejectWithValue }) => { + try { + const token = localStorage?.getItem("authToken"); + if (!token) throw new Error("No token found"); - const response = await http.get("/"); + const response = await http.get("/get-vehicles"); - if (!response.data?.data) throw new Error("Invalid API response"); + if (!response.data?.data) throw new Error("Invalid API response"); - return response.data.data; - } catch (error: any) { - toast.error("Error Fetching Profile" + error); - return rejectWithValue( - error?.response?.data?.message || "An error occurred" - ); - } + return response.data.data; + } catch (error: any) { + toast.error("Error Fetching Profile" + error); + return rejectWithValue( + error?.response?.data?.message || "An error occurred" + ); } -); +}); //Add Vehicle export const addVehicle = createAsyncThunk< Vehicle, { name: string; - brand: string; + company: string; + modelName: string; + chargeType: string; imageUrl: string; }, { rejectValue: string } >("/AddVehicle", async (data, { rejectWithValue }) => { try { - const response = await http.post("/", data); + const response = await http.post("create-vehicle", data); return response.data; } catch (error: any) { return rejectWithValue( @@ -61,13 +65,15 @@ export const addVehicle = createAsyncThunk< } }); - // Update Vehicle details export const updateVehicle = createAsyncThunk( "updateVehicle", async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => { try { - const response = await http.put(`/${id}`, vehicleData); + const response = await http.patch( + `${id}/update-vehicle`, + vehicleData + ); toast.success("Vehicle Deatils updated successfully"); return response?.data; } catch (error: any) { @@ -78,6 +84,23 @@ export const updateVehicle = createAsyncThunk( } } ); +export const deleteVehicle = createAsyncThunk< + string, + string, + { rejectValue: string } +>("deleteVehicle", async (id, { rejectWithValue }) => { + try { + const response = await http.delete(`/${id}/delete-vehicle`); + toast.success(response.data?.message); + return id; + } catch (error: any) { + toast.error("Error deleting the vehicle" + error); + + return rejectWithValue( + error.response?.data?.message || "An error occurred" + ); + } +}); const vehicleSlice = createSlice({ name: "vehicle", initialState, @@ -125,6 +148,18 @@ const vehicleSlice = createSlice({ }) .addCase(updateVehicle.rejected, (state) => { state.loading = false; + }) + .addCase(deleteVehicle.pending, (state) => { + state.loading = true; + }) + .addCase(deleteVehicle.fulfilled, (state, action) => { + state.loading = false; + state.vehicles = state.vehicles.filter( + (vehicle) => String(vehicle.id) !== String(action.payload) + ); + }) + .addCase(deleteVehicle.rejected, (state) => { + state.loading = false; }); }, }); diff --git a/src/redux/slices/authSlice.ts b/src/redux/slices/authSlice.ts index 549ecdf..2f0f3f2 100644 --- a/src/redux/slices/authSlice.ts +++ b/src/redux/slices/authSlice.ts @@ -90,11 +90,8 @@ const initialState: AuthState = { isAuthenticated: false, isLoading: false, // error: null, - //Eknoor singh - //date:- 12-Feb-2025 - //initial state of token set to null token: null, - role: null, // New field for role + role: null, }; const authSlice = createSlice({ @@ -144,14 +141,7 @@ const authSlice = createSlice({ } ); - // created by Jaanvi and Eknoor - //AdminList - - //Eknoor singh - //date:- 12-Feb-2025 - //Reducers for fetching profiles has been implemente }, }); -// export const { logout } = authSlice.actions; export default authSlice.reducer; diff --git a/src/redux/slices/managerSlice.ts b/src/redux/slices/managerSlice.ts index 4d1f415..c5a6c53 100644 --- a/src/redux/slices/managerSlice.ts +++ b/src/redux/slices/managerSlice.ts @@ -2,188 +2,179 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; import http from "../../lib/https"; import { toast } from "sonner"; -// Define TypeScript types +// Define the Manager interface based on the payload + interface Manager { - id: string; - name: string; - email: string; - phone?: string; - role: string; + id: number; + name: string; + email: string; + phone: string; + registeredAddress: string; + roleId: number; } interface ManagerState { - managers: Manager[]; - loading: boolean; - error: string | null; + managers: Manager[]; + loading: boolean; + error: string | null; } // Initial state const initialState: ManagerState = { - managers: [], - loading: false, - error: null, + 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"); +// Fetch Manager List (Async Thunk) +export const managerList = createAsyncThunk< + Manager[], + void, + { rejectValue: string } +>("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.message); + return rejectWithValue( + error?.response?.data?.message || "An error occurred" + ); + } +}); - 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"); - } - } -); +// Create Manager (Async Thunk) +export const addManager = createAsyncThunk< + Manager, + Manager, + { rejectValue: string } +>("addManager", async (data, { rejectWithValue }) => { + try { + const response = await http.post("create-manager", data); + toast.success("Manager created successfully"); + return response.data?.data; + } catch (error: any) { + toast.error("Error creating manager: " + error.message); + return rejectWithValue( + error.response?.data?.message || "An error occurred" + ); + } +}); +// Update Manager (Async Thunk) 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 { - + Manager, + { id: number; managerData: Manager }, + { rejectValue: string } +>("updateManager", async ({ id, managerData }, { rejectWithValue }) => { + if (!id) { + return rejectWithValue("Manager ID is required."); + } + try { + const response = await http.put(`/${id}/update-manager`, managerData); + toast.success("Manager updated successfully"); + return response?.data; + } catch (error: any) { + toast.error("Error updating manager: " + error.message); + return rejectWithValue( + error.response?.data?.message || "An error occurred" + ); + } +}); - 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 +// Delete Manager (Async Thunk) 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" - ); - } - } -); - - - - + string, + string, + { rejectValue: string } +>("deleteManager", async (id, { rejectWithValue }) => { + try { + await http.delete(`/${id}/delete-manager`); + toast.success("Manager deleted successfully!"); + return id; + } catch (error: any) { + toast.error("Error deleting manager: " + error.message); + 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"; - }) + name: "maanger", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + // Fetch Managers + .addCase(managerList.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase( + managerList.fulfilled, + (state, action: PayloadAction) => { + state.loading = false; + state.managers = action.payload; + } + ) + .addCase(managerList.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; - }) + // 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; + state.error = action.payload || "Failed to add manager"; + }) - // 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; - }) + // 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( + // (manager) => manager.id === updatedManager.id + // ); + // if (index !== -1) { + // state.managers[index] = updatedManager; // Update the manager in the state + // } + }) + .addCase(updateManager.rejected, (state, action) => { + state.loading = false; + state.error = action.payload || "Failed to update manager"; + }) - // 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; -}); - - - }, + // 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, action) => { + state.loading = false; + state.error = action.payload || "Failed to delete manager"; + }); + }, }); export default managerSlice.reducer; diff --git a/src/shared-theme/customizations/dataDisplay.js b/src/shared-theme/customizations/dataDisplay.js index b75f8bc..5db7a2f 100644 --- a/src/shared-theme/customizations/dataDisplay.js +++ b/src/shared-theme/customizations/dataDisplay.js @@ -14,6 +14,7 @@ export const dataDisplayCustomizations = { padding: '8px', display: 'flex', flexDirection: 'column', + gap: 0, }, }, diff --git a/src/shared-theme/customizations/inputs.js b/src/shared-theme/customizations/inputs.js index 5df212d..c1e9814 100644 --- a/src/shared-theme/customizations/inputs.js +++ b/src/shared-theme/customizations/inputs.js @@ -385,7 +385,7 @@ export const inputsCustomizations = { color: (theme.vars || theme).palette.text.primary, borderRadius: (theme.vars || theme).shape.borderRadius, border: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundColor: (theme.vars || theme).palette.background.default, + backgroundColor: "#272727", transition: 'border 120ms ease-in', '&:hover': { borderColor: gray[400], diff --git a/src/shared-theme/customizations/inputs.tsx b/src/shared-theme/customizations/inputs.tsx index b384563..de02c07 100644 --- a/src/shared-theme/customizations/inputs.tsx +++ b/src/shared-theme/customizations/inputs.tsx @@ -1,445 +1,459 @@ -import * as React from 'react'; -import { alpha, Theme, Components } from '@mui/material/styles'; -import { outlinedInputClasses } from '@mui/material/OutlinedInput'; -import { svgIconClasses } from '@mui/material/SvgIcon'; -import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup'; -import { toggleButtonClasses } from '@mui/material/ToggleButton'; -import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded'; -import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; -import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded'; -import { gray, brand } from '../themePrimitives'; +import * as React from "react"; +import { alpha, Theme, Components } from "@mui/material/styles"; +import { outlinedInputClasses } from "@mui/material/OutlinedInput"; +import { svgIconClasses } from "@mui/material/SvgIcon"; +import { toggleButtonGroupClasses } from "@mui/material/ToggleButtonGroup"; +import { toggleButtonClasses } from "@mui/material/ToggleButton"; +import CheckBoxOutlineBlankRoundedIcon from "@mui/icons-material/CheckBoxOutlineBlankRounded"; +import CheckRoundedIcon from "@mui/icons-material/CheckRounded"; +import RemoveRoundedIcon from "@mui/icons-material/RemoveRounded"; +import { gray, brand } from "../themePrimitives"; /* eslint-disable import/prefer-default-export */ export const inputsCustomizations: Components = { - MuiButtonBase: { - defaultProps: { - disableTouchRipple: true, - disableRipple: true, - }, - styleOverrides: { - root: ({ theme }) => ({ - boxSizing: 'border-box', - transition: 'all 100ms ease-in', - '&:focus-visible': { - outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`, - outlineOffset: '2px', - }, - }), - }, - }, - MuiButton: { - styleOverrides: { - root: ({ theme }) => ({ - boxShadow: 'none', - borderRadius: (theme.vars || theme).shape.borderRadius, - textTransform: 'none', - variants: [ - { - props: { - size: 'small', - }, - style: { - height: '2.25rem', - padding: '8px 12px', - }, - }, - { - props: { - size: 'medium', - }, - style: { - height: '2.5rem', // 40px - }, - }, - { - props: { - color: 'primary', - variant: 'contained', - }, - style: { - color: 'white', - backgroundColor: gray[900], - backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`, - boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`, - border: `1px solid ${gray[700]}`, - '&:hover': { - backgroundImage: 'none', - backgroundColor: gray[700], - boxShadow: 'none', - }, - '&:active': { - backgroundColor: gray[800], - }, - ...theme.applyStyles('dark', { - color: 'black', - backgroundColor: gray[50], - backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`, - boxShadow: 'inset 0 -1px 0 hsl(220, 30%, 80%)', - border: `1px solid ${gray[50]}`, - '&:hover': { - backgroundImage: 'none', - backgroundColor: gray[300], - boxShadow: 'none', - }, - '&:active': { - backgroundColor: gray[400], - }, - }), - }, - }, - { - props: { - color: 'secondary', - variant: 'contained', - }, - style: { - color: 'white', - backgroundColor: brand[300], - backgroundImage: `linear-gradient(to bottom, ${alpha(brand[400], 0.8)}, ${brand[500]})`, - boxShadow: `inset 0 2px 0 ${alpha(brand[200], 0.2)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`, - border: `1px solid ${brand[500]}`, - '&:hover': { - backgroundColor: brand[700], - boxShadow: 'none', - }, - '&:active': { - backgroundColor: brand[700], - backgroundImage: 'none', - }, - }, - }, - { - props: { - variant: 'outlined', - }, - style: { - color: (theme.vars || theme).palette.text.primary, - border: '1px solid', - borderColor: gray[200], - backgroundColor: alpha(gray[50], 0.3), - '&:hover': { - backgroundColor: gray[100], - borderColor: gray[300], - }, - '&:active': { - backgroundColor: gray[200], - }, - ...theme.applyStyles('dark', { - backgroundColor: gray[800], - borderColor: gray[700], + MuiButtonBase: { + defaultProps: { + disableTouchRipple: true, + disableRipple: true, + }, + styleOverrides: { + root: ({ theme }) => ({ + boxSizing: "border-box", + transition: "all 100ms ease-in", + "&:focus-visible": { + outline: `3px solid ${alpha( + theme.palette.primary.main, + 0.5 + )}`, + outlineOffset: "2px", + }, + }), + }, + }, + MuiButton: { + styleOverrides: { + root: ({ theme }) => ({ + boxShadow: "none", + borderRadius: (theme.vars || theme).shape.borderRadius, + textTransform: "none", + variants: [ + { + props: { + size: "small", + }, + style: { + height: "2.25rem", + padding: "8px 12px", + }, + }, + { + props: { + size: "medium", + }, + style: { + height: "2.5rem", // 40px + }, + }, + { + props: { + color: "primary", + variant: "contained", + }, + style: { + color: "white", + backgroundColor: gray[900], + backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`, + boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`, + border: `1px solid ${gray[700]}`, + "&:hover": { + backgroundImage: "none", + backgroundColor: gray[700], + boxShadow: "none", + }, + "&:active": { + backgroundColor: gray[800], + }, + ...theme.applyStyles("dark", { + color: "black", + backgroundColor: gray[50], + backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`, + boxShadow: "inset 0 -1px 0 hsl(220, 30%, 80%)", + border: `1px solid ${gray[50]}`, + "&:hover": { + backgroundImage: "none", + backgroundColor: gray[300], + boxShadow: "none", + }, + "&:active": { + backgroundColor: gray[400], + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "contained", + }, + style: { + color: "white", + backgroundColor: brand[300], + backgroundImage: `linear-gradient(to bottom, ${alpha( + brand[400], + 0.8 + )}, ${brand[500]})`, + boxShadow: `inset 0 2px 0 ${alpha( + brand[200], + 0.2 + )}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`, + border: `1px solid ${brand[500]}`, + "&:hover": { + backgroundColor: brand[700], + boxShadow: "none", + }, + "&:active": { + backgroundColor: brand[700], + backgroundImage: "none", + }, + }, + }, + { + props: { + variant: "outlined", + }, + style: { + color: (theme.vars || theme).palette.text.primary, + border: "1px solid", + borderColor: gray[200], + backgroundColor: alpha(gray[50], 0.3), + "&:hover": { + backgroundColor: gray[100], + borderColor: gray[300], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + backgroundColor: gray[800], + borderColor: gray[700], - '&:hover': { - backgroundColor: gray[900], - borderColor: gray[600], - }, - '&:active': { - backgroundColor: gray[900], - }, - }), - }, - }, - { - props: { - color: 'secondary', - variant: 'outlined', - }, - style: { - color: brand[700], - border: '1px solid', - borderColor: brand[200], - backgroundColor: brand[50], - '&:hover': { - backgroundColor: brand[100], - borderColor: brand[400], - }, - '&:active': { - backgroundColor: alpha(brand[200], 0.7), - }, - ...theme.applyStyles('dark', { - color: brand[50], - border: '1px solid', - borderColor: brand[900], - backgroundColor: alpha(brand[900], 0.3), - '&:hover': { - borderColor: brand[700], - backgroundColor: alpha(brand[900], 0.6), - }, - '&:active': { - backgroundColor: alpha(brand[900], 0.5), - }, - }), - }, - }, - { - props: { - variant: 'text', - }, - style: { - color: gray[600], - '&:hover': { - backgroundColor: gray[100], - }, - '&:active': { - backgroundColor: gray[200], - }, - ...theme.applyStyles('dark', { - color: gray[50], - '&:hover': { - backgroundColor: gray[700], - }, - '&:active': { - backgroundColor: alpha(gray[700], 0.7), - }, - }), - }, - }, - { - props: { - color: 'secondary', - variant: 'text', - }, - style: { - color: brand[700], - '&:hover': { - backgroundColor: alpha(brand[100], 0.5), - }, - '&:active': { - backgroundColor: alpha(brand[200], 0.7), - }, - ...theme.applyStyles('dark', { - color: brand[100], - '&:hover': { - backgroundColor: alpha(brand[900], 0.5), - }, - '&:active': { - backgroundColor: alpha(brand[900], 0.3), - }, - }), - }, - }, - ], - }), - }, - }, - MuiIconButton: { - styleOverrides: { - root: ({ theme }) => ({ - boxShadow: 'none', - borderRadius: (theme.vars || theme).shape.borderRadius, - textTransform: 'none', - fontWeight: theme.typography.fontWeightMedium, - letterSpacing: 0, - color: (theme.vars || theme).palette.text.primary, - border: '1px solid ', - borderColor: gray[200], - backgroundColor: alpha(gray[50], 0.3), - '&:hover': { - backgroundColor: gray[100], - borderColor: gray[300], - }, - '&:active': { - backgroundColor: gray[200], - }, - ...theme.applyStyles('dark', { - backgroundColor: gray[800], - borderColor: gray[700], - '&:hover': { - backgroundColor: gray[900], - borderColor: gray[600], - }, - '&:active': { - backgroundColor: gray[900], - }, - }), - variants: [ - { - props: { - size: 'small', - }, - style: { - width: '2.25rem', - height: '2.25rem', - padding: '0.25rem', - [`& .${svgIconClasses.root}`]: { fontSize: '1rem' }, - }, - }, - { - props: { - size: 'medium', - }, - style: { - width: '2.5rem', - height: '2.5rem', - }, - }, - ], - }), - }, - }, - MuiToggleButtonGroup: { - styleOverrides: { - root: ({ theme }) => ({ - borderRadius: '10px', - boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`, - [`& .${toggleButtonGroupClasses.selected}`]: { - color: brand[500], - }, - ...theme.applyStyles('dark', { - [`& .${toggleButtonGroupClasses.selected}`]: { - color: '#fff', - }, - boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`, - }), - }), - }, - }, - MuiToggleButton: { - styleOverrides: { - root: ({ theme }) => ({ - padding: '12px 16px', - textTransform: 'none', - borderRadius: '10px', - fontWeight: 500, - ...theme.applyStyles('dark', { - color: gray[400], - boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)', - [`&.${toggleButtonClasses.selected}`]: { - color: brand[300], - }, - }), - }), - }, - }, - MuiCheckbox: { - defaultProps: { - disableRipple: true, - icon: ( - - ), - checkedIcon: , - indeterminateIcon: , - }, - styleOverrides: { - root: ({ theme }) => ({ - margin: 10, - height: 16, - width: 16, - borderRadius: 5, - border: '1px solid ', - borderColor: alpha(gray[300], 0.8), - boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset', - backgroundColor: alpha(gray[100], 0.4), - transition: 'border-color, background-color, 120ms ease-in', - '&:hover': { - borderColor: brand[300], - }, - '&.Mui-focusVisible': { - outline: `3px solid ${alpha(brand[500], 0.5)}`, - outlineOffset: '2px', - borderColor: brand[400], - }, - '&.Mui-checked': { - color: 'white', - backgroundColor: brand[500], - borderColor: brand[500], - boxShadow: `none`, - '&:hover': { - backgroundColor: brand[600], - }, - }, - ...theme.applyStyles('dark', { - borderColor: alpha(gray[700], 0.8), - boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset', - backgroundColor: alpha(gray[900], 0.8), - '&:hover': { - borderColor: brand[300], - }, - '&.Mui-focusVisible': { - borderColor: brand[400], - outline: `3px solid ${alpha(brand[500], 0.5)}`, - outlineOffset: '2px', - }, - }), - }), - }, - }, - MuiInputBase: { - styleOverrides: { - root: { - border: 'none', - }, - input: { - '&::placeholder': { - opacity: 0.7, - color: gray[500], - }, - }, - }, - }, - MuiOutlinedInput: { - styleOverrides: { - input: { - padding: 0, - }, - root: ({ theme }) => ({ - padding: '8px 12px', - color: (theme.vars || theme).palette.text.primary, - borderRadius: (theme.vars || theme).shape.borderRadius, - border: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundColor: (theme.vars || theme).palette.background.default, - transition: 'border 120ms ease-in', - '&:hover': { - borderColor: gray[400], - }, - [`&.${outlinedInputClasses.focused}`]: { - outline: `3px solid ${alpha(brand[500], 0.5)}`, - borderColor: brand[400], - }, - ...theme.applyStyles('dark', { - '&:hover': { - borderColor: gray[500], - }, - }), - variants: [ - { - props: { - size: 'small', - }, - style: { - height: '2.25rem', - }, - }, - { - props: { - size: 'medium', - }, - style: { - height: '2.5rem', - }, - }, - ], - }), - notchedOutline: { - border: 'none', - }, - }, - }, - MuiInputAdornment: { - styleOverrides: { - root: ({ theme }) => ({ - color: (theme.vars || theme).palette.grey[500], - ...theme.applyStyles('dark', { - color: (theme.vars || theme).palette.grey[400], - }), - }), - }, - }, - MuiFormLabel: { - styleOverrides: { - root: ({ theme }) => ({ - typography: theme.typography.caption, - marginBottom: 8, - }), - }, - }, + "&:hover": { + backgroundColor: gray[900], + borderColor: gray[600], + }, + "&:active": { + backgroundColor: gray[900], + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "outlined", + }, + style: { + color: brand[700], + border: "1px solid", + borderColor: brand[200], + backgroundColor: brand[50], + "&:hover": { + backgroundColor: brand[100], + borderColor: brand[400], + }, + "&:active": { + backgroundColor: alpha(brand[200], 0.7), + }, + ...theme.applyStyles("dark", { + color: brand[50], + border: "1px solid", + borderColor: brand[900], + backgroundColor: alpha(brand[900], 0.3), + "&:hover": { + borderColor: brand[700], + backgroundColor: alpha(brand[900], 0.6), + }, + "&:active": { + backgroundColor: alpha(brand[900], 0.5), + }, + }), + }, + }, + { + props: { + variant: "text", + }, + style: { + color: gray[600], + "&:hover": { + backgroundColor: gray[100], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + color: gray[50], + "&:hover": { + backgroundColor: gray[700], + }, + "&:active": { + backgroundColor: alpha(gray[700], 0.7), + }, + }), + }, + }, + { + props: { + color: "secondary", + variant: "text", + }, + style: { + color: brand[700], + "&:hover": { + backgroundColor: alpha(brand[100], 0.5), + }, + "&:active": { + backgroundColor: alpha(brand[200], 0.7), + }, + ...theme.applyStyles("dark", { + color: brand[100], + "&:hover": { + backgroundColor: alpha(brand[900], 0.5), + }, + "&:active": { + backgroundColor: alpha(brand[900], 0.3), + }, + }), + }, + }, + ], + }), + }, + }, + MuiIconButton: { + styleOverrides: { + root: ({ theme }) => ({ + boxShadow: "none", + borderRadius: (theme.vars || theme).shape.borderRadius, + textTransform: "none", + fontWeight: theme.typography.fontWeightMedium, + letterSpacing: 0, + color: (theme.vars || theme).palette.text.primary, + border: "1px solid ", + borderColor: gray[200], + backgroundColor: alpha(gray[50], 0.3), + "&:hover": { + backgroundColor: gray[100], + borderColor: gray[300], + }, + "&:active": { + backgroundColor: gray[200], + }, + ...theme.applyStyles("dark", { + backgroundColor: gray[800], + borderColor: gray[700], + "&:hover": { + backgroundColor: gray[900], + borderColor: gray[600], + }, + "&:active": { + backgroundColor: gray[900], + }, + }), + variants: [ + { + props: { + size: "small", + }, + style: { + width: "2.25rem", + height: "2.25rem", + padding: "0.25rem", + [`& .${svgIconClasses.root}`]: { fontSize: "1rem" }, + }, + }, + { + props: { + size: "medium", + }, + style: { + width: "2.5rem", + height: "2.5rem", + }, + }, + ], + }), + }, + }, + MuiToggleButtonGroup: { + styleOverrides: { + root: ({ theme }) => ({ + borderRadius: "10px", + boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`, + [`& .${toggleButtonGroupClasses.selected}`]: { + color: brand[500], + }, + ...theme.applyStyles("dark", { + [`& .${toggleButtonGroupClasses.selected}`]: { + color: "#fff", + }, + boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`, + }), + }), + }, + }, + MuiToggleButton: { + styleOverrides: { + root: ({ theme }) => ({ + padding: "12px 16px", + textTransform: "none", + borderRadius: "10px", + fontWeight: 500, + ...theme.applyStyles("dark", { + color: gray[400], + boxShadow: "0 4px 16px rgba(0, 0, 0, 0.5)", + [`&.${toggleButtonClasses.selected}`]: { + color: brand[300], + }, + }), + }), + }, + }, + MuiCheckbox: { + defaultProps: { + disableRipple: true, + icon: ( + + ), + checkedIcon: , + indeterminateIcon: ( + + ), + }, + styleOverrides: { + root: ({ theme }) => ({ + margin: 10, + height: 16, + width: 16, + borderRadius: 5, + border: "1px solid ", + borderColor: alpha(gray[300], 0.8), + boxShadow: "0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset", + backgroundColor: alpha(gray[100], 0.4), + transition: "border-color, background-color, 120ms ease-in", + "&:hover": { + borderColor: brand[300], + }, + "&.Mui-focusVisible": { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: "2px", + borderColor: brand[400], + }, + "&.Mui-checked": { + color: "white", + backgroundColor: brand[500], + borderColor: brand[500], + boxShadow: `none`, + "&:hover": { + backgroundColor: brand[600], + }, + }, + ...theme.applyStyles("dark", { + borderColor: alpha(gray[700], 0.8), + boxShadow: "0 0 0 1.5px hsl(210, 0%, 0%) inset", + backgroundColor: alpha(gray[900], 0.8), + "&:hover": { + borderColor: brand[300], + }, + "&.Mui-focusVisible": { + borderColor: brand[400], + outline: `3px solid ${alpha(brand[500], 0.5)}`, + outlineOffset: "2px", + }, + }), + }), + }, + }, + MuiInputBase: { + styleOverrides: { + root: { + border: "none", + }, + input: { + "&::placeholder": { + opacity: 0.7, + color: gray[500], + }, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + input: { + padding: 0, + }, + root: ({ theme }) => ({ + padding: "8px 12px", + color: (theme.vars || theme).palette.text.primary, + borderRadius: (theme.vars || theme).shape.borderRadius, + border: `1px solid ${(theme.vars || theme).palette.divider}`, + backgroundColor: (theme.vars || theme).palette.background + .default, + transition: "border 120ms ease-in", + "&:hover": { + borderColor: gray[400], + }, + [`&.${outlinedInputClasses.focused}`]: { + outline: `3px solid ${alpha(brand[500], 0.5)}`, + borderColor: brand[400], + }, + ...theme.applyStyles("dark", { + "&:hover": { + borderColor: gray[500], + }, + }), + variants: [ + { + props: { + size: "small", + }, + style: { + height: "2.25rem", + }, + }, + { + props: { + size: "medium", + }, + style: { + height: "2.5rem", + }, + }, + ], + }), + notchedOutline: { + border: "none", + }, + }, + }, + MuiInputAdornment: { + styleOverrides: { + root: ({ theme }) => ({ + color: (theme.vars || theme).palette.grey[500], + ...theme.applyStyles("dark", { + color: (theme.vars || theme).palette.grey[400], + }), + }), + }, + }, + MuiFormLabel: { + styleOverrides: { + root: ({ theme }) => ({ + typography: theme.typography.caption, + marginBottom: 8, + }), + }, + }, };