Skip to content

Commit

Permalink
highlight workplan row when inserted or edited (#1084)
Browse files Browse the repository at this point in the history
* highlight workplan row when inserted or edited

* clear timeout if it exists

* simplify save task and save milestone methods

* remove try catch at resource level
  • Loading branch information
jadmsaadaot authored Oct 25, 2023
1 parent c86c7f1 commit 77350e6
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 102 deletions.
52 changes: 52 additions & 0 deletions epictrack-web/src/components/workPlan/event/EventContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { createContext } from "react";
import { EVENT_TYPE } from "../phase/type";

export interface HighlightedRow {
type: EVENT_TYPE;
id: number;
}

interface EventContextProps {
highlightedRow: HighlightedRow | null;
handleHighlightRow: (rowToHighlight?: HighlightedRow) => void;
}

export const EventContext = createContext<EventContextProps>({
highlightedRow: null,
handleHighlightRow: (_rowToHighlight?: HighlightedRow) => {
return;
},
});

let highlightTimout: null | NodeJS.Timeout = null;
export const EventProvider = ({
children,
}: {
children: JSX.Element | JSX.Element[];
}) => {
const [highlightedRow, setHighlightedRow] =
React.useState<HighlightedRow | null>(null);

const handleHighlightRow = (rowToHighlight?: HighlightedRow) => {
if (!rowToHighlight) return;
if (highlightTimout) {
clearTimeout(highlightTimout);
}
const HIGHLIGHT_DURATION = 6000;
setHighlightedRow(rowToHighlight);
highlightTimout = setTimeout(() => {
setHighlightedRow(null);
}, HIGHLIGHT_DURATION);
};

return (
<EventContext.Provider
value={{
highlightedRow,
handleHighlightRow,
}}
>
{children}
</EventContext.Provider>
);
};
119 changes: 71 additions & 48 deletions epictrack-web/src/components/workPlan/event/EventForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState, useEffect, useContext } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
Expand Down Expand Up @@ -39,6 +39,8 @@ import DecisionInput from "./components/DecisionInput";
import { POSITION_ENUM } from "../../../models/position";
import { Else, If, Then } from "react-if";
import ExtensionInput from "./components/ExtensionInput";
import { EventContext } from "./EventContext";
import { EVENT_TYPE } from "../phase/type";

