note history, work order images
This commit is contained in:
parent
3f3dea2a5f
commit
24896b8acb
12 changed files with 430 additions and 19 deletions
|
|
@ -9,6 +9,13 @@ const STANDARD_HEADERS = {
|
||||||
Authorization: getAccessToken()
|
Authorization: getAccessToken()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STANDARD_HEADERS_FORM = {
|
||||||
|
accept: "application/json",
|
||||||
|
pragma: "no-cache",
|
||||||
|
"cache-control": "no-cache",
|
||||||
|
Authorization: getAccessToken(),
|
||||||
|
};
|
||||||
|
|
||||||
export const get = (url) =>
|
export const get = (url) =>
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
headers: { ...STANDARD_HEADERS, Authorization: getAccessToken() },
|
headers: { ...STANDARD_HEADERS, Authorization: getAccessToken() },
|
||||||
|
|
@ -30,6 +37,26 @@ export const post = (url, payload) =>
|
||||||
throw new Error(ex.message || API_ERROR_MESSAGE);
|
throw new Error(ex.message || API_ERROR_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const postFormBody = (url, payload) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(payload)) {
|
||||||
|
formData.append(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...STANDARD_HEADERS_FORM,
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then(handleHttpResponse)
|
||||||
|
.catch((ex) => {
|
||||||
|
throw new Error(ex.message || API_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const put = (url, payload) =>
|
export const put = (url, payload) =>
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
headers: STANDARD_HEADERS,
|
headers: STANDARD_HEADERS,
|
||||||
|
|
@ -41,6 +68,25 @@ export const put = (url, payload) =>
|
||||||
throw new Error(ex.message || API_ERROR_MESSAGE);
|
throw new Error(ex.message || API_ERROR_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const putFormBody = (url, payload) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(payload)) {
|
||||||
|
formData.append(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
...STANDARD_HEADERS_FORM,
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then(handleHttpResponse)
|
||||||
|
.catch((ex) => {
|
||||||
|
throw new Error(ex.message || API_ERROR_MESSAGE);
|
||||||
|
});
|
||||||
|
}
|
||||||
export const deleteItem = (url, payload) =>
|
export const deleteItem = (url, payload) =>
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
headers: STANDARD_HEADERS,
|
headers: STANDARD_HEADERS,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import * as React from 'react';
|
import React, { useEffect } 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';
|
||||||
|
|
@ -70,6 +70,15 @@ const DrawerHeader = styled('div')(({ theme }) => ({
|
||||||
export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
|
export default function PersistentDrawerLeft({ handleDrawerClose, open}) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('mousedown', handleDrawerClose);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleDrawerClose);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
sx={{
|
sx={{
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ export const initialValues =
|
||||||
"po": "",
|
"po": "",
|
||||||
"tt": "",
|
"tt": "",
|
||||||
"assignTo": "",
|
"assignTo": "",
|
||||||
"dueDate": null,
|
|
||||||
"priority": "",
|
"priority": "",
|
||||||
"locationId": null,
|
|
||||||
"istemplate": null,
|
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"beforPhotoIssue": "",
|
"beforPhotoIssue": "",
|
||||||
"beforPhotoAttachment": null,
|
"beforPhotoAttachment": null,
|
||||||
|
|
|
||||||
144
src/pages/workorder/attachments/ImageDrawer.js
Normal file
144
src/pages/workorder/attachments/ImageDrawer.js
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Drawer,
|
||||||
|
Typography,
|
||||||
|
Grid,
|
||||||
|
Paper,
|
||||||
|
IconButton,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
FormHelperText,
|
||||||
|
} from '@mui/material';
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
|
import {getAttachments, AddEditAttachments} from './api';
|
||||||
|
|
||||||
|
const ImageDrawer = ({ item, open, setOpen }) => {
|
||||||
|
const [imagePaths, setImagePaths] = useState([]);
|
||||||
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
||||||
|
const [errors, setErrors] = useState({ file: null });
|
||||||
|
|
||||||
|
const toggleDrawer = (open) => () => {
|
||||||
|
setOpen(open);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle file upload
|
||||||
|
const handleFileChange = (event) => {
|
||||||
|
const files = Array.from(event.target.files);
|
||||||
|
const fileURLs = files.map(file => URL.createObjectURL(file));
|
||||||
|
|
||||||
|
setImagePaths((prev) => [...prev, ...fileURLs]);
|
||||||
|
setSelectedFiles((prev) => [...prev, ...files]);
|
||||||
|
setErrors({ file: null }); // Clear errors
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle delete image
|
||||||
|
const handleDeleteImage = (image, index) => {
|
||||||
|
setImagePaths((prev) => prev.filter((img, idx) => img !== image));
|
||||||
|
setSelectedFiles((prev) => prev.filter((_, idx) => idx !== index)); // Ensure file array is in sync
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getImageName = (file) => {
|
||||||
|
if(!file) return "";
|
||||||
|
const name = file.name;
|
||||||
|
if(name.indexOf('_') > 0) {
|
||||||
|
const parts = name.split('_');
|
||||||
|
return parts[parts.length - 1].split('.')[0];
|
||||||
|
} else {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handUpload = async () => {
|
||||||
|
};
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (selectedFiles) {
|
||||||
|
await AddEditAttachments({id: item.id , attachments: selectedFiles[0], imagePaths:imagePaths});
|
||||||
|
if(item.id) {
|
||||||
|
const attachments = await getAttachments(item.id);
|
||||||
|
if(attachments && attachments.length>0) {
|
||||||
|
const _attachments = attachments.map(c=> c.attachments)
|
||||||
|
setImagePaths(_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(async() => {
|
||||||
|
if(item.id) {
|
||||||
|
const attachments = await getAttachments(item.id);
|
||||||
|
if(attachments && attachments.length>0) {
|
||||||
|
const _attachments = attachments.map(c=> c.attachments)
|
||||||
|
setImagePaths(_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [item])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Drawer anchor="right" open={open} onClose={toggleDrawer(false)}>
|
||||||
|
<div style={{ width: 400, padding: 16 }}>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Workorder Images
|
||||||
|
</Typography>
|
||||||
|
<FormControl fullWidth margin="normal" error={!!errors.file}>
|
||||||
|
<Typography variant="subtitle1">Upload Images</Typography>
|
||||||
|
<Input
|
||||||
|
type="file"
|
||||||
|
inputProps={{ accept: "image/*", multiple: true }} // Allow multiple files
|
||||||
|
onChange={handleFileChange}
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
id="file-input"
|
||||||
|
/>
|
||||||
|
<label htmlFor="file-input">
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component="span"
|
||||||
|
onClick={handUpload}
|
||||||
|
>
|
||||||
|
{selectedFiles.length > 0 ? `${selectedFiles.length} File(s) Selected` : 'Choose Files'}
|
||||||
|
</Button>
|
||||||
|
</label>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
style={{ marginTop: 10 }}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
{errors.file && <FormHelperText>{errors.file}</FormHelperText>}
|
||||||
|
</FormControl>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{imagePaths.map((image, index) => (
|
||||||
|
<Grid item xs={6} key={index}> {/* Set to 6 to allow 2 items per row */}
|
||||||
|
<Paper elevation={2} style={{ padding: '8px' }}>
|
||||||
|
<img
|
||||||
|
src={image}
|
||||||
|
alt={`Image ${index}`}
|
||||||
|
style={{ width: '100%', height: 'auto', borderRadius: '4px' }}
|
||||||
|
/>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', marginTop: '8px' }}>
|
||||||
|
<Typography variant="body2" style={{ flexGrow: 1 }}>
|
||||||
|
{getImageName(selectedFiles[index])} {/* Display the extracted image name */}
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleDeleteImage(image, index)}
|
||||||
|
aria-label="delete"
|
||||||
|
>
|
||||||
|
<DeleteIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageDrawer;
|
||||||
6
src/pages/workorder/attachments/api.js
Normal file
6
src/pages/workorder/attachments/api.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { postFormBody, get } from '../../../apiUtil';
|
||||||
|
import { API_URL } from '../../../constants';
|
||||||
|
|
||||||
|
export const AddEditAttachments = (item) => postFormBody(`${API_URL}/WorkOrder/AddEditAttachments`, item);
|
||||||
|
|
||||||
|
export const getAttachments = (id) => get(`${API_URL}/WorkOrder/GetAttachmentsByWorkorderId?woid=${id}`);
|
||||||
|
|
@ -20,6 +20,7 @@ import { formValidation } from "../_utility";
|
||||||
import SubmitButtonWithLoader from "../../../components/SubmitButtonWithLoader";
|
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';
|
||||||
const ItemForm = ({
|
const ItemForm = ({
|
||||||
item,
|
item,
|
||||||
mode,
|
mode,
|
||||||
|
|
@ -38,6 +39,19 @@ const ItemForm = ({
|
||||||
{ text: "Sev 4", value: "Sev 4" },
|
{ text: "Sev 4", value: "Sev 4" },
|
||||||
{ text: "Sev 5", value: "Sev 5" }
|
{ text: "Sev 5", value: "Sev 5" }
|
||||||
]);
|
]);
|
||||||
|
const [workOrderTypes, setWorkOrderTypes] = useState([
|
||||||
|
{ text: "PM", value: "PM" },
|
||||||
|
{ text: "Reactive", value: "Reactive" },
|
||||||
|
{ text: "Project", value: "Project" },
|
||||||
|
]);
|
||||||
|
const workOrderTitles = [
|
||||||
|
"GENERIC PLUMBING SYSTEMS – ANNUAL",
|
||||||
|
"GENERIC TANK/TANKLESS WATER HEATER ANNUAL",
|
||||||
|
"BACKFLOW PREVENTER ANNUAL PM",
|
||||||
|
"GENERIC PUMPING SYSTEMS - ANNUAL",
|
||||||
|
"ANNUAL PRESSURE REDUCING VALVE INSPECTION",
|
||||||
|
"PLUMBING JANITORIAL SCRUBBER PIT CLEANOUT 13W",
|
||||||
|
];
|
||||||
const [locations, setLocations] = useState([]);
|
const [locations, setLocations] = useState([]);
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [categories, setCategories] = useState([]);
|
const [categories, setCategories] = useState([]);
|
||||||
|
|
@ -75,13 +89,7 @@ const ItemForm = ({
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen({ open: false });
|
setOpen({ open: false });
|
||||||
};
|
};
|
||||||
const handlelineItemIncrements = () => {
|
|
||||||
|
|
||||||
setValues((values) => ({
|
|
||||||
...values,
|
|
||||||
...{lineitems:[...values.lineitems||[], {descriptions:'', lineitem:0}]},
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUsers().then((response) => { handleSuccess(response, setUsers) });
|
getUsers().then((response) => { handleSuccess(response, setUsers) });
|
||||||
getContacts().then((response) => { handleSuccess(response, setContacts) });
|
getContacts().then((response) => { handleSuccess(response, setContacts) });
|
||||||
|
|
@ -127,10 +135,28 @@ const ItemForm = ({
|
||||||
error={!!errors.workerOrderNumber}
|
error={!!errors.workerOrderNumber}
|
||||||
helperText={errors.workerOrderNumber}
|
helperText={errors.workerOrderNumber}
|
||||||
/>
|
/>
|
||||||
|
</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>
|
<InputLabel id="PO-label">Work Order Title:</InputLabel>
|
||||||
<TextField
|
<TextField
|
||||||
id="WorkerOrderTitle"
|
id="WorkerOrderTitle"
|
||||||
label="Work Order Title:"
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
|
|
@ -213,7 +239,7 @@ const ItemForm = ({
|
||||||
required
|
required
|
||||||
name="dueDate"
|
name="dueDate"
|
||||||
type="date"
|
type="date"
|
||||||
value={values.dueDate}
|
defaultValue={ moment(values.formattedDueDate).format("YYYY-MM-DD")}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
handleChangeKV("dueDate", value);
|
handleChangeKV("dueDate", value);
|
||||||
|
|
@ -282,6 +308,26 @@ const ItemForm = ({
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<InputLabel id="Wo-select-label">Location:*</InputLabel>
|
<InputLabel id="Wo-select-label">Location:*</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ import MUIDataTable from "mui-datatables";
|
||||||
import IconButton from "@mui/material/IconButton";
|
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 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';
|
||||||
|
|
||||||
const TableItems = ({ items, onEditClick, onDeleteClick }) => {
|
const TableItems = ({ items, onEditClick, onDeleteClick, onOpenNoteClick, onOpenAttachmentsClick }) => {
|
||||||
const classes = TableStyles();
|
const classes = TableStyles();
|
||||||
|
|
||||||
const getMuiTheme = () => {
|
const getMuiTheme = () => {
|
||||||
|
|
@ -153,6 +155,34 @@ const TableItems = ({ items, onEditClick, onDeleteClick }) => {
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
aria-label="history"
|
||||||
|
className={classes.IconButton}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const id = tableMeta.rowData[0];
|
||||||
|
const _selectedItem = tableMeta.tableData
|
||||||
|
.find(obj => obj.id == id);
|
||||||
|
onOpenNoteClick({ open: true, selectedItem: _selectedItem });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SpeakerNotes/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
aria-label="attachments"
|
||||||
|
className={classes.IconButton}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const id = tableMeta.rowData[0];
|
||||||
|
const _selectedItem = tableMeta.tableData
|
||||||
|
.find(obj => obj.id == id);
|
||||||
|
onOpenAttachmentsClick({ open: true, selectedItem: _selectedItem });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PermMediaIcon/>
|
||||||
|
</IconButton>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { post } from '../../../apiUtil';
|
import { postFormBody } from '../../../apiUtil';
|
||||||
import { API_URL } from '../../../constants';
|
import { API_URL } from '../../../constants';
|
||||||
|
|
||||||
export const editItems = (item) => post(`${API_URL}/WorkOrder/EditWorkorder`, item);
|
export const editItems = (item) => postFormBody(`${API_URL}/WorkOrder/EditWorkorder`, item);
|
||||||
|
|
@ -6,11 +6,13 @@ import Banner from "../../../components/Banner";
|
||||||
import AddModal from "../new";
|
import AddModal from "../new";
|
||||||
import EditModal from "../edit";
|
import EditModal from "../edit";
|
||||||
import DeleteModal from "../delete";
|
import DeleteModal from "../delete";
|
||||||
|
import NoteHistoryDrawer from '../noteHistory/NoteHistoryDrawer'
|
||||||
import useItems from "./useItems";
|
import useItems from "./useItems";
|
||||||
import { CircularProgress, Box } from "@mui/material";
|
import { CircularProgress, Box } from "@mui/material";
|
||||||
import Alert from "@mui/material/Alert";
|
import Alert from "@mui/material/Alert";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import { getUser } from '../../../tokenUtility'
|
import { getUser } from '../../../tokenUtility'
|
||||||
|
import ImageDrawer from '../attachments/ImageDrawer';
|
||||||
|
|
||||||
const List = () => {
|
const List = () => {
|
||||||
const [formModalopen, setFormModalopen] = React.useState({
|
const [formModalopen, setFormModalopen] = React.useState({
|
||||||
|
|
@ -21,6 +23,14 @@ const List = () => {
|
||||||
selectedItem: {},
|
selectedItem: {},
|
||||||
open: false,
|
open: false,
|
||||||
});
|
});
|
||||||
|
const [noteHistoryModalopen, setNoteHistoryModalopen] = React.useState({
|
||||||
|
selectedItem: {},
|
||||||
|
open: false,
|
||||||
|
});
|
||||||
|
const [attachmentsModalopen, setAttachmentsModalopen] = React.useState({
|
||||||
|
selectedItem: {},
|
||||||
|
open: false,
|
||||||
|
});
|
||||||
const [apiStatus, setApiStatus] = React.useState({
|
const [apiStatus, setApiStatus] = React.useState({
|
||||||
show: false,
|
show: false,
|
||||||
status: "",
|
status: "",
|
||||||
|
|
@ -67,6 +77,8 @@ const List = () => {
|
||||||
items={items}
|
items={items}
|
||||||
onEditClick={setFormModalopen}
|
onEditClick={setFormModalopen}
|
||||||
onDeleteClick={setDeleteModalopen}
|
onDeleteClick={setDeleteModalopen}
|
||||||
|
onOpenNoteClick = {setNoteHistoryModalopen}
|
||||||
|
onOpenAttachmentsClick = {setAttachmentsModalopen}
|
||||||
isEmpty={isEmptyList}
|
isEmpty={isEmptyList}
|
||||||
/>
|
/>
|
||||||
<AddModal
|
<AddModal
|
||||||
|
|
@ -76,12 +88,24 @@ const List = () => {
|
||||||
setApiStatus={setApiStatus}
|
setApiStatus={setApiStatus}
|
||||||
/>
|
/>
|
||||||
<EditModal
|
<EditModal
|
||||||
userItem={{ ...formModalopen.selectedItem }}
|
item={{ ...formModalopen.selectedItem }}
|
||||||
open={formModalopen.open}
|
open={formModalopen.open}
|
||||||
setOpen={setFormModalopen}
|
setOpen={setFormModalopen}
|
||||||
isEdit={isEdit}
|
isEdit={isEdit}
|
||||||
setApiStatus={setApiStatus}
|
setApiStatus={setApiStatus}
|
||||||
/>
|
/>
|
||||||
|
<NoteHistoryDrawer
|
||||||
|
item={{ ...noteHistoryModalopen.selectedItem }}
|
||||||
|
open={noteHistoryModalopen.open}
|
||||||
|
setOpen={setNoteHistoryModalopen}
|
||||||
|
setApiStatus={setApiStatus}
|
||||||
|
/>
|
||||||
|
<ImageDrawer
|
||||||
|
item={{ ...attachmentsModalopen.selectedItem }}
|
||||||
|
open={attachmentsModalopen.open}
|
||||||
|
setOpen={setAttachmentsModalopen}
|
||||||
|
setApiStatus={setApiStatus}
|
||||||
|
/>
|
||||||
<DeleteModal
|
<DeleteModal
|
||||||
item={{ ...deleteModalopen.selectedItem }}
|
item={{ ...deleteModalopen.selectedItem }}
|
||||||
open={deleteModalopen.open}
|
open={deleteModalopen.open}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { post } from '../../../apiUtil';
|
import { postFormBody } from '../../../apiUtil';
|
||||||
import { API_URL } from '../../../constants';
|
import { API_URL } from '../../../constants';
|
||||||
|
|
||||||
export const addItems = (item) => post(`${API_URL}/WorkOrder/AddWorkorder`, item);
|
export const addItems = (item) => postFormBody(`${API_URL}/WorkOrder/AddWorkorder`, item);
|
||||||
103
src/pages/workorder/noteHistory/NoteHistoryDrawer.js
Normal file
103
src/pages/workorder/noteHistory/NoteHistoryDrawer.js
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
Button,
|
||||||
|
TextField,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
Typography,
|
||||||
|
IconButton,
|
||||||
|
Paper,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
|
import {getComments, addComments} from './api';
|
||||||
|
|
||||||
|
const NoteHistoryDrawer = ({ item, open, setOpen, setApiStatus }) => {
|
||||||
|
const [note, setNote] = useState('');
|
||||||
|
const [noteHistory, setNoteHistory] = useState([]);
|
||||||
|
const [comments, setComments] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (note.trim()) {
|
||||||
|
await addComments({id: item.id , Comments: [...noteHistory, note]});
|
||||||
|
setNoteHistory([]);
|
||||||
|
setNote('');
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDrawer = (open) => (event) => {
|
||||||
|
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setOpen(open);
|
||||||
|
};
|
||||||
|
useEffect(async() => {
|
||||||
|
if(item.id) {
|
||||||
|
const comments = await getComments(item.id);
|
||||||
|
if(comments && comments.length>0) {
|
||||||
|
const _comments = comments.map(c=> c.commenttext)
|
||||||
|
setComments(_comments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [item])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button variant="outlined" onClick={toggleDrawer(true)}>
|
||||||
|
Open Note History
|
||||||
|
</Button>
|
||||||
|
<Drawer anchor="right" open={open} onClose={toggleDrawer(false)}>
|
||||||
|
<div style={{ padding: 20, width: 300 }}>
|
||||||
|
<IconButton onClick={toggleDrawer(false)}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h6">Note History</Typography>
|
||||||
|
<TextField
|
||||||
|
label="Add a note"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
value={note}
|
||||||
|
onChange={(e) => setNote(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
style={{ marginTop: 10 }}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
<Typography variant="subtitle1" style={{ marginTop: 20 }}>
|
||||||
|
Submitted Notes:
|
||||||
|
</Typography>
|
||||||
|
<div>
|
||||||
|
<List>
|
||||||
|
{[...comments, ...noteHistory].map((n, index) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f0f0f0',
|
||||||
|
marginBottom: 5,
|
||||||
|
borderRadius: 4,
|
||||||
|
transition: 'background-color 0.3s',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '#e0e0e0',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemText primary={n} />
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NoteHistoryDrawer;
|
||||||
6
src/pages/workorder/noteHistory/api.js
Normal file
6
src/pages/workorder/noteHistory/api.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { post, get } from '../../../apiUtil';
|
||||||
|
import { API_URL } from '../../../constants';
|
||||||
|
|
||||||
|
export const addComments = (item) => post(`${API_URL}/WorkOrder/AddComments`, item);
|
||||||
|
|
||||||
|
export const getComments = (id) => get(`${API_URL}/WorkOrder/GetCommentsByWorkorderId?woid=${id}`);
|
||||||
Loading…
Add table
Reference in a new issue