This commit is contained in:
npalOmega 2025-05-17 21:51:23 -05:00
parent 24896b8acb
commit 29276f0fb9
11 changed files with 512 additions and 443 deletions

View file

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { useEffect, useRef } from 'react';
import { styled, useTheme } from '@mui/material/styles'; import { styled, useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer'; import Drawer from '@mui/material/Drawer';
@ -18,7 +18,7 @@ import PersonIcon from '@mui/icons-material/Person';
import TextSnippetIcon from '@mui/icons-material/TextSnippet'; import TextSnippetIcon from '@mui/icons-material/TextSnippet';
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
const drawerWidth = 240; const drawerWidth = 240;
@ -44,14 +44,12 @@ const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({
], ],
})); }));
const NavStyles = makeStyles((theme) => ({ const NavStyles = makeStyles((theme) => ({
header: { header: {
borderBottom: "2px solid rgba(0,0,0,0.1)", borderBottom: "2px solid rgba(0,0,0,0.1)",
display: "flex", display: "flex",
}, },
title:{ title: {
fontWeight: "600!important", fontWeight: "600!important",
fontSize: "1.25rem!important", fontSize: "1.25rem!important",
} }
@ -62,93 +60,101 @@ const DrawerHeader = styled('div')(({ theme }) => ({
alignItems: 'center', alignItems: 'center',
padding: theme.spacing(0, 1), padding: theme.spacing(0, 1),
background: 'rgb(246 248 249)', background: 'rgb(246 248 249)',
// necessary for content to be below app bar
...theme.mixins.toolbar, ...theme.mixins.toolbar,
justifyContent: 'flex-end', justifyContent: 'flex-end',
})); }));
export default function PersistentDrawerLeft({ handleDrawerClose, open}) { export default function PersistentDrawerLeft({ handleDrawerClose, open }) {
const location = useLocation(); const location = useLocation();
const theme = useTheme(); const theme = useTheme();
const drawerRef = useRef(null); // Create a ref for the drawer
useEffect(() => { useEffect(() => {
document.addEventListener('mousedown', handleDrawerClose); const handleClickOutside = (event) => {
// Check if the click was outside the drawer
if (drawerRef.current && !drawerRef.current.contains(event.target)) {
handleDrawerClose();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => { return () => {
document.removeEventListener('mousedown', handleDrawerClose); document.removeEventListener('mousedown', handleClickOutside);
}; };
}, []); }, [handleDrawerClose]);
return ( return (
<Drawer <Drawer
sx={{ ref={drawerRef} // Attach the ref to the Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth, width: drawerWidth,
flexShrink: 0, boxSizing: 'border-box',
'& .MuiDrawer-paper': { },
width: drawerWidth, }}
boxSizing: 'border-box', variant="persistent"
}, anchor="left"
}} open={open}
variant="persistent" >
anchor="left" <DrawerHeader>
open={open} <IconButton onClick={handleDrawerClose}>
> {theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
<DrawerHeader> </IconButton>
<IconButton onClick={handleDrawerClose}> </DrawerHeader>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />} <Divider />
</IconButton> <List>
</DrawerHeader> <ListItem key={'Dashboard'} disablePadding component={Link} to={'dashboard'} selected={location.pathname == '/dashboard'}>
<Divider /> <ListItemButton>
<List> <ListItemIcon>
<ListItem key={'Dashboard'} disablePadding component={Link} to={'dashboard'} selected={location.pathname=='/dashboard'}> <PersonIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'Dashboard'} />
<PersonIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'Dashboard'} /> <ListItem key={'User'} disablePadding component={Link} to={'user'} selected={location.pathname == '/user'}>
</ListItemButton> <ListItemButton>
</ListItem> <ListItemIcon>
<ListItem key={'User'} disablePadding component={Link} to={'user'} selected={location.pathname=='/user'}> <PersonIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'User'} />
<PersonIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'User'} /> <ListItem key={'Quotes'} disablePadding component={Link} to={'quotes'} selected={location.pathname == '/quotes'}>
</ListItemButton> <ListItemButton>
</ListItem> <ListItemIcon>
<ListItem key={'Quotes'} disablePadding component={Link} to={'quotes'} selected={location.pathname=='/quotes'}> <MonetizationOnIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'Quotes'} />
<MonetizationOnIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'Quotes'} /> <ListItem key={'WorkOrder'} disablePadding component={Link} to={'workOrder'} selected={location.pathname == '/workOrder'}>
</ListItemButton> <ListItemButton>
</ListItem> <ListItemIcon>
<ListItem key={'WorkOrder'} disablePadding component={Link} to={'workOrder'} selected={location.pathname=='/workOrder'}> <TextSnippetIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'Work Order'} />
<TextSnippetIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'Work Order'} /> <ListItem key={'Contacts'} disablePadding component={Link} to={'contacts'} selected={location.pathname == '/contacts'}>
</ListItemButton> <ListItemButton>
</ListItem> <ListItemIcon>
<ListItem key={'Contacts'} disablePadding component={Link} to={'contacts'} selected={location.pathname=='/contacts'}> <TextSnippetIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'Contacts'} />
<TextSnippetIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'Contacts'} /> <ListItem key={'AddressBook'} disablePadding component={Link} to={'addressBook'} selected={location.pathname == '/addressBook'}>
</ListItemButton> <ListItemButton>
</ListItem> <ListItemIcon>
<ListItem key={'AddressBook'} disablePadding component={Link} to={'addressBook'} selected={location.pathname=='/addressBook'}> <TextSnippetIcon />
<ListItemButton> </ListItemIcon>
<ListItemIcon> <ListItemText primary={'Address Book'} />
<TextSnippetIcon /> </ListItemButton>
</ListItemIcon> </ListItem>
<ListItemText primary={'Address Book'} /> </List>
</ListItemButton> </Drawer>
</ListItem>
</List>
</Drawer>
); );
} }