interface EventFormProps {
onSave: () => void;
Expand All @@ -52,7 +54,13 @@ interface NumberOfDaysChangeProps {
}
const InfoIcon: React.FC<IconProps> = Icons["InfoIcon"];

const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => {
const EventForm = ({
onSave = () => {
return;
},
event,
isFormFieldsLocked,
}: EventFormProps) => {
const [submittedEvent, setSubmittedEvent] = React.useState<MilestoneEvent>();
const [configurations, setConfigurations] = React.useState<
EventConfiguration[]
Expand All @@ -67,7 +75,10 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => {
const anticipatedDateRef = React.useRef();
const numberOfDaysRef = React.useRef();
const endDateRef = React.useRef();

const ctx = React.useContext(WorkplanContext);
const { handleHighlightRow } = useContext(EventContext);

const [actualAdded, setActualAdded] = React.useState<boolean>(false);
const [anticipatedLabel, setAnticipatedLabel] =
React.useState("Anticipated Date");
Expand Down Expand Up @@ -204,7 +215,7 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => {
(isCreateMode && !!submittedData.actual_date) ||
(!isCreateMode && !event?.actual_date && !!submittedData.actual_date);
if (!showConfirmDialog) {
saveEvent(submittedData);
handleSaveEvent(submittedData);
}
setShowEventLockDialog(showConfirmDialog);
} catch (e: any) {
Expand All @@ -219,50 +230,62 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => {
}
};

const saveEvent = React.useCallback(
async (data?: MilestoneEvent) => {
try {
const dataToBeSubmitted = data || getValues();
if (event) {
const createResult = await eventService.update(
dataToBeSubmitted,
Number(event.id)
);
if (createResult.status === 200) {
showNotification("Milestone details updated", {
type: "success",
});
if (onSave) {
onSave();
}
}
} else {
const createResult = await eventService.create(
dataToBeSubmitted,
Number(ctx.selectedWorkPhase?.id)
);
if (createResult.status === 201) {
showNotification("Milestone details inserted", {
type: "success",
});
if (onSave) {
onSave();
}
}
}
} catch (e) {
const error = getAxiosError(e);
const message =
error?.response?.status === 422
? error.response.data?.toString()
: COMMON_ERROR_MESSAGE;
showNotification(message, {
type: "error",
});
}
},
[event, submittedEvent]
);
const createEvent = async (data: MilestoneEvent) => {
const createdResult = await eventService.create(
data,
Number(ctx.selectedWorkPhase?.id)
);
showNotification("Milestone details inserted", {
type: "success",
});
handleHighlightRow({
type: EVENT_TYPE.MILESTONE,
id: createdResult.data.id,
});

return createdResult;
};

const updateEvent = async (data: MilestoneEvent) => {
if (!event) {
return;
}

const updatedResult = await eventService.update(data, Number(event.id));
showNotification("Milestone details updated", {
type: "success",
});
handleHighlightRow({
type: EVENT_TYPE.MILESTONE,
id: event.id,
});
return updatedResult;
};

const saveEvent = (data: MilestoneEvent) => {
if (event) {
return updateEvent(data);
}

return createEvent(data);
};

const handleSaveEvent = async (data?: MilestoneEvent) => {
try {
const dataToBeSubmitted = data ?? getValues();
await saveEvent(dataToBeSubmitted);
onSave();
} catch (e) {
const error = getAxiosError(e);
const message =
error?.response?.status === 422
? error.response.data?.toString()
: COMMON_ERROR_MESSAGE;
showNotification(message, {
type: "error",
});
}
};

const onChangeMilestoneType = (configuration_id: number) => {
const configuration = configurations.filter(
Expand Down Expand Up @@ -540,7 +563,7 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => {
disableEscapeKeyDown
fullWidth
okButtonText="Save"
onOk={() => saveEvent()}
onOk={() => handleSaveEvent()}
onCancel={() => {
setShowEventLockDialog(false);
}}
Expand Down
11 changes: 7 additions & 4 deletions epictrack-web/src/components/workPlan/event/EventList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from "react";
import React, { useContext, useEffect } from "react";
import { EVENT_TYPE } from "../phase/type";
import eventService from "../../../services/eventService/eventService";
import Icons from "../../icons";
Expand Down Expand Up @@ -33,14 +33,14 @@ import { showNotification } from "../../shared/notificationProvider";
import ImportTaskEvent from "../task/ImportTaskEvent";
import { getAxiosError } from "../../../utils/axiosUtils";
import { COMMON_ERROR_MESSAGE } from "../../../constants/application-constant";
import EventForm from "./EventForm";
import { TemplateStatus, WorkPhase } from "../../../models/work";
import { SnackbarKey, closeSnackbar } from "notistack";
import { OptionType } from "../../shared/filterSelect/type";
import FilterSelect from "../../shared/filterSelect/FilterSelect";
import { ListType } from "../../../models/code";
import responsibilityService from "../../../services/responsibilityService/responsibilityService";
import EventListTable from "./EventListTable";
import EventForm from "./EventForm";

const ImportFileIcon: React.FC<IconProps> = Icons["ImportFileIcon"];
const DownloadIcon: React.FC<IconProps> = Icons["DownloadIcon"];
Expand Down Expand Up @@ -106,9 +106,11 @@ const EventList = () => {
React.useState<boolean>(false);
const [showDeleteDialog, setShowDeleteDialog] =
React.useState<boolean>(false);

const isEventFormFieldLocked = React.useMemo(() => {
return !!milestoneEvent?.actual_date;
}, [milestoneEvent]);

React.useEffect(() => setEvents([]), [ctx.selectedWorkPhase?.phase.id]);
React.useEffect(() => {
getCombinedEvents();
Expand Down Expand Up @@ -206,16 +208,17 @@ const EventList = () => {
}
}, []);

const onDialogClose = React.useCallback(() => {
const onDialogClose = () => {
setShowTaskForm(false);
setShowTemplateForm(false);
setShowMilestoneForm(false);
getCombinedEvents();
getTemplateUploadStatus();
getWorkPhases();
// setEventId(undefined);
setTaskEvent(undefined);
setMilestoneEvent(undefined);
}, [ctx.work, ctx.selectedWorkPhase]);
};

const onTemplateFormSaveHandler = (templateId: number) => {
setShowTemplateForm(false);
Expand Down
14 changes: 13 additions & 1 deletion epictrack-web/src/components/workPlan/event/EventListTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { EVENT_TYPE } from "../phase/type";
import MasterTrackTable from "../../shared/MasterTrackTable";
import Icons from "../../icons";
Expand All @@ -23,6 +23,7 @@ import {
getSelectFilterOptions,
rowsPerPageOptions,
} from "../../shared/MasterTrackTable/utils";
import { EventContext } from "./EventContext";

const LockIcon: React.FC<IconProps> = Icons["LockIcon"];

Expand Down Expand Up @@ -53,6 +54,8 @@ const EventListTable = ({
}: EventListTable) => {
const classes = useStyle();

const { highlightedRow } = useContext(EventContext);

const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10, //customize the default page size
Expand Down Expand Up @@ -395,6 +398,15 @@ const EventListTable = ({
muiTablePaginationProps={{
rowsPerPageOptions: rowsPerPageOptions(events.length),
}}
muiTableBodyRowProps={({ row }) => ({
style: {
background:
row.original.type === highlightedRow?.type &&
row.original.id === highlightedRow?.id
? Palette.success.bg.light
: "inherit",
},
})}
state={{
isLoading: loading,
showGlobalFilter: true,
Expand Down
13 changes: 13 additions & 0 deletions epictrack-web/src/components/workPlan/event/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { EventProvider } from "./EventContext";
import EventList from "./EventList";

const Event = () => {
return (
<EventProvider>
<EventList />
</EventProvider>
);
};

export default Event;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ETAccordionSummary from "../../shared/accordion/components/AccordionSumma
import { ETCaption1, ETParagraph } from "../../shared";
import { Palette } from "../../../styles/theme";
import ETAccordionDetails from "../../shared/accordion/components/AccordionDetails";
import EventGrid from "../event/EventList";
import EventGrid from "../event";
import { WorkplanContext } from "../WorkPlanContext";
import BorderLinearProgress from "../../shared/progress/Progress";
import Icons from "../../icons/index";
Expand Down
Loading

0 comments on commit 77350e6

Please sign in to comment.