Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TRACK-181: Issues Is Resolved Slider #2478

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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