View file

@ -6,7 +6,6 @@ import ListHeaderStyles from "./ListHeader.Styles";
const ListHeader = ({ handleClickFormModalOpen, isEmpty, text }) => { const ListHeader = ({ handleClickFormModalOpen, isEmpty, text }) => {
const classes = ListHeaderStyles(); const classes = ListHeaderStyles();
return ( return (
!isEmpty && (
<Box component="span" m={1} className={classes.box}> <Box component="span" m={1} className={classes.box}>
<Typography variant="h6" component="h6"> <Typography variant="h6" component="h6">
{text} {text}
@ -20,7 +19,6 @@ const ListHeader = ({ handleClickFormModalOpen, isEmpty, text }) => {
Add Add
</Button> </Button>
</Box> </Box>
)
); );
}; };
export default ListHeader; export default ListHeader;

View file

@ -76,28 +76,11 @@ const ItemForm = ({
<Box component="form" onSubmit={handleSubmit} noValidate> <Box component="form" onSubmit={handleSubmit} noValidate>
<DialogContent> <DialogContent>
<DialogContentText className={classes.title}> <DialogContentText className={classes.title}>
{PRIMARY_TEXT} {PRIMARY_TEXT.replace("Item", "Address")}
</DialogContentText> </DialogContentText>
<TextField
id="Name"
label="Name"
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="Name"
value={values.name}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("name", value);
}}
error={!!errors.FirstName}
helperText={errors.FirstName}
/>
<TextField <TextField
id="Title" id="Title"
label="Title" label="Name"
variant="outlined" variant="outlined"
style={{ width: '100%' }} style={{ width: '100%' }}
margin="normal" margin="normal"
@ -110,7 +93,23 @@ const ItemForm = ({
}} }}
error={!!errors.LastName} error={!!errors.LastName}
helperText={errors.LastName} helperText={errors.LastName}
/> />
<TextField
id="Name"
label="Address"
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="Name"
value={values.name}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("name", value);
}}
error={!!errors.FirstName}
helperText={errors.FirstName}
/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose}>Cancel</Button> <Button onClick={handleClose}>Cancel</Button>

View file

