diff --git a/client/src/api/reducers.js b/client/src/api/reducers.js index 1ce533d..410d99d 100644 --- a/client/src/api/reducers.js +++ b/client/src/api/reducers.js @@ -52,7 +52,7 @@ export const dataFetchReducer = (state, action) => { case "FETCH_ASSIGN_LOG_SUCCESS": return { ...state, - assignLog: action.payload, + assignLogData: action.payload, assignLogLoading: false, }; default: diff --git a/client/src/components/app/app.js b/client/src/components/app/app.js index 23685f5..8920956 100644 --- a/client/src/components/app/app.js +++ b/client/src/components/app/app.js @@ -10,4 +10,4 @@ export const App = () => { ); }; -export const DEBUG_ON = true; +export const DEBUG_ON = false; diff --git a/client/src/components/form/ManageTechnicianForm/fields.js b/client/src/components/form/ManageTechnicianForm/fields.js index 84f47c6..e810d15 100644 --- a/client/src/components/form/ManageTechnicianForm/fields.js +++ b/client/src/components/form/ManageTechnicianForm/fields.js @@ -4,6 +4,7 @@ export const selectTechnicianOptions_default = [ options: [{ value: "", text: "" }], }, ]; + export const fields = [ { name: "technician", diff --git a/client/src/components/form/ManageTicketForm/adminView.js b/client/src/components/form/ManageTicketForm/adminView.js index d6d8b24..ebbd4a8 100644 --- a/client/src/components/form/ManageTicketForm/adminView.js +++ b/client/src/components/form/ManageTicketForm/adminView.js @@ -12,21 +12,52 @@ import { } from "@elastic/eui"; import React, { useState } from "react"; import { MySelectField } from "../MySelectField"; -import { errorMessages } from "./fields"; +import { errorMessages, workLogFields } from "./fields"; import { handleDateChange, handleFormFieldBlur, handleFormFieldChange, + handleFormSubmit, } from "./handlers"; import { selectOptions } from "../selectOptions"; -import { TimeLogTable } from "../../table/TimeLogTable"; +import { WorkLogTable } from "../../table/WorkLogTable"; +import { TicketAssignmentTable } from "../../table/TicketAssignmentTable"; +import { AddTechnicianPopover } from "../../popover/TechnicianPopover"; +import { MyDatePicker } from "../MyDatePicker"; var _ = require("lodash"); export const AdminView = ( - { data, dispatch, workLogData, assignLogData }, + { + technician, + selectedTicket, + data, + dispatch, + workLogData, + workLogLoading, + assignLogData, + assignLogLoading, + assignmentRefresh, + setAssignmentRefresh, + workRefresh, + setWorkRefresh, + }, ...props ) => { + const internalFormSubmit = async (e, data) => { + const d = []; + d.push({ name: "start_datetime", value: data[0].value }); + d.push({ name: "end_datetime", value: data[1].value }); + d.push({ name: "ticket_id", value: selectedTicket.ticket_id }); + d.push({ name: "lsu_id", value: technician[0].value }); + + const response = await handleFormSubmit(e, d, "/work"); + if (response != null) { + const w = !workRefresh; + setWorkRefresh(w); + } + }; + const [timeData, setTimeData] = useState(workLogFields); return ( <> @@ -91,10 +122,20 @@ export const AdminView = ( <> - {/* TODO: WAITING ON BACKEND*/} - {/**/} - {/**/} - {/**/} + {assignLogLoading === true ? null : ( + <> + + + + + )} @@ -104,40 +145,43 @@ export const AdminView = (

Work Log

- - + + {workLogLoading === true ? null : ( + + )} + + + + + + + handleDateChange(date, "start_datetime", timeData, setTimeData) + } + /> + + + + + + handleDateChange(e, "end_datetime", timeData, setTimeData) + } + /> + + + + + internalFormSubmit(e, timeData)}> + Add Time Entry + + - {/* TODO: WAITING ON BACKEND*/} - {/**/} - {/* */} - {/* */} - {/* */} - {/* handleDateChange(date, "start_datetime", data, dispatch)*/} - {/* }*/} - {/* />*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* handleDateChange(e, "end_datetime", data, dispatch)*/} - {/* }*/} - {/* />*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* Add Time Entry*/} - {/* */} - {/* */} - {/**/} diff --git a/client/src/components/form/ManageTicketForm/handlers.js b/client/src/components/form/ManageTicketForm/handlers.js index 141d7f8..fa07521 100644 --- a/client/src/components/form/ManageTicketForm/handlers.js +++ b/client/src/components/form/ManageTicketForm/handlers.js @@ -28,7 +28,7 @@ export const handleFormSubmit = async (e, data, endpoint, put) => { } addToast({ - title: "Ticket Submitted!", + title: "Saved!", color: "success", }); diff --git a/client/src/components/form/MySelectField.js b/client/src/components/form/MySelectField.js index 96aa4af..0fc85e9 100644 --- a/client/src/components/form/MySelectField.js +++ b/client/src/components/form/MySelectField.js @@ -14,7 +14,7 @@ export const MySelectField = ( name={item.name} id={item.name} options={selectOptions.options} - value={item.value} + value={item.value === null ? "" : item.value} onChange={(e) => handleChange(e)} onBlur={(e) => handleBlur(e)} hasNoInitialSelection={true} diff --git a/client/src/components/popover/TechnicianPopover.js b/client/src/components/popover/TechnicianPopover.js index 0fb9e42..820b794 100644 --- a/client/src/components/popover/TechnicianPopover.js +++ b/client/src/components/popover/TechnicianPopover.js @@ -16,24 +16,37 @@ import { import { handleFormFieldBlur, handleFormFieldChange, + handleFormSubmit, } from "../form/ManageTicketForm/handlers"; import { dataFetchReducer } from "../../api/reducers"; import axios from "../../api/api"; +import { personFields } from "../form/person/fields"; -export const AddTechnicianPopover = () => { +export const AddTechnicianPopover = ( + { selectedTicket, assignmentRefresh, setAssignmentRefresh }, + ...props +) => { const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, - data: null, + data: fields, + selectTechnicianOptions: selectTechnicianOptions_default, }); - + const [isPopoverOpen, setIsPopoverOpen] = useState(false); useEffect(() => { const fetchData = async () => { dispatch({ type: "FETCH_INIT" }); - // TODO: ONCE BACKEND IS SET UP, FORMAT AND IMPLEMENT DATA FOR TABLE try { - const result = await axios.get("ticket"); - dispatch({ type: "FETCH_SUCCESS", payload: result.data }); + const result = await axios.get("/user/admin"); + const final = { + name: "technician", + options: result.data.map((o) => ({ + ...o, + value: o.lsu_id, + text: o.first_name + " " + o.last_name, + })), + }; + dispatch({ type: "FETCH_TECHNICIANS_SUCCESS", payload: final }); } catch (error) { dispatch({ type: "FETCH_FAILURE" }); } @@ -41,12 +54,21 @@ export const AddTechnicianPopover = () => { fetchData(); }, []); - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); const closePopover = () => setIsPopoverOpen(false); + const internalFormSubmit = async (e, data) => { + const d = []; + d.push({ name: "lsu_id", value: data[0].value }); + d.push({ name: "ticket_id", value: selectedTicket.ticket_id }); + + const response = await handleFormSubmit(e, d, "/assign"); + if (response != null) { + setIsPopoverOpen(false); + setAssignmentRefresh(!assignmentRefresh); + } + }; const button = ( Add @@ -54,31 +76,39 @@ export const AddTechnicianPopover = () => { ); return ( - -
- - - - handleFormFieldChange(e, state.data, dispatch) - } - handleBlur={(e) => handleFormFieldBlur(e, state.data, dispatch)} - /> - - - - Add - - - -
-
+ <> + {state.isLoading === true ? null : ( + + + + + { + handleFormFieldChange(e, state.data, dispatch); + }} + handleBlur={(e) => + handleFormFieldBlur(e, state.data, dispatch) + } + /> + + + + internalFormSubmit(e, state.data)}> + Add + + + + + + + )} + ); }; diff --git a/client/src/components/table/AssignedToTable.js b/client/src/components/table/TicketAssignmentTable.js similarity index 55% rename from client/src/components/table/AssignedToTable.js rename to client/src/components/table/TicketAssignmentTable.js index 48af5cb..cd5b103 100644 --- a/client/src/components/table/AssignedToTable.js +++ b/client/src/components/table/TicketAssignmentTable.js @@ -3,28 +3,7 @@ import React from "react"; import { EuiBasicTable, EuiLink, EuiHealth, EuiButton } from "@elastic/eui"; import { AdminTicketFlyout } from "../flyout/flyout"; -const userTest = [ - { - id: "1", - firstName: "john", - lastName: "doe", - }, -]; -/* -Example user object: - - - -Example country object: - -{ - code: 'NL', - name: 'Netherlands', - flag: '🇳🇱' -} -*/ - -export const TicketAssignmentTable = ({ handleTicketSelection }, ...props) => { +export const TicketAssignmentTable = ({ items, isLoading }, ...props) => { const deleteUser = (user) => {}; const actions = [ @@ -41,18 +20,12 @@ export const TicketAssignmentTable = ({ handleTicketSelection }, ...props) => { const columns = [ { - field: "firstName", + field: "first_name", name: "First Name", - sortable: true, - "data-test-subj": "firstNameCell", }, { - field: "lastName", + field: "last_name", name: "Last Name", - truncateText: true, - mobileOptions: { - show: false, - }, }, { name: "Actions", @@ -60,16 +33,14 @@ export const TicketAssignmentTable = ({ handleTicketSelection }, ...props) => { }, ]; - const items = userTest.filter((user, index) => index < 10); - const getRowProps = (item) => { const { id } = item; return { "data-test-subj": `row-${id}`, className: "customRowClass", - onClick: (e) => { - handleTicketSelection(e, id); - }, + // onClick: (e) => { + // handleTicketSelection(e, id); + // }, }; }; @@ -84,12 +55,17 @@ export const TicketAssignmentTable = ({ handleTicketSelection }, ...props) => { }; return ( - +
+ {isLoading === true ? null : ( + + )} +
); }; diff --git a/client/src/components/table/TicketsTable.js b/client/src/components/table/TicketsTable.js index f698741..35aa608 100644 --- a/client/src/components/table/TicketsTable.js +++ b/client/src/components/table/TicketsTable.js @@ -103,6 +103,7 @@ export const TicketsTable = ( columns={columns} rowProps={getRowProps} cellProps={getCellProps} + tableLayout={"auto"} /> )} diff --git a/client/src/components/table/TimeLogTable.js b/client/src/components/table/WorkLogTable.js similarity index 56% rename from client/src/components/table/TimeLogTable.js rename to client/src/components/table/WorkLogTable.js index 71d6e97..8114742 100644 --- a/client/src/components/table/TimeLogTable.js +++ b/client/src/components/table/WorkLogTable.js @@ -2,29 +2,9 @@ import React from "react"; import { EuiBasicTable, EuiLink, EuiHealth, EuiButton } from "@elastic/eui"; import { AdminTicketFlyout } from "../flyout/flyout"; +import moment from "moment"; -const userTest = [ - { - id: "1", - start_datetime: "john", - end_datetime: "doe", - }, -]; -/* -Example user object: - - - -Example country object: - -{ - code: 'NL', - name: 'Netherlands', - flag: '🇳🇱' -} -*/ - -export const TimeLogTable = ({ handleTicketSelection }, ...props) => { +export const WorkLogTable = ({ items, isLoading }, ...props) => { const deleteUser = (user) => {}; const actions = [ @@ -40,18 +20,26 @@ export const TimeLogTable = ({ handleTicketSelection }, ...props) => { ]; const columns = [ + { + field: "first_name", + name: "First Name", + }, + { + field: "last_name", + name: "Last Name", + }, { field: "start_datetime", name: "Start Datetime", - sortable: true, - "data-test-subj": "firstNameCell", + render: (start_datetime) => { + return moment(start_datetime).format("MMMM Do YYYY, h:mm a"); + }, }, { field: "end_datetime", name: "End Datetime", - truncateText: true, - mobileOptions: { - show: false, + render: (end_datetime) => { + return moment(end_datetime).format("MMMM Do YYYY, h:mm a"); }, }, { @@ -60,16 +48,14 @@ export const TimeLogTable = ({ handleTicketSelection }, ...props) => { }, ]; - const items = userTest.filter((user, index) => index < 10); - const getRowProps = (item) => { const { id } = item; return { "data-test-subj": `row-${id}`, className: "customRowClass", - onClick: (e) => { - handleTicketSelection(e, id); - }, + // onClick: (e) => { + // handleTicketSelection(e, id); + // }, }; }; @@ -84,12 +70,17 @@ export const TimeLogTable = ({ handleTicketSelection }, ...props) => { }; return ( - +
+ {isLoading === true ? null : ( + + )} +
); }; diff --git a/client/src/routes/admin/ManageTicket.js b/client/src/routes/admin/ManageTicket.js index bc9637b..40b09f0 100644 --- a/client/src/routes/admin/ManageTicket.js +++ b/client/src/routes/admin/ManageTicket.js @@ -28,14 +28,20 @@ import { handleFormSubmit } from "../../components/form/ManageTicketForm/handler import { AdminView } from "../../components/form/ManageTicketForm/adminView"; import { Debug } from "../../components/debug/debug"; import axios from "../../api/api"; -import { fields } from "../../components/form/ManageTicketForm/fields"; +import { + fields, + workLogFields, +} from "../../components/form/ManageTicketForm/fields"; import { dataFetchReducer } from "../../api/reducers"; import { MyStat } from "./Stats"; import { personFields } from "../../components/form/person/fields"; var _ = require("lodash"); -const TicketForm = ({ setSelectedTicket, selectedTicket }, ...props) => { +const TicketForm = ( + { technician, setSelectedTicket, selectedTicket }, + ...props +) => { const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: false, isError: false, @@ -46,29 +52,60 @@ const TicketForm = ({ setSelectedTicket, selectedTicket }, ...props) => { assignLogLoading: true, }); + const [workRefresh, setWorkRefresh] = useState(false); + const [assignmentRefresh, setAssignmentRefresh] = useState(false); + const [dataRefresh, setDataRefresh] = useState(false); + useEffect(() => { const fetchWorkLog = async (selectedTicket) => { try { const result = await axios.get( - "/ticket/work/" + selectedTicket.ticket_id + "/work/ticket/" + selectedTicket.ticket_id ); - dispatch({ type: "FETCH_WORK_LOG_SUCCESS", payload: result.data }); + const userResult = await axios.get("/user/admin"); + let final = []; + + for (let i = 0; i < result.data.length; i++) { + final.push({ + ...result.data[i], + ...userResult.data.find( + (itmInner) => itmInner.lsu_id === result.data[i].lsu_id + ), + }); + } + dispatch({ type: "FETCH_WORK_LOG_SUCCESS", payload: final }); } catch (error) { console.log(error); } }; + if (selectedTicket != null) { + fetchWorkLog(selectedTicket); + } + }, [workRefresh, selectedTicket]); + useEffect(() => { const fetchAssignLog = async (selectedTicket) => { try { const result = await axios.get( - "/ticket/assign/" + selectedTicket.ticket_id + "/assign/ticket/" + selectedTicket.ticket_id + ); + + const userResult = await axios.get("/user/admin"); + const final = userResult.data.filter((o) => + result.data.find(({ lsu_id }) => o.lsu_id === lsu_id) ); - dispatch({ type: "FETCH_ASSIGN_LOG_SUCCESS", payload: result.data }); + + dispatch({ type: "FETCH_ASSIGN_LOG_SUCCESS", payload: final }); } catch (error) { console.log(error); } }; + if (selectedTicket != null) { + fetchAssignLog(selectedTicket); + } + }, [assignmentRefresh, selectedTicket]); + useEffect(() => { const fetchData = async (selectedTicket) => { String.prototype.toProperCase = function () { return this.replace(/\w\S*/g, function (txt) { @@ -102,10 +139,8 @@ const TicketForm = ({ setSelectedTicket, selectedTicket }, ...props) => { }; if (selectedTicket != null) { fetchData(selectedTicket); - fetchWorkLog(selectedTicket); - fetchAssignLog(selectedTicket); } - }, [selectedTicket]); + }, [dataRefresh, selectedTicket]); const internalFormSubmit = async (e, data) => { const response = await handleFormSubmit(e, data, "/ticket"); @@ -123,10 +158,18 @@ const TicketForm = ({ setSelectedTicket, selectedTicket }, ...props) => { <> )} @@ -148,7 +191,7 @@ const TicketForm = ({ setSelectedTicket, selectedTicket }, ...props) => { ); }; -export const ManageTicket = (props) => { +export const ManageTicket = ({ technician }, ...props) => { const [selectedTicket, setSelectedTicket] = useState(null); const [tickets, setTickets] = useState(null); const [isTicketsLoading, setIsTicketsLoading] = useState(true); @@ -233,6 +276,7 @@ export const ManageTicket = (props) => { "Please select ticket." ) : ( diff --git a/client/src/routes/admin/NewTechnicianFlyout.js b/client/src/routes/admin/NewTechnicianFlyout.js index 72e0b8d..5232360 100644 --- a/client/src/routes/admin/NewTechnicianFlyout.js +++ b/client/src/routes/admin/NewTechnicianFlyout.js @@ -32,7 +32,7 @@ export const NewTechnicianFlyout = ( const showFlyout = () => setIsFlyoutVisible(true); - const localFormSubmit = (e, data) => { + const internalFormSubmit = (e, data) => { if (handleFormSubmit(e, data, "/user") != null) { setIsFlyoutVisible(false); } @@ -61,7 +61,7 @@ export const NewTechnicianFlyout = ( { - localFormSubmit(e, state.data); + internalFormSubmit(e, state.data); }} > Save diff --git a/client/src/routes/admin/SelectTechnician.js b/client/src/routes/admin/SelectTechnician.js index 9182dd4..cef0910 100644 --- a/client/src/routes/admin/SelectTechnician.js +++ b/client/src/routes/admin/SelectTechnician.js @@ -49,7 +49,7 @@ export const SelectTechnician = ({ technician, setTechnician }, ...props) => { name: "technician", options: result.data.map((o) => ({ ...o, - value: (o.first_name + "_" + o.last_name).toLowerCase(), + value: o.lsu_id, text: o.first_name + " " + o.last_name, })), }; diff --git a/client/src/routes/admin/Stats.js b/client/src/routes/admin/Stats.js index ba77b54..53f4599 100644 --- a/client/src/routes/admin/Stats.js +++ b/client/src/routes/admin/Stats.js @@ -20,15 +20,17 @@ export const MyStat = ( return ( <> - - - + {isLoading ? null : ( + + + + )} ); diff --git a/client/src/routes/admin/admin.js b/client/src/routes/admin/admin.js index 0388ff7..fc86871 100644 --- a/client/src/routes/admin/admin.js +++ b/client/src/routes/admin/admin.js @@ -42,7 +42,7 @@ export const AdminRoute = (props) => { setTechnician={setTechnician} /> ) : ( - + )} diff --git a/client/src/routes/user/user.js b/client/src/routes/user/user.js index f7b6a65..4b42475 100644 --- a/client/src/routes/user/user.js +++ b/client/src/routes/user/user.js @@ -44,9 +44,10 @@ export const UserRoute = (props) => { }); const internalFormSubmit = async (e, data) => { + data.find((o) => o.name === "component").value = "N/A"; const response = await handleFormSubmit(e, data, "/ticket"); if (response.status === 201) { - dispatch({ type: "CLEAR_FORM" }); + // dispatch({ type: "CLEAR_FORM" }); } }; return (