Skip to content

Commit

Permalink
[Track-411] Issue staleness calculated on approved issues only (#2537)
Browse files Browse the repository at this point in the history
  • Loading branch information
tolkamps1 authored Feb 24, 2025
1 parent 1676ea8 commit 9683ce1
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 51 deletions.
4 changes: 1 addition & 3 deletions epictrack-web/src/components/workPlan/WorkPlanContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import About from "./about";
import { useLocation } from "react-router-dom";
import { WORKPLAN_TAB } from "./constants";
import { StalenessEnum } from "constants/application-constant";
import { issueListMaxStaleness, calculateStaleness } from "./utils";
import { issueListMaxStaleness } from "./utils";

const IndicatorIcon: React.FC<IconProps> = Icons["IndicatorIcon"];
const ExclamationSmallIcon: React.FC<IconProps> = Icons["ExclamationSmallIcon"];
Expand Down Expand Up @@ -55,8 +55,6 @@ const WorkPlanContainer = () => {

const highestStaleness = issueListMaxStaleness(issues);

console.info("highestStaleness:", highestStaleness);

const iconStyles = React.useMemo(() => {
if (highestStaleness === StalenessEnum.CRITICAL) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const EditIssueUpdate = () => {
</Grid>
<Grid item xs={12}>
<ETFormLabelWithCharacterLimit
characterCount={watchedDescription.length}
characterCount={watchedDescription ? watchedDescription.length : 0}
maxCharacterLength={descriptionCharacterLimit}
required
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import React from "react";
import { FC, useContext } from "react";
import { Else, If, Then } from "react-if";
import { Button, Grid } from "@mui/material";
import { WorkIssue } from "../../../../models/Issue";
import { ETCaption1, ETHeading4, ETParagraph, GrayBox } from "../../../shared";
import moment from "moment";
import { ETChip } from "../../../shared/chip/ETChip";
import { WorkIssue } from "../../../../models/Issue";
import icons from "../../../icons";
import { IconProps } from "../../../icons/type";
import { Palette } from "../../../../styles/theme";
import { Else, If, Then } from "react-if";
import { IssuesContext } from "../IssuesContext";
import TrackDialog from "../../../shared/TrackDialog";
import IssueHistory from "./IssueHistory";
import { ETChip } from "../../../shared/chip/ETChip";
import { Restricted } from "../../../shared/restricted";
import { ETCaption1, ETHeading4, ETParagraph, GrayBox } from "../../../shared";
import {
MONTH_DAY_YEAR,
ROLES,
} from "../../../../constants/application-constant";
import { Restricted } from "../../../shared/restricted";
import { useIsTeamMember, useUserHasRole } from "../../utils";
import IssueHistory from "./IssueHistory";

const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
const latestUpdate = issue.updates[0];
const CheckCircleIcon: React.FC<IconProps> = icons["CheckCircleIcon"];
const PencilEditIcon: React.FC<IconProps> = icons["PencilEditIcon"];
const AddIcon: React.FC<IconProps> = icons["AddIcon"];
const latestUpdate = issue.updates[0] ?? null;
const CheckCircleIcon: FC<IconProps> = icons["CheckCircleIcon"];
const PencilEditIcon: FC<IconProps> = icons["PencilEditIcon"];
const AddIcon: FC<IconProps> = icons["AddIcon"];
const isTeamMember = useIsTeamMember();
const userHasRole = useUserHasRole();

Expand All @@ -34,10 +34,10 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
setUpdateToClone,
setUpdateToEdit,
setNewIssueUpdateFormIsOpen,
} = React.useContext(IssuesContext);
} = useContext(IssuesContext);

const handleApproveIssue = () => {
approveIssue(issue.id, latestUpdate.id);
approveIssue(issue.id, latestUpdate?.id);
setIssueToApproveId(null);
};

Expand All @@ -56,12 +56,12 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
>
<Grid item xs={"auto"}>
<ETCaption1 bold color={Palette.neutral.dark}>
{moment(latestUpdate.posted_date)
{moment(latestUpdate?.posted_date)
.format(MONTH_DAY_YEAR)
.toUpperCase()}
</ETCaption1>
</Grid>
<If condition={latestUpdate.is_approved}>
<If condition={latestUpdate?.is_approved}>
<Then>
<Grid item xs="auto">
<ETChip
Expand Down Expand Up @@ -93,7 +93,7 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
</ETParagraph>
</Grid>

<If condition={!latestUpdate.is_approved}>
<If condition={!latestUpdate?.is_approved}>
<Then>
<Grid item>
<Restricted
Expand Down Expand Up @@ -148,10 +148,12 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
<Grid item>
<Restricted
allowed={[
latestUpdate.is_approved ? ROLES.EXTENDED_EDIT : ROLES.EDIT,
latestUpdate?.is_approved
? ROLES.EXTENDED_EDIT
: ROLES.EDIT,
]}
exception={
(!latestUpdate.is_approved && isTeamMember) || userHasRole
(!latestUpdate?.is_approved && isTeamMember) || userHasRole
}
errorProps={{ disabled: true }}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import React, { useContext, useState } from "react";
import { FC, useContext, useState } from "react";
import moment from "moment";
import { Else, If, Then, When, Unless } from "react-if";
import { Button, Collapse, Grid, Stack, useTheme } from "@mui/material";
import Timeline from "@mui/lab/Timeline";
import TimelineItem from "@mui/lab/TimelineItem";
import TimelineSeparator from "@mui/lab/TimelineSeparator";
import TimelineConnector from "@mui/lab/TimelineConnector";
import TimelineDot from "@mui/lab/TimelineDot";
import TimelineOppositeContent from "@mui/lab/TimelineOppositeContent";
import TimelineContent, {
timelineContentClasses,
} from "@mui/lab/TimelineContent";
import { WorkIssue } from "../../../../../models/Issue";
import moment from "moment";
import { Palette } from "../../../../../styles/theme";
import { ETCaption1, ETCaption3, ETPreviewText } from "../../../../shared";
import ReadMoreText from "../../../../shared/ReadMoreText";
import TimelineContent, {
timelineContentClasses,
} from "@mui/lab/TimelineContent";
import { Button, Collapse, Grid, Stack, useTheme } from "@mui/material";
import { Else, If, Then, When, Unless } from "react-if";
import { IconProps } from "../../../../icons/type";
import icons from "../../../../icons";
import { IssuesContext } from "../../IssuesContext";
import {
MONTH_DAY_YEAR,
ROLES,
} from "../../../../../constants/application-constant";
import { Restricted } from "../../../../shared/restricted";
import { IssuesContext } from "../../IssuesContext";
import { EmptyIssueHistory } from "./EmptyIssueHistory";
import { useUserHasRole } from "../../../utils";

Expand All @@ -35,11 +35,11 @@ const IssueHistory = ({ issue }: { issue: WorkIssue }) => {

const [expand, setExpand] = useState(false);

const latestUpdate = issue.updates[0];
const latestUpdate = issue.updates[0] ?? null;
const subsequentUpdates = issue.updates.slice(1);
const highlightFirstInTimeLineApproved = !latestUpdate.is_approved;
const PencilEditIcon: React.FC<IconProps> = icons["PencilEditIcon"];
const ExpandIcon: React.FC<IconProps> = icons["ExpandIcon"];
const highlightFirstInTimeLineApproved = !latestUpdate?.is_approved;
const PencilEditIcon: FC<IconProps> = icons["PencilEditIcon"];
const ExpandIcon: FC<IconProps> = icons["ExpandIcon"];

const SHOW_MORE_THRESHOLD = 3;

Expand Down
8 changes: 4 additions & 4 deletions epictrack-web/src/components/workPlan/issues/IssuesView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import { useContext } from "react";
import AddIcon from "@mui/icons-material/Add";
import NoDataEver from "../../shared/NoDataEver";
import { IssuesContext } from "./IssuesContext";
Expand All @@ -16,13 +16,13 @@ import { issueListMaxStaleness, calculateStaleness } from "../utils";
import WarningBox from "../../shared/warningBox";

const IssuesView = () => {
const { issues, team } = React.useContext(WorkplanContext) as {
const { issues, team } = useContext(WorkplanContext) as {
issues: WorkIssue[];
team: { staff: { email: string } }[];
};

const { isIssuesLoading, setCreateIssueFormIsOpen } =
React.useContext(IssuesContext);
useContext(IssuesContext);

const { roles, email } = useAppSelector((state) => state.user.userDetail);
const canCreate = hasPermission({ roles, allowed: [ROLES.CREATE] });
Expand All @@ -44,7 +44,7 @@ const IssuesView = () => {
};

const mapIssues = (issues: WorkIssue[]) => {
return issues.map((currentIssue, index) => {
return issues.map((currentIssue) => {
const staleness = calculateStaleness(currentIssue);
return (
<Grid key={`accordion-${currentIssue.id}`} item xs={12}>
Expand Down
32 changes: 19 additions & 13 deletions epictrack-web/src/components/workPlan/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useContext } from "react";
import { useAppSelector } from "hooks";
import moment from "moment";
import {
ROLES,
ISSUES_STALENESS_THRESHOLD,
ROLES,
StalenessEnum,
} from "../../constants/application-constant";
import moment from "moment";
import dateUtils from "../../utils/dateUtils";
import { useContext } from "react";
import { useAppSelector } from "hooks";
import { WorkplanContext } from "./WorkPlanContext";
import { WorkIssue } from "../../models/Issue";

// Get the active team members
export const useActiveTeam = () => {
const { team } = useContext(WorkplanContext);
Expand Down Expand Up @@ -38,12 +39,7 @@ export const useUserHasRole = () => {
};

// Helper function to calculate staleness
export const calculateStaleness = (issue: {
updates: { posted_date: string }[];
type?: string;
is_active: boolean;
is_resolved: boolean;
}) => {
export const calculateStaleness = (issue: WorkIssue) => {
const now = moment();
// Check if the issue is inactive or resolved
if (issue.is_resolved) {
Expand All @@ -52,15 +48,25 @@ export const calculateStaleness = (issue: {
if (!issue.is_active) {
return StalenessEnum.INACTIVE;
}
// Check if there are no updates
if (!issue.updates || issue.updates.length === 0) {
const approvedUpdates =
issue.updates?.filter((update) => update.is_approved) || [];

// Check if there are no approved updates
if (approvedUpdates.length === 0) {
return StalenessEnum.GOOD; // No update, consider it "GOOD"
}

// Find the latest approved update by posted_date
const latestApprovedUpdate = approvedUpdates.reduce((latest, update) => {
return moment(update.posted_date).isAfter(moment(latest.posted_date))
? update
: latest;
});

// Calculate the difference in days from the latest update
const diffDays = dateUtils.diff(
now.toLocaleString(),
issue.updates[0]?.posted_date,
latestApprovedUpdate.posted_date,
"days"
);

Expand Down

0 comments on commit 9683ce1

Please sign in to comment.