@ -62,7 +62,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick }) => {
} }
}, },
{ {
name: "name", name: "title",
label: "Name", label: "Name",
options: { options: {
display: true, display: true,
@ -71,8 +71,8 @@ const TableItems = ({ items, onEditClick, onDeleteClick }) => {
} }
}, },
{ {
name: "title", name: "name",
label: "Title", label: "Address",
options: { options: {
display: true, display: true,
filter: true, filter: true,

View file

@ -1,7 +1,7 @@
export const initialValues = export const initialValues =
{ {
"workerOrderNumber": "", "workerOrderNumber": "",
"workerOrderTitle": "", "workerOrderTitle": "PLUMBING JANITORIAL SCRUBBER PIT CLEANOUT 13W",
"description": "", "description": "",
"po": "", "po": "",
"tt": "", "tt": "",

View file

@ -86,7 +86,7 @@ const ImageDrawer = ({ item, open, setOpen }) => {
<Typography variant="subtitle1">Upload Images</Typography> <Typography variant="subtitle1">Upload Images</Typography>
<Input <Input
type="file" type="file"
inputProps={{ accept: "image/*", multiple: true }} // Allow multiple files inputProps={{ accept: ".jpg, .jpeg, .png, .pdf, .doc, .docx" }} // Allow multiple files
onChange={handleFileChange} onChange={handleFileChange}
style={{ display: 'none' }} style={{ display: 'none' }}
id="file-input" id="file-input"

View file

@ -11,8 +11,7 @@ import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select"; import Select from "@mui/material/Select";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; import FormStyles from "./Form.Styles";
import FormStyles from "./Form.Styles";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import useForm from "../../../hooks/useForm"; import useForm from "../../../hooks/useForm";
@ -21,6 +20,7 @@ import SubmitButtonWithLoader from "../../../components/SubmitButtonWithLoader";
import { form_text_builder, API_SUCCESS_MESSAGE } from "../../../constants"; import { form_text_builder, API_SUCCESS_MESSAGE } from "../../../constants";
import { getContacts, getCategories, getLocations, getUsers } from '../../contacts/list/api'; import { getContacts, getCategories, getLocations, getUsers } from '../../contacts/list/api';
import moment from 'moment'; import moment from 'moment';
const ItemForm = ({ const ItemForm = ({
item, item,
mode, mode,
@ -28,6 +28,7 @@ const ItemForm = ({
setOpen, setOpen,
apiCall, apiCall,
setApiStatus, setApiStatus,
viewOnly = false // New prop added here
}) => { }) => {
const [isFormLoading, setIsFormLoading] = useState(false); const [isFormLoading, setIsFormLoading] = useState(false);
const [workOrders, setWorkOrders] = useState([]); const [workOrders, setWorkOrders] = useState([]);
@ -58,7 +59,8 @@ const ItemForm = ({
const [isFormSubmit, setIsFormSubmit] = useState(false); const [isFormSubmit, setIsFormSubmit] = useState(false);
const classes = FormStyles(); const classes = FormStyles();
const { PRIMARY_TEXT, SECONDARY_TEXT, BUTTON_TEXT } = form_text_builder(mode); const { PRIMARY_TEXT, SECONDARY_TEXT, BUTTON_TEXT } = form_text_builder(mode);
const { values, errors, handleChangeKV, handleSubmit, setValues } = useForm({
const { values, errors, handleChangeKV, handleSubmit } = useForm({
initialValues: item, initialValues: item,
onSubmit(values) { onSubmit(values) {
setIsFormLoading(true); setIsFormLoading(true);
@ -81,11 +83,11 @@ const ItemForm = ({
}); });
}, },
validate: (formValues) => { validate: (formValues) => {
if (isFormSubmit) setIsFormSubmit(false); if (isFormSubmit) setIsFormSubmit(false);
return formValidation(formValues); return formValidation(formValues);
}, },
}); });
const handleClose = () => { const handleClose = () => {
setOpen({ open: false }); setOpen({ open: false });
}; };
@ -95,7 +97,6 @@ const ItemForm = ({
getContacts().then((response) => { handleSuccess(response, setContacts) }); getContacts().then((response) => { handleSuccess(response, setContacts) });
getLocations().then((response) => { handleSuccess(response, setLocations) }); getLocations().then((response) => { handleSuccess(response, setLocations) });
getCategories().then((response) => { handleSuccess(response, setCategories) }); getCategories().then((response) => { handleSuccess(response, setCategories) });
}, []); }, []);
function handleSuccess(response, cb) { function handleSuccess(response, cb) {
@ -113,269 +114,230 @@ const ItemForm = ({
</IconButton> </IconButton>
</DialogTitle> </DialogTitle>
<Box component="form" onSubmit={handleSubmit} noValidate> <Box component="form" onSubmit={handleSubmit} noValidate>
<DialogContent> <DialogContent>
<DialogContentText className={classes.title}> <DialogContentText className={classes.title}>
{PRIMARY_TEXT} {PRIMARY_TEXT}
</DialogContentText> </DialogContentText>
<div> <div style={{ display: 'flex', alignItems: 'flex-start' }}> {/* Changed alignItems to flex-start */}
<InputLabel id="PO-label">WO ID:</InputLabel> {/* Left Section */}
<TextField <div style={{ flex: 1, paddingRight: '0.5rem', display: 'flex', flexDirection: 'column' }}>
id="WOID" {/* Full-width Work Order Title */}
label="WO ID:" <InputLabel id="work-order-title">Work Order Title:</InputLabel>
variant="outlined" <TextField
style={{ width: '100%' }} id="workerOrderTitle"
margin="normal" variant="outlined"
required fullWidth
name="workerOrderNumber" margin="normal"
value={values.workerOrderNumber} value={values?.workerOrderTitle || ''}
onChange={(event) => { onChange={viewOnly ? null : (event) => handleChangeKV("workerOrderTitle", event.target.value)}
const { value } = event.target; error={!!errors.workerOrderTitle}
handleChangeKV("workerOrderNumber", value); helperText={errors.workerOrderTitle}
}} disabled={viewOnly} // Disable if in read-only mode
error={!!errors.workerOrderNumber} />
helperText={errors.workerOrderNumber}
/> {/* Inputs Layout for WO number, PO#, TT# */}
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between' }}>
<div style={{ flex: '1 1 30%', paddingRight: '0.5rem' }}>
<InputLabel id="wo-number-label">WO Number:</InputLabel>
<TextField
id="workerOrderNumber"
variant="outlined"
fullWidth
value={values?.workerOrderNumber || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("workerOrderNumber", event.target.value)}
error={!!errors.workerOrderNumber}
helperText={errors.workerOrderNumber}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
<div style={{ flex: '1 1 30%', paddingRight: '0.5rem' }}>
<InputLabel id="po-label">PO #:</InputLabel>
<TextField
id="po"
variant="outlined"
fullWidth
value={values?.po || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("po", event.target.value)}
error={!!errors.po}
helperText={errors.po}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
<div style={{ flex: '1 1 30%' }}>
<InputLabel id="tt-label">TT #:</InputLabel>
<TextField
id="tt"
variant="outlined"
fullWidth
value={values?.tt || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("tt", event.target.value)}
error={!!errors.tt}
helperText={errors.tt}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
</div>
{/* Inputs Layout for Priority, Schedule Date, Due Date */}
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between' }}>
<div style={{ flex: '1 1 30%', paddingRight: '0.5rem' }}>
<InputLabel id="priority-label">Priority:</InputLabel>
<Select
fullWidth
id="priority"
value={values?.priority || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("priority", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{priorities.map((priority, index) => (
<MenuItem value={priority.value} key={index}>
{priority.text}
</MenuItem>
))}
</Select>
</div>
<div style={{ flex: '1 1 30%', paddingRight: '0.5rem' }}>
<InputLabel id="schedule-date-label">Schedule Date:</InputLabel>
<TextField
id="scheduleDate"
type="date"
fullWidth
defaultValue={moment(values?.scheduleDate).format("YYYY-MM-DD") || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("scheduleDate", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
<div style={{ flex: '1 1 30%' }}>
<InputLabel id="due-date-label">Due Date:</InputLabel>
<TextField
id="dueDate"
type="date"
fullWidth
defaultValue={moment(values?.dueDate).format("YYYY-MM-DD") || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("dueDate", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
</div>
{/* Inputs Layout for Categories and Work Order Type */}
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between' }}>
<div style={{ flex: '1 1 48%', paddingRight: '0.5rem' }}>
<InputLabel id="categories-label">Categories:</InputLabel>
<Select
fullWidth
id="categories"
value={values.categoryId || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("workOrderCategories", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{categories.map((category) => (
<MenuItem value={category.id} key={category.id}>
{category.name}
</MenuItem>
))}
</Select>
</div>
<div style={{ flex: '1 1 48%' }}>
<InputLabel id="work-order-type-label">Work Order Type:</InputLabel>
<Select
fullWidth
id="workOrderType"
value={values.workOrderType || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("workOrderType", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{workOrderTypes.map((type) => (
<MenuItem value={type.value} key={type.value}>
{type.text}
</MenuItem>
))}
</Select>
</div>
</div>
{/* Contact and Dispatcher */}
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between' }}>
<div style={{ flex: '1 1 48%', paddingRight: '0.5rem' }}>
<InputLabel id="site-contact-label">Site Contact:</InputLabel>
<Select
fullWidth
value={values.contactId || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("workOrderContacts", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{contacts.map((contact) => (
<MenuItem value={contact.id} key={contact.id}>
{contact.name}
</MenuItem>
))}
</Select>
</div>
<div style={{ flex: '1 1 48%' }}>
<InputLabel id="dispatcher-label">Dispatcher:</InputLabel>
<Select
fullWidth
value={values.assignTo || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("assignTo", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{users.map((user) => (
<MenuItem value={user.id} key={user.id}>
{user.firstName}
</MenuItem>
))}
</Select>
</div>
</div>
{/* Location */}
<div style={{ flex: '1 1 100%', paddingRight: '0.5rem' }}>
<InputLabel id="location-label">Location:</InputLabel>
<Select
fullWidth
value={values.locationId || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("locationId", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
>
{locations.map((location) => (
<MenuItem value={location.id} key={location.id}>
{location.name}
</MenuItem>
))}
</Select>
</div>
</div> </div>
<div>
<Select
labelId="work-order-title-select-label"
id="work-order-title-select"
fullWidth
value={values.workOrderTitle || ''}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workerOrderTitle", value);
}}
label="Work Order Title"
>
{workOrderTitles.map((title, index) => (
<MenuItem value={title} key={index}>
{title}
</MenuItem>
))}
</Select>
<InputLabel id="PO-label">Work Order Title:</InputLabel>
<TextField
id="WorkerOrderTitle"
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="workerOrderTitle"
value={values.workerOrderTitle}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workerOrderTitle", value);
}}
error={!!errors.workerOrderTitle}
helperText={errors.workerOrderTitle}
/>
</div>
<div>
<InputLabel id="description-label">Description</InputLabel>
<TextField
id="Description"
label="Description"
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="description"
value={values.description}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("description", value);
}}
error={!!errors.description}
helperText={errors.description}
/>
</div>
<div>
<InputLabel id="PO-label">PO # :</InputLabel>
<TextField
id="po"
label=""
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="po"
value={values?.po}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("po", value);
}}
error={!!errors.po}
helperText={errors.po}
/>
</div>
<div>
<InputLabel id="TT-label">TT #</InputLabel>
<TextField
id="tt"
label=""
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="tt"
value={values?.tt}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("tt", value);
}}
error={!!errors.tt}
helperText={errors.tt}
/>
</div>
<div>
<InputLabel id="Wo-select-date-label">Due Date*</InputLabel>
<TextField
id="DueDate"
label=""
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="dueDate"
type="date"
defaultValue={ moment(values.formattedDueDate).format("YYYY-MM-DD")}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("dueDate", value);
}}
error={!!errors.dueDate}
helperText={errors.dueDate}
/>
</div>
<div>
<InputLabel id="Wo-select-contact-label">POC*</InputLabel>
<Select
labelId="Wo-select-contact-label"
id="wo"
fullWidth
value={values.contactId}
label=""
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workOrderContacts", value?.toString());
}}
>
{contacts.map((s, i) => (
<MenuItem value={s.id} key={i}>
{s.name}
</MenuItem>
))}
</Select>
</div>
<div>
<InputLabel id="Wo-select-label">Assigned To:*</InputLabel>
<Select
labelId="AssignTo-label"
id="AssignTo"
fullWidth
value={values.assignTo}
label=""
onChange={(event) => {
const { value } = event.target;
handleChangeKV("assignTo", value);
}}
>
{users.map((s, i) => (
<MenuItem value={s.id} key={i}>
{s.firstName}
</MenuItem>
))}
</Select>
</div>
<div>
<InputLabel id="Wo-select-label">Priority:*</InputLabel>
<Select
labelId="Priority-label"
id="Priority"
fullWidth
value={values.priority}
label=""
onChange={(event) => {
const { value } = event.target;
handleChangeKV("priority", value);
}}
>
{priorities.map((s, i) => (
<MenuItem value={s.value} key={i}>
{s.text}
</MenuItem>
))}
</Select>
</div>
<div>
<InputLabel id="work-order-type-select-label">Work Order Type:*</InputLabel>
<Select
labelId="work-order-type-select-label"
id="work-order-type-select"
value={values.workOrderType}
fullWidth
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workOrderType", value);
}}
label="Work Order Type"
>
{workOrderTypes.map((type, index) => (
<MenuItem value={type.value} key={index}>
{type.text}
</MenuItem>
))}
</Select>
</div>
<div>
<InputLabel id="Wo-select-label">Location:*</InputLabel>
<Select
id="Location"
fullWidth
value={values.locationId}
label=""
onChange={(event) => {
const { value } = event.target;
handleChangeKV("locationId", value);
}}
>
{locations.map((s, i) => (
<MenuItem value={s.id} key={i}>
{s.name}
</MenuItem>
))}
</Select>
</div>
<div> {/* Right Section for Notes, starts at the same level as Work Order Title */}
<InputLabel id="Wo-select-label">Categories:*</InputLabel> <div style={{ flex: '1', paddingLeft: '0.5rem', marginTop: '1.25rem' }}> {/* Added margin-top to align with the title */}
<Select <InputLabel id="notes-label">Notes:</InputLabel>
labelId="categories-label" <TextField
id="categories" id="notes"
fullWidth variant="outlined"
value={values.categoryId} multiline
label="" rows={10} // Larger textarea
onChange={(event) => { fullWidth
const { value } = event.target; value={values?.notes || ''}
handleChangeKV("workOrderCategories", value?.toString()); onChange={viewOnly ? null : (event) => handleChangeKV("notes", event.target.value)}
}} disabled={viewOnly} // Disable if in read-only mode
> />
{categories.map((s, i) => ( </div>
<MenuItem value={s.id} key={i}>
{s.name}
</MenuItem>
))}
</Select>
</div> </div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose}>Cancel</Button> <Button onClick={handleClose}>Close</Button>
<SubmitButtonWithLoader {!viewOnly && (
buttonText={BUTTON_TEXT} <SubmitButtonWithLoader
loading={isFormLoading} buttonText={BUTTON_TEXT}
/> loading={isFormLoading}
/>
)}
</DialogActions> </DialogActions>
</Box> </Box>
</Dialog> </Dialog>
@ -384,4 +346,4 @@ const ItemForm = ({
); );
}; };
export default ItemForm; export default ItemForm;

View file

@ -4,12 +4,35 @@ import IconButton from "@mui/material/IconButton";
import EditOutlined from "@mui/icons-material/EditOutlined"; import EditOutlined from "@mui/icons-material/EditOutlined";
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import SpeakerNotes from '@mui/icons-material/SpeakerNotes'; import SpeakerNotes from '@mui/icons-material/SpeakerNotes';
import PermMediaIcon from '@mui/icons-material/PermMedia';
import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import TableStyles from './TableStyles'; import TableStyles from './TableStyles';
import { createTheme, ThemeProvider } from '@mui/material/styles'; import { createTheme, ThemeProvider } from '@mui/material/styles';
import PermMediaIcon from '@mui/icons-material/PermMedia'; import { post } from '../../../apiUtil';
import { API_URL } from '../../../constants';
const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpenAttachmentsClick }) => { export const EditStatus = (item) => post(`${API_URL}/WorkOrder/EditStatus`, item);
export const EditAvettaTask = (item) => post(`${API_URL}/WorkOrder/EditAvettaTask`, item);
const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpenAttachmentsClick, onRowClickViewOnly }) => {
const classes = TableStyles(); const classes = TableStyles();
const statusOptions = [
"Open",
"Pending",
"Assigned",
"In Progress",
"On Hold",
"Cancelled",
"Completed"
];
const completionOptions = [
"Yes, Complete",
"Yes, Incomplete",
"No" // Default option
];
const getMuiTheme = () => { const getMuiTheme = () => {
return createTheme({ return createTheme({
@ -19,49 +42,61 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
root: { root: {
backgroundColor: '#98acbf', backgroundColor: '#98acbf',
color: '#fff', color: '#fff',
} cursor: 'col-resize', // Indicate column can be resized
}, },
},
MUIDataTableSelectCell: {
styleOverrides: {
headerCell: {
backgroundColor: '#98acbf',
color: '#fff',
}
}
},
MUIDataTableBodyRow: {
styleOverrides: {
root: {
'&:nth-of-type(odd)': {
backgroundColor: 'rgba(0, 0, 0, 0.02)'
}
}
}, },
}, },
MUIDataTableBodyCell: { MUIDataTableBodyCell: {
styleOverrides: { styleOverrides: {
root: { root: {
padding: 6, padding: 6,
wordBreak: 'break-all' wordBreak: 'break-all',
} },
},
},
MUIDataTableBodyRow: {
styleOverrides: {
root: {
cursor: 'pointer', // Change cursor to pointer for clickable rows
},
}, },
}, },
}, },
}) });
} };
const updateStatusOnServer = async (id, status) => {
try {
await EditStatus({ id, status });
} catch (error) {
console.error('Error updating status:', error);
}
};
const updateTaskOnServer = async (id, avettaTask) => {
try {
await EditAvettaTask({ id, avettaTask });
} catch (error) {
console.error('Error updating Avetta Task:', error);
}
};
const handleRowClick = (rowData, rowMeta) => {
const index = rowMeta.dataIndex;
const selectedItem = items[index]; // Access the original items array
onRowClickViewOnly({ open: true, selectedItem, viewOnly:true }); // Pass the selected item
};
const columns = [ const columns = [
{ {
name: "id", name: "id",
label: "id", label: "ID",
options: { options: {
display: false, display: false,
filter: false, filter: false,
sort: false, sort: false,
viewColumns:false, viewColumns: false,
} },
}, },
{ {
name: "workerOrderNumber", name: "workerOrderNumber",
@ -70,7 +105,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: false, filter: false,
sort: true, sort: true,
} },
}, },
{ {
name: "workerOrderTitle", name: "workerOrderTitle",
@ -79,7 +114,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: false, filter: false,
sort: true, sort: true,
} },
}, },
{ {
name: "assignToUserName", name: "assignToUserName",
@ -88,7 +123,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: true, filter: true,
sort: false, sort: false,
} },
}, },
{ {
name: "priority", name: "priority",
@ -97,7 +132,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: true, filter: true,
sort: false, sort: false,
} },
}, },
{ {
name: "status", name: "status",
@ -106,7 +141,58 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: true, filter: true,
sort: false, sort: false,
} customBodyRender: (value, tableMeta, updateValue) => {
return (
<Select
labelId="status-select-label"
id="status-select"
value={value || 'Open'}
onChange={async (event) => {
const newStatus = event.target.value;
const rowId = tableMeta.rowData[0];
await updateStatusOnServer(rowId, newStatus);
updateValue(newStatus);
}}
>
{statusOptions.map((status) => (
<MenuItem value={status} key={status}>
{status}
</MenuItem>
))}
</Select>
);
},
},
},
{
name: "avettaTask",
label: "Avetta Task", // New column
options: {
display: true,
filter: true,
sort: false,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Select
labelId="avetta-task-select-label"
id="avetta-task-select"
value={value || 'No'} // Default to 'No'
onChange={async (event) => {
const newAvettaTask = event.target.value;
const rowId = tableMeta.rowData[0];
await updateTaskOnServer(rowId, newAvettaTask); // Update server with new task status
updateValue(newAvettaTask);
}}
>
{completionOptions.map((completion) => (
<MenuItem value={completion} key={completion}>
{completion}
</MenuItem>
))}
</Select>
);
},
},
}, },
{ {
name: "formattedDueDate", name: "formattedDueDate",
@ -115,7 +201,16 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
display: true, display: true,
filter: true, filter: true,
sort: false, sort: false,
} },
},
{
name: "formattedAssignDate",
label: "AssignDate",
options: {
display: true,
filter: true,
sort: false,
},
}, },
{ {
name: "", name: "",
@ -124,7 +219,7 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
filter: false, filter: false,
sort: false, sort: false,
empty: true, empty: true,
customBodyRender: (value, tableMeta, updateValue) => { customBodyRender: (value, tableMeta) => {
return ( return (
<> <>
<IconButton <IconButton
@ -133,41 +228,25 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
className={classes.IconButton} className={classes.IconButton}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const id = tableMeta.rowData[0]; const rowIndex = tableMeta.rowIndex; // Get the index of the row
const _selectedItem = tableMeta.tableData const selectedItem = items[rowIndex]; // Access the original items array
.find(obj => obj.id == id); onEditClick({ open: true, selectedItem }); // Pass the selected item
onEditClick({ open: true, selectedItem: _selectedItem });
}} }}
> >
<EditOutlined /> <EditOutlined />
</IconButton> </IconButton>
<IconButton
edge="end"
aria-label="delete"
className={classes.IconButton}
onClick={(e) => {
e.stopPropagation();
const id = tableMeta.rowData[0];
const _selectedItem = tableMeta.tableData
.find(obj => obj.id == id);
onDeleteClick({ open: true, selectedItem: _selectedItem });
}}
>
<DeleteIcon />
</IconButton>
<IconButton <IconButton
edge="end" edge="end"
aria-label="history" aria-label="history"
className={classes.IconButton} className={classes.IconButton}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const id = tableMeta.rowData[0]; const rowIndex = tableMeta.rowIndex; // Get the index of the row
const _selectedItem = tableMeta.tableData const selectedItem = items[rowIndex]; // Access the original items array
.find(obj => obj.id == id); onOpenNoteClick({ open: true, selectedItem }); // Pass the selected item
onOpenNoteClick({ open: true, selectedItem: _selectedItem });
}} }}
> >
<SpeakerNotes/> <SpeakerNotes />
</IconButton> </IconButton>
<IconButton <IconButton
edge="end" edge="end"
@ -175,13 +254,25 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
className={classes.IconButton} className={classes.IconButton}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const id = tableMeta.rowData[0]; const rowIndex = tableMeta.rowIndex; // Get the index of the row
const _selectedItem = tableMeta.tableData const selectedItem = items[rowIndex]; // Access the original items array
.find(obj => obj.id == id); onOpenAttachmentsClick({ open: true, selectedItem }); // Pass the selected item
onOpenAttachmentsClick({ open: true, selectedItem: _selectedItem });
}} }}
> >
<PermMediaIcon/> <PermMediaIcon />
</IconButton>
<IconButton
edge="end"
aria-label="delete"
className={classes.IconButton}
onClick={(e) => {
e.stopPropagation();
const rowIndex = tableMeta.rowIndex; // Get the index of the row
const selectedItem = items[rowIndex]; // Access the original items array
onDeleteClick({ open: true, selectedItem }); // Pass the selected item
}}
>
<DeleteIcon />
</IconButton> </IconButton>
</> </>
); );
@ -190,16 +281,13 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
} }
]; ];
const handleRowClick = (rowData, rowMeta) => {
console.log(rowData, rowMeta);
};
const options = { const options = {
filterType: "checkbox", filterType: "checkbox",
rowsPerPage: 50, rowsPerPage: 50,
rowsPerPageOptions: [50, 100, 200], rowsPerPageOptions: [50, 100, 200],
jumpToPage: true, jumpToPage: true,
onRowClick: handleRowClick, onRowClick: handleRowClick,
resizableColumns: true, // Enables column resizing
}; };
return ( return (
@ -212,6 +300,6 @@ const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpen
/> />
</ThemeProvider> </ThemeProvider>
); );
} };
export default React.memo(TableItems); export default React.memo(TableItems);

