diff --git a/src/apiUtil.js b/src/apiUtil.js index f1719ef..6a6a1c2 100644 --- a/src/apiUtil.js +++ b/src/apiUtil.js @@ -9,6 +9,13 @@ const STANDARD_HEADERS = { Authorization: getAccessToken() }; +const STANDARD_HEADERS_FORM = { + accept: "application/json", + pragma: "no-cache", + "cache-control": "no-cache", + Authorization: getAccessToken(), +}; + export const get = (url) => fetch(url, { headers: { ...STANDARD_HEADERS, Authorization: getAccessToken() }, @@ -30,6 +37,26 @@ export const post = (url, payload) => 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) => fetch(url, { headers: STANDARD_HEADERS, @@ -41,6 +68,25 @@ export const put = (url, payload) => 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) => fetch(url, { headers: STANDARD_HEADERS, diff --git a/src/components/LeftNav.js b/src/components/LeftNav.js index 1f40d1b..8a4c8b5 100644 --- a/src/components/LeftNav.js +++ b/src/components/LeftNav.js @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React, { useEffect } from 'react'; import { styled, useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; @@ -70,6 +70,15 @@ const DrawerHeader = styled('div')(({ theme }) => ({ export default function PersistentDrawerLeft({ handleDrawerClose, open}) { const location = useLocation(); const theme = useTheme(); + + useEffect(() => { + document.addEventListener('mousedown', handleDrawerClose); + + return () => { + document.removeEventListener('mousedown', handleDrawerClose); + }; + }, []); + return ( { + 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 ( +
+ +
+ + Workorder Images + + + Upload Images + + + + {errors.file && {errors.file}} + + + {imagePaths.map((image, index) => ( + {/* Set to 6 to allow 2 items per row */} + + {`Image +
+ + {getImageName(selectedFiles[index])} {/* Display the extracted image name */} + + handleDeleteImage(image, index)} + aria-label="delete" + > + + +
+
+
+ ))} +
+
+
+
+ ); +}; + +export default ImageDrawer; \ No newline at end of file diff --git a/src/pages/workorder/attachments/api.js b/src/pages/workorder/attachments/api.js new file mode 100644 index 0000000..2b83b17 --- /dev/null +++ b/src/pages/workorder/attachments/api.js @@ -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}`); \ No newline at end of file diff --git a/src/pages/workorder/components/Form.js b/src/pages/workorder/components/Form.js index 5c36fbe..9c050e8 100644 --- a/src/pages/workorder/components/Form.js +++ b/src/pages/workorder/components/Form.js @@ -20,6 +20,7 @@ import { formValidation } from "../_utility"; import SubmitButtonWithLoader from "../../../components/SubmitButtonWithLoader"; import { form_text_builder, API_SUCCESS_MESSAGE } from "../../../constants"; import { getContacts, getCategories, getLocations, getUsers } from '../../contacts/list/api'; +import moment from 'moment'; const ItemForm = ({ item, mode, @@ -38,6 +39,19 @@ const ItemForm = ({ { text: "Sev 4", value: "Sev 4" }, { 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 [users, setUsers] = useState([]); const [categories, setCategories] = useState([]); @@ -75,13 +89,7 @@ const ItemForm = ({ const handleClose = () => { setOpen({ open: false }); }; - const handlelineItemIncrements = () => { - setValues((values) => ({ - ...values, - ...{lineitems:[...values.lineitems||[], {descriptions:'', lineitem:0}]}, - })); - }; useEffect(() => { getUsers().then((response) => { handleSuccess(response, setUsers) }); getContacts().then((response) => { handleSuccess(response, setContacts) }); @@ -127,10 +135,28 @@ const ItemForm = ({ error={!!errors.workerOrderNumber} helperText={errors.workerOrderNumber} /> - Work Order Title: + +
+ + Work Order Title: { const { value } = event.target; handleChangeKV("dueDate", value); @@ -282,6 +308,26 @@ const ItemForm = ({ ))}
+
+ Work Order Type:* + +
Location:*