Vehicle API integeration and Add viewVehicleModal

This commit is contained in:
jaanvi 2025-03-07 18:17:24 +05:30
parent e0cb35e783
commit 1d67f85f22
13 changed files with 1376 additions and 655 deletions

View file

@ -185,44 +185,51 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
}}
render={({ field }) => (
<>
<Box sx={{position:"relative" }}>
<TextField
{...field}
required
margin="dense"
label="Password"
type={showPassword ? "text" : "password"}
id="password"
autoComplete="current-password"
autoFocus
fullWidth
variant="standard"
error={!!errors.password}
helperText={errors.password?.message}
/>
<IconButton
<Box
sx={{
position: "absolute",
top: "60%",
right: "10px",
background: "none",
borderColor: "transparent",
transform: "translateY(-50%)",
"&:hover": {
backgroundColor: "transparent",
borderColor: "transparent",
},
display: "flex",
alignItems: "center",
}}
onClick={() =>
setShowPassword((prev) => !prev)
}
>
{showPassword ? (
<VisibilityOff />
) : (
<Visibility />
)}
</IconButton>
<TextField
{...field}
required
margin="dense"
label="Password"
type={
showPassword ? "text" : "password"
}
id="password"
autoComplete="current-password"
autoFocus
fullWidth
variant="standard"
error={!!errors.password}
helperText={errors.password?.message}
/>
<IconButton
sx={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)", // Center vertically
backgroundColor: "transparent", // Remove background color
border: "none", // Remove any border
boxShadow: "none", // Remove any shadow
padding: 0, // Remove padding to ensure it's fully transparent
}}
onClick={() =>
setShowPassword((prev) => !prev)
}
>
{showPassword ? (
<VisibilityOff />
) : (
<Visibility />
)}
</IconButton>
</Box>
</>
)}

View file

@ -0,0 +1,196 @@
import { useState } from "react";
import {
Box,
Button,
Typography,
TextField,
Modal,
IconButton,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
export default function AddVehicleModal({
open,
handleClose,
handleAddVehicle,
}) {
// State for input fields
const [name, setName] = useState("");
const [company, setCompany] = useState("");
const [modelName, setModelName] = useState("");
const [chargeType, setChargeType] = useState("");
const [imageUrl, setImageUrl] = useState("");
const handleSubmit = () => {
if (!name || !company || !modelName || !chargeType || !imageUrl) {
alert("Please fill all fields");
return;
}
const newVehicle = {
name,
company,
modelName,
chargeType,
imageUrl,
};
handleAddVehicle(newVehicle); // Add vehicle to table
handleClose(); // Close modal after adding
};
return (
<Modal
open={open}
onClose={handleClose}
aria-labelledby="add-vehicle-modal"
>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
boxShadow: 24,
p: 3,
borderRadius: 2,
}}
>
{/* Header */}
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography variant="h6" fontWeight={600}>
Add Vehicle
</Typography>
<IconButton onClick={handleClose}>
<CloseIcon />
</IconButton>
</Box>
{/* Horizontal Line */}
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
{/* Input Fields */}
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* First Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Vehicle Name
</Typography>
<TextField
fullWidth
placeholder="Enter Vehicle Name"
size="small"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Company
</Typography>
<TextField
fullWidth
placeholder="Enter Company Name"
size="small"
value={company}
onChange={(e) => setCompany(e.target.value)}
/>
</Box>
</Box>
{/* Second Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Model Name
</Typography>
<TextField
fullWidth
placeholder="Enter Model Name"
size="small"
value={modelName}
onChange={(e) => setModelName(e.target.value)}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Charge Type
</Typography>
<TextField
fullWidth
placeholder="Enter Charge Type"
size="small"
value={chargeType}
onChange={(e) => setChargeType(e.target.value)}
/>
</Box>
</Box>
{/* Third Row - Image URL */}
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Image URL
</Typography>
<TextField
fullWidth
placeholder="Enter Image URL"
size="small"
value={imageUrl}
onChange={(e) => setImageUrl(e.target.value)}
/>
</Box>
</Box>
{/* Submit Button */}
<Box
sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
>
<Button variant="contained" onClick={handleSubmit}>
Add Vehicle
</Button>
</Box>
</Box>
</Modal>
);
}

View file