View file

@ -3,7 +3,7 @@ import Form from "../components/Form";
import { EDIT } from "../../../constants"; import { EDIT } from "../../../constants";
import { editItems } from "./api"; import { editItems } from "./api";
const EditItemModal = ({ item, open, setOpen, isEdit, setApiStatus }) => { const EditItemModal = ({ item, open, setOpen, isEdit, setApiStatus, viewOnly = false }) => {
return ( return (
isEdit && open && ( isEdit && open && (
<Form <Form
@ -13,6 +13,7 @@ const EditItemModal = ({ item, open, setOpen, isEdit, setApiStatus }) => {
mode={EDIT} mode={EDIT}
apiCall={editItems} apiCall={editItems}
setApiStatus={setApiStatus} setApiStatus={setApiStatus}
viewOnly = {viewOnly}
/> />
) )
); );

View file

@ -23,6 +23,11 @@ const List = () => {
selectedItem: {}, selectedItem: {},
open: false, open: false,
}); });
const [viewOnlyopen, setViewOnlyopen] = React.useState({
selectedItem: {},
open: false,
viewOnly:false,
});
const [noteHistoryModalopen, setNoteHistoryModalopen] = React.useState({ const [noteHistoryModalopen, setNoteHistoryModalopen] = React.useState({
selectedItem: {}, selectedItem: {},
open: false, open: false,
@ -80,6 +85,7 @@ const List = () => {
onOpenNoteClick = {setNoteHistoryModalopen} onOpenNoteClick = {setNoteHistoryModalopen}
onOpenAttachmentsClick = {setAttachmentsModalopen} onOpenAttachmentsClick = {setAttachmentsModalopen}
isEmpty={isEmptyList} isEmpty={isEmptyList}
onRowClickViewOnly = {setViewOnlyopen}
/> />
<AddModal <AddModal
open={formModalopen.open} open={formModalopen.open}
@ -88,12 +94,22 @@ const List = () => {
setApiStatus={setApiStatus} setApiStatus={setApiStatus}
/> />
<EditModal <EditModal
key={"edit"}
item={{ ...formModalopen.selectedItem }} item={{ ...formModalopen.selectedItem }}
open={formModalopen.open} open={formModalopen.open}
setOpen={setFormModalopen} setOpen={setFormModalopen}
isEdit={isEdit} isEdit={isEdit}
setApiStatus={setApiStatus} setApiStatus={setApiStatus}
/> />
<EditModal
key={"view"}
item={{ ...viewOnlyopen.selectedItem }}
open={viewOnlyopen.viewOnly}
setOpen={() => setViewOnlyopen({viewOnly:false})}
isEdit={viewOnlyopen.viewOnly}
setApiStatus={setApiStatus}
viewOnly={viewOnlyopen.viewOnly}
/>
<NoteHistoryDrawer <NoteHistoryDrawer
item={{ ...noteHistoryModalopen.selectedItem }} item={{ ...noteHistoryModalopen.selectedItem }}
open={noteHistoryModalopen.open} open={noteHistoryModalopen.open}

View file

@ -17,14 +17,13 @@ const NoteHistoryDrawer = ({ item, open, setOpen, setApiStatus }) => {
const [note, setNote] = useState(''); const [note, setNote] = useState('');
const [noteHistory, setNoteHistory] = useState([]); const [noteHistory, setNoteHistory] = useState([]);
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [loaded, setLoaded] = useState(false);
const handleSubmit = async () => { const handleSubmit = async () => {
if (note.trim()) { if (note.trim()) {
await addComments({id: item.id , Comments: [...noteHistory, note]}); await addComments({id: item.id , Comments: [...noteHistory, note]});
setNoteHistory([]); setLoaded(true);
setNote('');
setOpen(false);
} }
}; };
@ -42,7 +41,7 @@ const NoteHistoryDrawer = ({ item, open, setOpen, setApiStatus }) => {
setComments(_comments); setComments(_comments);
} }
} }
}, [item]) }, [item,loaded])
return ( return (
<div> <div>