Skip to content

Commit

Permalink
TRACK-181: Issues Is Resolved Slider (#2478)
Browse files Browse the repository at this point in the history
- add resolved slider to edit issue modal
- add is_resolved column to work_issues table
- update api request
- add Resolved flag
- add sorting to list of issues
- filter report preview to remove resolved issues
-  filter EA report to remove resolved issues
  • Loading branch information
Marilynn-Stone authored Dec 12, 2024
1 parent 0f9d016 commit 55489c7
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""add is_resolved to work_issues
Revision ID: 09fb62ec79e9
Revises: af4022c1a70c
Create Date: 2024-11-29 16:29:02.770899
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = '09fb62ec79e9'
down_revision = 'af4022c1a70c'
branch_labels = None
depends_on = None


def upgrade():

with op.batch_alter_table('work_issues', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_resolved', sa.Boolean(), nullable=False, server_default=sa.false()))

with op.batch_alter_table('work_issues_history', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_resolved', sa.Boolean(), autoincrement=False, nullable=False, server_default=sa.false()))


def downgrade():

with op.batch_alter_table('work_issues_history', schema=None) as batch_op:
batch_op.drop_column('is_resolved')

with op.batch_alter_table('work_issues', schema=None) as batch_op:
batch_op.drop_column('is_resolved')
1 change: 1 addition & 0 deletions epictrack-api/src/api/models/work_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class WorkIssues(BaseModelVersioned):
title = Column(String(500), nullable=False)
is_active = Column(Boolean(), default=True, nullable=False)
is_high_priority = Column(Boolean(), default=False, nullable=False)
is_resolved = Column(Boolean(), default=False, nullable=False)
start_date = Column(DateTime(timezone=True), nullable=False)
expected_resolution_date = Column(DateTime(timezone=True), nullable=True)

Expand Down
5 changes: 5 additions & 0 deletions epictrack-api/src/api/schemas/request/work_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ class WorkIssuesParameterSchema(RequestBodyParameterSchema):
description="Flag indicating whether the issue is of high priority",
)

is_resolved: bool = fields.Bool(
default=False,
description="Flag indicating whether the issue is resolved",
)

start_date = fields.DateTime(
metadata={"description": "Start date for the issue"}, required=False
)
Expand Down
25 changes: 17 additions & 8 deletions epictrack-web/src/apiManager/http-request-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,23 @@ const PutRequest = <T>(url: string, data = {}, params = {}) => {
};

const PatchRequest = <T>(url: string, data = {}) => {
return instance.patch<T>(url, data, {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${
UserService.getToken() || window.localStorage.getItem("authToken")
}`,
},
});
return instance
.patch<T>(url, data, {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${
UserService.getToken() || window.localStorage.getItem("authToken")
}`,
},
})
.then((response) => {
// console.warn("PatchRequest - Response:", response); // Log the response
return response;
})
.catch((error) => {
console.error("PatchRequest - Error:", error); // Log the error
throw error; // Ensure the error propagates
});
};

const DeleteRequest = <T>(url: string, params = {}) => {
Expand Down
11 changes: 10 additions & 1 deletion epictrack-web/src/components/shared/chip/ETChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface StyledChipProps extends ChipProps {
active?: boolean;
inactive?: boolean;
highPriority?: boolean;
resolved?: boolean;
error?: boolean;
}

Expand All @@ -19,11 +20,12 @@ const ETChip = styled(
active = true,
inactive = false,
highPriority = false,
resolved = false,
error = false,
size = "small",
...other
}: StyledChipProps) => <Chip size={size} {...other} />
)(({ active, inactive, highPriority, error }: StyledChipProps) => {
)(({ active, inactive, highPriority, resolved, error }: StyledChipProps) => {
if (inactive) {
return {
background: "#F2F2F2",
Expand All @@ -38,6 +40,13 @@ const ETChip = styled(
...chipStyles,
};
}
if (resolved) {
return {
background: `${Palette.neutral.bg.dark}`,
color: `${Palette.neutral.dark}`,
...chipStyles,
};
}
if (error) {
return {
background: `${Palette.error.bg.light}`,
Expand Down
25 changes: 23 additions & 2 deletions epictrack-web/src/components/workPlan/issues/Forms/EditIssue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ const schema = yup.object().shape({
title: yup.string().required("Title is required"),
is_active: yup.boolean(),
is_high_priority: yup.boolean(),
is_resolved: yup.boolean(),
start_date: yup.string().required("Start date is required"),
expected_resolution_date: yup.string().nullable(),
expected_resolution_date: yup.string().when("is_resolved", {
is: true,
then: (schema) => schema.required("Missing Resolution Date"),
otherwise: (schema) => schema.nullable(),
}),
});

const EditIssue = () => {
Expand All @@ -35,6 +40,7 @@ const EditIssue = () => {
title: issueToEdit?.title || "",
is_active: Boolean(issueToEdit?.is_active),
is_high_priority: Boolean(issueToEdit?.is_high_priority),
is_resolved: Boolean(issueToEdit?.is_resolved),
start_date: issueToEdit?.start_date || "",
expected_resolution_date: issueToEdit?.expected_resolution_date || "",
},
Expand Down Expand Up @@ -66,6 +72,7 @@ const EditIssue = () => {
expected_resolution_date,
is_active,
is_high_priority,
is_resolved,
} = await schema.validate(data);

const dataToBeSubmitted = {
Expand All @@ -76,6 +83,7 @@ const EditIssue = () => {
: undefined,
is_active: Boolean(is_active),
is_high_priority: Boolean(is_high_priority),
is_resolved: Boolean(is_resolved),
};

editIssue(dataToBeSubmitted);
Expand Down Expand Up @@ -141,6 +149,19 @@ const EditIssue = () => {
</Stack>
}
/>
<FormControlLabel
control={<ControlledSwitch name="is_resolved" />}
label={
<Stack direction="row" spacing={1}>
<ETParagraph>Resolved</ETParagraph>
<Tooltip title="Resolved Issues will not appear on any Report">
<Box component={"span"}>
<InfoIcon />
</Box>
</Tooltip>
</Stack>
}
/>
</Stack>
</Grid>
<Grid item xs={6}>
Expand All @@ -153,7 +174,7 @@ const EditIssue = () => {
/>
</Grid>
<Grid item xs={6}>
<ETFormLabel required>Expected Resolution Date</ETFormLabel>
<ETFormLabel>Resolution Date</ETFormLabel>
<ControlledDatePicker name="expected_resolution_date" />
</Grid>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
MONTH_DAY_YEAR,
ROLES,
} from "../../../../constants/application-constant";
import { Else, If, Then, When } from "react-if";
import { Else, If, Switch, Case, Then, When, Default } from "react-if";
import icons from "../../../icons";
import { IconProps } from "../../../icons/type";
import { IssuesContext } from "../IssuesContext";
Expand Down Expand Up @@ -41,14 +41,17 @@ const IssueSummary = ({ issue }: { issue: WorkIssue }) => {
<When condition={issue.is_high_priority}>
<ETChip highPriority label="High Profile" />
</When>
<If condition={issue.is_active}>
<Then>
<Switch>
<Case condition={issue.is_resolved}>
<ETChip resolved label="Resolved" />
</Case>
<Case condition={issue.is_active}>
<ETChip active label="Active" />
</Then>
<Else>
</Case>
<Default>
<ETChip inactive label="Inactive" />
</Else>
</If>
</Default>
</Switch>
</Stack>
</AccordionSummaryItem>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export const IssuesProvider = ({
expected_resolution_date,
is_active,
is_high_priority,
is_resolved,
} = issueForm;

const request = {
Expand All @@ -175,11 +176,12 @@ export const IssuesProvider = ({
expected_resolution_date,
is_active,
is_high_priority,
is_resolved,
};

await issueService.editIssue(workId, String(issueToEdit.id), request);
handleLoadIssues();
} catch (error) {
console.error("editIssue error:", error);
const message = getErrorMessage(error);
showNotification(message, {
type: "error",
Expand Down
66 changes: 50 additions & 16 deletions epictrack-web/src/components/workPlan/issues/IssuesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,69 @@ import { Else, If, Then } from "react-if";
import IssueAccordion from "./IssueAccordion";
import { Button, Grid } from "@mui/material";
import { WorkplanContext } from "../WorkPlanContext";
import { WorkIssue } from "../../../models/Issue";
import IssueDialogs from "./Dialogs";
import { Restricted, hasPermission } from "components/shared/restricted";
import { useAppSelector } from "hooks";
import { ROLES } from "constants/application-constant";

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

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

const { roles, email } = useAppSelector((state) => state.user.userDetail);
const canCreate = hasPermission({ roles, allowed: [ROLES.CREATE] });
const isTeamMember = team?.some((member) => member.staff.email === email);

const lastInteractedIssue = React.useRef<number | null>(null);

// Sorting function
const sortIssues = (issues: WorkIssue[]): WorkIssue[] => {
return [...issues].sort((a, b) => {
// First, sort by resolved status
if (a.is_resolved !== b.is_resolved) {
return a.is_resolved ? 1 : -1; // Unresolved items come first
}
// Then, sort by date
if (a.start_date > b.start_date) {
return -1;
}
if (a.start_date < b.start_date) {
return 1;
}
return 0; //Equal
});
};

// Mapping function
const mapIssues = (
issues: WorkIssue[],
lastInteractedIssue: React.MutableRefObject<number | null>
) => {
return issues.map((issue, index) => (
<Grid key={`accordion-${issue.id}`} item xs={12}>
<IssueAccordion
issue={issue}
defaultOpen={
lastInteractedIssue.current
? issue.id === lastInteractedIssue.current
: index === 0
}
onInteraction={() => {
lastInteractedIssue.current = issue.id;
}}
/>
</Grid>
));
};

const sortedIssues = sortIssues(issues);

if (isIssuesLoading) {
return <IssuesViewSkeleton />;
}
Expand Down Expand Up @@ -57,21 +105,7 @@ const IssuesView = () => {
</Button>
</Restricted>
</Grid>
{issues.map((issue, index) => (
<Grid key={`accordion-${issue.id}`} item xs={12}>
<IssueAccordion
issue={issue}
defaultOpen={
lastInteractedIssue.current
? issue.id === lastInteractedIssue.current
: index === 0
}
onInteraction={() => {
lastInteractedIssue.current = issue.id;
}}
/>
</Grid>
))}
{mapIssues(sortedIssues, lastInteractedIssue)}
</Grid>
</Else>
</If>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export const ReferralSchedule = () => {

const activeApprovedIssues = issues.filter(
(issue) =>
issue.is_active && issue.updates.find((update) => update.is_approved)
!issue.is_resolved &&
issue.is_active &&
issue.updates.find((update) => update.is_approved)
);

const issueUpdates = activeApprovedIssues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const ThirtySixtyNinety = () => {

const activeApprovedHighprioIssues = issues.filter(
(issue) =>
!issue.is_resolved &&
issue.is_active &&
issue.is_high_priority &&
issue.updates.find((update) => update.is_approved)
Expand Down
1 change: 1 addition & 0 deletions epictrack-web/src/components/workPlan/issues/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface EditIssueForm {
expected_resolution_date?: string;
is_active: boolean;
is_high_priority: boolean;
is_resolved: boolean;
}

export interface EditIssueUpdateForm {
Expand Down
2 changes: 2 additions & 0 deletions epictrack-web/src/models/Issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface WorkIssue {
expected_resolution_date: string;
is_active: boolean;
is_high_priority: boolean;
is_resolved: boolean;
work_id: number;
is_deleted: boolean;
created_by: string;
Expand All @@ -33,6 +34,7 @@ export const defaultWorkIssue = {
expected_resolution_date: "",
active: false,
high_priority: false,
is_resolved: false,
updates: [],
};

Expand Down
Loading

0 comments on commit 55489c7

Please sign in to comment.