@ -9,6 +9,8 @@ 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 { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice";
import { useDispatch } from "react-redux";
import {
Box,
@ -24,6 +26,8 @@ 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";
@ -95,6 +99,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
const [currentPage, setCurrentPage] = React.useState(1);
const usersPerPage = 10;
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
@ -114,17 +120,45 @@ const CustomTable: React.FC<CustomTableProps> = ({
return false;
};
const handleDeleteButton = (id: string | undefined) => {
if (!id) console.error("ID not found", id);
dispatch(deleteAdmin(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;
}
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);
};
@ -433,7 +467,7 @@ const filteredRows = rows.filter(
>
View
</Button>
{viewModal && (
{viewModal && tableType === "admin" && (
<ViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
@ -443,6 +477,16 @@ const filteredRows = rows.filter(
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "vehicle" && (
<VehicleViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
<Button
variant="text"
@ -510,3 +554,4 @@ const filteredRows = rows.filter(
};
export default CustomTable;

View file

@ -0,0 +1,300 @@
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";
import { updateVehicle } from "../../redux/slices/VehicleSlice";
interface EditVehicleModalProps {
open: boolean;
handleClose: () => void;
handleUpdate: (
id: string,
name: string,
email: string,
phone: string,
registeredAddress: string,
password: string
) => void;
editRow: any;
}
interface FormData {
name: string;
company: string;
modelName: string;
chargeType: string;
imageUrl: string;
}
const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
open,
handleClose,
handleUpdate,
editRow,
}) => {
const {
control,
handleSubmit,
formState: { errors },
setValue,
reset,
} = useForm<FormData>({
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 (
<Modal
open={open}
onClose={handleClose}
aria-labelledby="edit-vehicle-modal"
>
<Box
component="form"
onSubmit={handleSubmit(onSubmit)}
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
boxShadow: 24,
p: 3,
borderRadius: 2,
}}
>
{/* Header */}
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography variant="h6" fontWeight={600}>
Edit Vehicle
</Typography>
<IconButton onClick={handleClose}>
<CloseIcon />
</IconButton>
</Box>
{/* Horizontal Line */}
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
{/* Input Fields */}
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* First Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography
variant="body2"
fontWeight={500}
mb={0.5}
>
Vehicle Name
</Typography>
<Controller
name="name"
control={control}
rules={{ required: "Vehicle Name is required" }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder="Enter Vehicle Name"
size="small"
error={!!errors.name}
helperText={errors.name?.message}
/>
)}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography
variant="body2"
fontWeight={500}
mb={0.5}
>
Company
</Typography>
<Controller
name="company"
control={control}
rules={{ required: "Company is required" }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder="Enter Company Name"
size="small"
error={!!errors.company}
helperText={errors.company?.message}
/>
)}
/>
</Box>
</Box>
{/* Second Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography
variant="body2"
fontWeight={500}
mb={0.5}
>
Model Name
</Typography>
<Controller
name="modelName"
control={control}
rules={{ required: "Model Name is required" }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder="Enter Model Name"
size="small"
error={!!errors.modelName}
helperText={errors.modelName?.message}
/>
)}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography
variant="body2"
fontWeight={500}
mb={0.5}
>
Charge Type
</Typography>
<Controller
name="chargeType"
control={control}
rules={{ required: "Charge Type is required" }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder="Enter Charge Type"
size="small"
error={!!errors.chargeType}
helperText={errors.chargeType?.message}
/>
)}
/>
</Box>
</Box>
{/* Third Row - Image URL Input */}
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500} mb={0.5}>
Image URL
</Typography>
<Controller
name="imageUrl"
control={control}
rules={{ required: "Image URL is required" }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder="Enter Image URL"
size="small"
error={!!errors.imageUrl}
helperText={errors.imageUrl?.message}
/>
)}
/>
</Box>
</Box>
{/* Submit Button */}
<Box
sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
>
<Button variant="contained" type="submit">
Update Vehicle
</Button>
</Box>
</Box>
</Modal>
);
};
export default EditVehicleModal;

View file

@ -0,0 +1,128 @@
import React, { useEffect, useState } from "react";
import { Box, Modal, Typography, Divider } 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<any>(null);
useEffect(() => {
if (id) {
const vehicle = vehicles.find((vehicle) => vehicle.id === id);
setSelectedVehicle(vehicle || null);
}
}, [id, vehicles]);
return (
<Modal
open={open}
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<Box sx={style}>
<Typography
id="modal-title"
variant="h5"
fontWeight="bold"
sx={{ width: "100%" }}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
}}
>
<Box sx={{ flex: 1, textAlign: "center" }}>
{selectedVehicle?.name || "Vehicle"}'s Details
</Box>
<Box
onClick={() => setViewModal(false)}
sx={{
cursor: "pointer",
display: "flex",
alignItems: "center",
}}
>
<CloseIcon />
</Box>
</Box>
</Typography>
<Divider sx={{ width: "100%" }} />
{selectedVehicle ? (
<Box
sx={{
width: "80%",
textAlign: "left",
display: "flex",
flexDirection: "column",
gap: 1.5,
whiteSpace: "pre-wrap",
wordBreak: "break-word",
overflowWrap: "break-word",
}}
>
<Typography variant="body1">
<strong>Name:</strong> {selectedVehicle.name}
</Typography>
<Typography variant="body1">
<strong>Company:</strong> {selectedVehicle.company}
</Typography>
<Typography variant="body1">
<strong>Model Name:</strong>{" "}
{selectedVehicle.modelName}
</Typography>
<Typography variant="body1">
<strong>Charge Type:</strong>{" "}
{selectedVehicle.chargeType}
</Typography>
<Typography variant="body1">
<strong>Image URL:</strong>{" "}
{selectedVehicle.imageUrl}
</Typography>
</Box>
) : (
<Typography align="center">
No vehicle found with this ID
</Typography>
)}
</Box>
</Modal>
);
}

View file

@ -25,7 +25,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
return (
<React.Fragment>
<ExpandMoreIcon onClick={handleClick} />
<ExpandMoreIcon onClick={handleClick} sx={{cursor:"pointer"}}/>
<Menu
anchorEl={anchorEl}
id="top-menu"

View file

@ -1,5 +1,5 @@
import axios from "axios";
// import { useHistory } from "react-router-dom";
const http = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL,
});
@ -11,5 +11,18 @@ http.interceptors.request.use((config) => {
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;

View file

@ -14,44 +14,49 @@ interface ForgotPasswordProps {
export default function ForgotPassword({ open, handleClose }: ForgotPasswordProps) {
return (
<Dialog
open={open}
onClose={handleClose}
PaperProps={{
component: 'form',
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleClose();
},
sx: { backgroundImage: 'none' },
}}
>
<DialogTitle>Reset password</DialogTitle>
<DialogContent
sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '100%' }}
>
<DialogContentText>
Enter your account&apos;s email address, and we&apos;ll send you a link to
reset your password.
</DialogContentText>
<OutlinedInput
autoFocus
required
margin="dense"
id="email"
name="email"
label="Email address"
placeholder="Email address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions sx={{ pb: 3, px: 3 }}>
<Button onClick={handleClose}>Cancel</Button>
<Button variant="contained" type="submit">
Continue
</Button>
</DialogActions>
</Dialog>
<Dialog
open={open}
onClose={handleClose}
PaperProps={{
component: "form",
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleClose();
},
sx: { backgroundImage: "none" },
}}
>
<DialogTitle>Reset password</DialogTitle>
<DialogContent
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
width: "100%",
}}
>
<DialogContentText>
Enter your account&apos;s email address, and we&apos;ll send
you a link to reset your password.
</DialogContentText>
<OutlinedInput
autoFocus
required
margin="dense"
id="email"
name="email"
label="Email address"
placeholder="Email address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions sx={{ pb: 3, px: 3 }}>
<Button onClick={handleClose}>Cancel</Button>
<Button variant="contained" type="submit">
Continue
</Button>
</DialogActions>
</Dialog>
);
}

