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';
@ -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,25 +60,33 @@ 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
ref={drawerRef} // Attach the ref to the Drawer
sx={{ sx={{
width: drawerWidth, width: drawerWidth,
flexShrink: 0, flexShrink: 0,
@ -100,7 +106,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
</DrawerHeader> </DrawerHeader>
<Divider /> <Divider />
<List> <List>
<ListItem key={'Dashboard'} disablePadding component={Link} to={'dashboard'} selected={location.pathname=='/dashboard'}> <ListItem key={'Dashboard'} disablePadding component={Link} to={'dashboard'} selected={location.pathname == '/dashboard'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<PersonIcon /> <PersonIcon />
@ -108,7 +114,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
<ListItemText primary={'Dashboard'} /> <ListItemText primary={'Dashboard'} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem key={'User'} disablePadding component={Link} to={'user'} selected={location.pathname=='/user'}> <ListItem key={'User'} disablePadding component={Link} to={'user'} selected={location.pathname == '/user'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<PersonIcon /> <PersonIcon />
@ -116,7 +122,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
<ListItemText primary={'User'} /> <ListItemText primary={'User'} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem key={'Quotes'} disablePadding component={Link} to={'quotes'} selected={location.pathname=='/quotes'}> <ListItem key={'Quotes'} disablePadding component={Link} to={'quotes'} selected={location.pathname == '/quotes'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<MonetizationOnIcon /> <MonetizationOnIcon />
@ -124,7 +130,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
<ListItemText primary={'Quotes'} /> <ListItemText primary={'Quotes'} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem key={'WorkOrder'} disablePadding component={Link} to={'workOrder'} selected={location.pathname=='/workOrder'}> <ListItem key={'WorkOrder'} disablePadding component={Link} to={'workOrder'} selected={location.pathname == '/workOrder'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<TextSnippetIcon /> <TextSnippetIcon />
@ -132,7 +138,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
<ListItemText primary={'Work Order'} /> <ListItemText primary={'Work Order'} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem key={'Contacts'} disablePadding component={Link} to={'contacts'} selected={location.pathname=='/contacts'}> <ListItem key={'Contacts'} disablePadding component={Link} to={'contacts'} selected={location.pathname == '/contacts'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<TextSnippetIcon /> <TextSnippetIcon />
@ -140,7 +146,7 @@ export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
<ListItemText primary={'Contacts'} /> <ListItemText primary={'Contacts'} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem key={'AddressBook'} disablePadding component={Link} to={'addressBook'} selected={location.pathname=='/addressBook'}> <ListItem key={'AddressBook'} disablePadding component={Link} to={'addressBook'} selected={location.pathname == '/addressBook'}>
<ListItemButton> <ListItemButton>
<ListItemIcon> <ListItemIcon>
<TextSnippetIcon /> <TextSnippetIcon />

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"
@ -111,6 +94,22 @@ 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,7 +11,6 @@ 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";
@ -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);
@ -82,10 +84,10 @@ 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) {
@ -117,265 +118,226 @@ const ItemForm = ({
<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 */}
<div style={{ flex: 1, paddingRight: '0.5rem', display: 'flex', flexDirection: 'column' }}>
{/* Full-width Work Order Title */}
<InputLabel id="work-order-title">Work Order Title:</InputLabel>
<TextField <TextField
id="WOID" id="workerOrderTitle"
label="WO ID:"
variant="outlined" variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="workerOrderNumber"
value={values.workerOrderNumber}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workerOrderNumber", value);
}}
error={!!errors.workerOrderNumber}
helperText={errors.workerOrderNumber}
/>
</div>
<div>
<Select
labelId="work-order-title-select-label"
id="work-order-title-select"
fullWidth 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" margin="normal"
required value={values?.workerOrderTitle || ''}
name="workerOrderTitle" onChange={viewOnly ? null : (event) => handleChangeKV("workerOrderTitle", event.target.value)}
value={values.workerOrderTitle}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("workerOrderTitle", value);
}}
error={!!errors.workerOrderTitle} error={!!errors.workerOrderTitle}
helperText={errors.workerOrderTitle} helperText={errors.workerOrderTitle}
disabled={viewOnly} // Disable if in read-only mode
/> />
</div>
<div> {/* Inputs Layout for WO number, PO#, TT# */}
<InputLabel id="description-label">Description</InputLabel> <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 <TextField
id="Description" id="workerOrderNumber"
label="Description"
variant="outlined" variant="outlined"
style={{ width: '100%' }} fullWidth
margin="normal" value={values?.workerOrderNumber || ''}
required onChange={viewOnly ? null : (event) => handleChangeKV("workerOrderNumber", event.target.value)}
name="description" error={!!errors.workerOrderNumber}
value={values.description} helperText={errors.workerOrderNumber}
onChange={(event) => { disabled={viewOnly} // Disable if in read-only mode
const { value } = event.target;
handleChangeKV("description", value);
}}
error={!!errors.description}
helperText={errors.description}
/> />
</div> </div>
<div>
<InputLabel id="PO-label">PO # :</InputLabel> <div style={{ flex: '1 1 30%', paddingRight: '0.5rem' }}>
<InputLabel id="po-label">PO #:</InputLabel>
<TextField <TextField
id="po" id="po"
label=""
variant="outlined" variant="outlined"
style={{ width: '100%' }} fullWidth
margin="normal" value={values?.po || ''}
required onChange={viewOnly ? null : (event) => handleChangeKV("po", event.target.value)}
name="po"
value={values?.po}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("po", value);
}}
error={!!errors.po} error={!!errors.po}
helperText={errors.po} helperText={errors.po}
disabled={viewOnly} // Disable if in read-only mode
/> />
</div> </div>
<div>
<InputLabel id="TT-label">TT #</InputLabel> <div style={{ flex: '1 1 30%' }}>
<InputLabel id="tt-label">TT #:</InputLabel>
<TextField <TextField
id="tt" id="tt"
label=""
variant="outlined" variant="outlined"
style={{ width: '100%' }} fullWidth
margin="normal" value={values?.tt || ''}
required onChange={viewOnly ? null : (event) => handleChangeKV("tt", event.target.value)}
name="tt"
value={values?.tt}
onChange={(event) => {
const { value } = event.target;
handleChangeKV("tt", value);
}}
error={!!errors.tt} error={!!errors.tt}
helperText={errors.tt} helperText={errors.tt}
disabled={viewOnly} // Disable if in read-only mode
/> />
</div> </div>
<div> </div>
<InputLabel id="Wo-select-date-label">Due Date*</InputLabel>
{/* 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 <TextField
id="DueDate" id="scheduleDate"
label=""
variant="outlined"
style={{ width: '100%' }}
margin="normal"
required
name="dueDate"
type="date" type="date"
defaultValue={ moment(values.formattedDueDate).format("YYYY-MM-DD")} fullWidth
onChange={(event) => { defaultValue={moment(values?.scheduleDate).format("YYYY-MM-DD") || ''}
const { value } = event.target; onChange={viewOnly ? null : (event) => handleChangeKV("scheduleDate", event.target.value)}
handleChangeKV("dueDate", value); disabled={viewOnly} // Disable if in read-only mode
}}
error={!!errors.dueDate}
helperText={errors.dueDate}
/> />
</div> </div>
<div>
<InputLabel id="Wo-select-contact-label">POC*</InputLabel> <div style={{ flex: '1 1 30%' }}>
<Select <InputLabel id="due-date-label">Due Date:</InputLabel>
labelId="Wo-select-contact-label" <TextField
id="wo" id="dueDate"
type="date"
fullWidth fullWidth
value={values.contactId} defaultValue={moment(values?.dueDate).format("YYYY-MM-DD") || ''}
label="" onChange={viewOnly ? null : (event) => handleChangeKV("dueDate", event.target.value)}
onChange={(event) => { disabled={viewOnly} // Disable if in read-only mode
const { value } = event.target; />
handleChangeKV("workOrderContacts", value?.toString()); </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
> >
{contacts.map((s, i) => ( {categories.map((category) => (
<MenuItem value={s.id} key={i}> <MenuItem value={category.id} key={category.id}>
{s.name} {category.name}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</div> </div>
<div>
<InputLabel id="Wo-select-label">Assigned To:*</InputLabel> <div style={{ flex: '1 1 48%' }}>
<InputLabel id="work-order-type-label">Work Order Type:</InputLabel>
<Select <Select
labelId="AssignTo-label"
id="AssignTo"
fullWidth fullWidth
value={values.assignTo} id="workOrderType"
label="" value={values.workOrderType || ''}
onChange={(event) => { onChange={viewOnly ? null : (event) => handleChangeKV("workOrderType", event.target.value)}
const { value } = event.target; disabled={viewOnly} // Disable if in read-only mode
handleChangeKV("assignTo", value);
}}
> >
{users.map((s, i) => ( {workOrderTypes.map((type) => (
<MenuItem value={s.id} key={i}> <MenuItem value={type.value} key={type.value}>
{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} {type.text}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</div> </div>
<div> </div>
<InputLabel id="Wo-select-label">Location:*</InputLabel>
{/* 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 <Select
id="Location"
fullWidth fullWidth
value={values.locationId} value={values.contactId || ''}
label="" onChange={viewOnly ? null : (event) => handleChangeKV("workOrderContacts", event.target.value)}
onChange={(event) => { disabled={viewOnly} // Disable if in read-only mode
const { value } = event.target;
handleChangeKV("locationId", value);
}}
> >
{locations.map((s, i) => ( {contacts.map((contact) => (
<MenuItem value={s.id} key={i}> <MenuItem value={contact.id} key={contact.id}>
{s.name} {contact.name}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</div> </div>
<div> <div style={{ flex: '1 1 48%' }}>
<InputLabel id="Wo-select-label">Categories:*</InputLabel> <InputLabel id="dispatcher-label">Dispatcher:</InputLabel>
<Select <Select
labelId="categories-label"
id="categories"
fullWidth fullWidth
value={values.categoryId} value={values.assignTo || ''}
label="" onChange={viewOnly ? null : (event) => handleChangeKV("assignTo", event.target.value)}
onChange={(event) => { disabled={viewOnly} // Disable if in read-only mode
const { value } = event.target;
handleChangeKV("workOrderCategories", value?.toString());
}}
> >
{categories.map((s, i) => ( {users.map((user) => (
<MenuItem value={s.id} key={i}> <MenuItem value={user.id} key={user.id}>
{s.name} {user.firstName}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</div> </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>
{/* Right Section for Notes, starts at the same level as Work Order Title */}
<div style={{ flex: '1', paddingLeft: '0.5rem', marginTop: '1.25rem' }}> {/* Added margin-top to align with the title */}
<InputLabel id="notes-label">Notes:</InputLabel>
<TextField
id="notes"
variant="outlined"
multiline
rows={10} // Larger textarea
fullWidth
value={values?.notes || ''}
onChange={viewOnly ? null : (event) => handleChangeKV("notes", event.target.value)}
disabled={viewOnly} // Disable if in read-only mode
/>
</div>
</div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose}>Cancel</Button> <Button onClick={handleClose}>Close</Button>
{!viewOnly && (
<SubmitButtonWithLoader <SubmitButtonWithLoader
buttonText={BUTTON_TEXT} buttonText={BUTTON_TEXT}
loading={isFormLoading} loading={isFormLoading}
/> />
)}
</DialogActions> </DialogActions>
</Box> </Box>
</Dialog> </Dialog>

View file

@ -4,13 +4,36 @@ 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({
components: { components: {
@ -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>