View file

@ -47,15 +47,15 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
};
const onSubmit: SubmitHandler<ILoginForm> = 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 (
<AppTheme {...props}>

View file

@ -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<boolean>(false);
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
const [editRow, setEditRow] = useState<any>(null);
const { reset } = useForm();
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
const [viewModal, setViewModal] = React.useState<boolean>(false);
const [rowData, setRowData] = React.useState<any | null>(null);
const [deleteModal, setDeleteModal] = useState<boolean>(false);
const [viewModal, setViewModal] = useState<boolean>(false);
const [rowData, setRowData] = useState<any | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch<AppDispatch>();
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 (
<>
<CustomTable
columns={categoryColumns}
rows={categoryRows}
@ -161,20 +147,22 @@ export default function VehicleList() {
setViewModal={setViewModal}
viewModal={viewModal}
setRowData={setRowData}
setModalOpen={setModalOpen}
setModalOpen={() => setEditModalOpen(true)}
tableType="vehicle"
handleClickOpen={handleClickOpen}
/>
{/* <AddEditCategoryModal
open={modalOpen}
handleClose={handleCloseModal}
editRow={rowData}
/>
<DeleteModal
open={deleteModal}
setDeleteModal={setDeleteModal}
handleDelete={handleDelete}
/> */}
<AddVehicleModal
open={addModalOpen}
handleClose={handleCloseModal}
handleAddVehicle={handleAddVehicle}
/>
<EditVehicleModal
open={editModalOpen}
handleClose={handleCloseModal}
handleUpdate={handleUpdate}
editRow={rowData}
/>
</>
);
}

View file

@ -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<Vehicle, void, { rejectValue: string }>(
"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;
});
},
});

View file

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

View file

@ -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<Theme> = {
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: (
<CheckBoxOutlineBlankRoundedIcon sx={{ color: 'hsla(210, 0%, 0%, 0.0)' }} />
),
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
indeterminateIcon: <RemoveRoundedIcon sx={{ height: 14, width: 14 }} />,
},
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: (
<CheckBoxOutlineBlankRoundedIcon
sx={{ color: "hsla(210, 0%, 0%, 0.0)" }}
/>
),
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
indeterminateIcon: (
<RemoveRoundedIcon sx={{ height: 14, width: 14 }} />
),
},
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,
}),
},
},
};