From ee117cfc478786e16e39efbe2c9ef869586f3a01 Mon Sep 17 00:00:00 2001 From: Dinesh <97143739+dinesh-aot@users.noreply.github.com> Date: Fri, 19 Apr 2024 00:13:40 -0700 Subject: [PATCH] Confetti (#2147) * setup confetti api fix * setup endpoints * remove unused attributes * setup workphase by id * set up event_position check * add work phase in response * fix migration error * clean up type declaration * fix linting * removing trailing white space * flake 8 * flake 8 * confetti related changes --------- Co-authored-by: David Nunez --- ...0_adding_column_in_action_template_and_.py | 1 + .../src/api/actions/set_event_date.py | 1 - epictrack-api/src/api/resources/work.py | 24 ++++++++- .../src/api/schemas/response/__init__.py | 2 +- .../src/api/schemas/response/work_response.py | 6 +++ epictrack-api/tests/unit/apis/test_works.py | 2 +- .../components/workPlan/event/EventList.tsx | 54 ++++++++++++------- .../workPlan/phase/PhaseAccordion.tsx | 15 +++--- epictrack-web/src/constants/api-endpoint.ts | 4 +- .../src/services/workService/workService.ts | 29 +++++++++- 10 files changed, 102 insertions(+), 36 deletions(-) diff --git a/epictrack-api/migrations/versions/0ccfa4829660_adding_column_in_action_template_and_.py b/epictrack-api/migrations/versions/0ccfa4829660_adding_column_in_action_template_and_.py index 0cfffa1c0..59f49c883 100644 --- a/epictrack-api/migrations/versions/0ccfa4829660_adding_column_in_action_template_and_.py +++ b/epictrack-api/migrations/versions/0ccfa4829660_adding_column_in_action_template_and_.py @@ -31,6 +31,7 @@ def upgrade(): batch_op.add_column(sa.Column('description', sa.String(), autoincrement=False, nullable=True)) with op.batch_alter_table('special_fields', schema=None) as batch_op: + batch_op.drop_index('entity_field_index') batch_op.create_index('entity_field_index', ['entity', 'entity_id', 'field_name', 'time_range'], unique=False) # ### end Alembic commands ### diff --git a/epictrack-api/src/api/actions/set_event_date.py b/epictrack-api/src/api/actions/set_event_date.py index 6c165de24..3178779bd 100644 --- a/epictrack-api/src/api/actions/set_event_date.py +++ b/epictrack-api/src/api/actions/set_event_date.py @@ -1,7 +1,6 @@ """Disable work start date action handler""" from datetime import timedelta - from api.actions.base import ActionFactory from api.models import db from api.models.event import Event diff --git a/epictrack-api/src/api/resources/work.py b/epictrack-api/src/api/resources/work.py index 71859105c..da941305c 100644 --- a/epictrack-api/src/api/resources/work.py +++ b/epictrack-api/src/api/resources/work.py @@ -28,7 +28,7 @@ from api.utils.caching import AppCache from api.utils.datetime_helper import get_start_of_day from api.utils.util import cors_preflight - +from api.models.work_phase import WorkPhase API = Namespace("works", description="Works") @@ -286,7 +286,7 @@ def post(): @cors_preflight("GET") -@API.route("/work-phases/", methods=["GET", "OPTIONS"]) +@API.route("/work-phases//template-upload-status", methods=["GET", "OPTIONS"]) class WorkPhaseTemplateStatus(Resource): """Endpoints to get work phase template upload status""" @@ -306,6 +306,26 @@ def get(work_phase_id): ) +@cors_preflight("GET") +@API.route("/work-phases/", methods=["GET", "OPTIONS"]) +class WorkPhaseId(Resource): + """Endpoints to get work phase template upload status""" + + @staticmethod + @cors.crossdomain(origin="*") + @auth.require + @profiletime + def get(work_phase_id): + """Get the status if template upload is available""" + req.WorkIdPhaseIdPathParameterSchema().load(request.view_args) + work_phase = WorkPhase.find_by_id(work_phase_id) + print(work_phase.name) + return ( + res.WorkPhaseByIdResponseSchema().dump({'work_phase': work_phase}), + HTTPStatus.OK, + ) + + @cors_preflight("GET,POST") @API.route("//first-nations", methods=["GET", "POST", "OPTIONS"]) class WorkFirstNations(Resource): diff --git a/epictrack-api/src/api/schemas/response/__init__.py b/epictrack-api/src/api/schemas/response/__init__.py index e72418969..fefa092b5 100644 --- a/epictrack-api/src/api/schemas/response/__init__.py +++ b/epictrack-api/src/api/schemas/response/__init__.py @@ -37,4 +37,4 @@ from .work_response import ( WorkIssuesResponseSchema, WorkIssueUpdatesResponseSchema, WorkPhaseAdditionalInfoResponseSchema, WorkPhaseResponseSchema, WorkPhaseTemplateAvailableResponse, WorkResourceResponseSchema, WorkResponseSchema, - WorkStaffRoleReponseSchema, WorkStatusResponseSchema) + WorkStaffRoleReponseSchema, WorkStatusResponseSchema, WorkPhaseByIdResponseSchema) diff --git a/epictrack-api/src/api/schemas/response/work_response.py b/epictrack-api/src/api/schemas/response/work_response.py index ca1b2722b..ae59b6d50 100644 --- a/epictrack-api/src/api/schemas/response/work_response.py +++ b/epictrack-api/src/api/schemas/response/work_response.py @@ -150,6 +150,12 @@ class Meta( staff = fields.Nested(WorkStaffRoleReponseSchema(many=True), dump_default=[]) +class WorkPhaseByIdResponseSchema(Schema): + """Schema for additional work phase details""" + + work_phase = fields.Nested(WorkPhaseResponseSchema, dump_only=True) + + class WorkPhaseAdditionalInfoResponseSchema(Schema): """Schema for additional work phase details""" diff --git a/epictrack-api/tests/unit/apis/test_works.py b/epictrack-api/tests/unit/apis/test_works.py index 7dba1a18d..1142c124a 100644 --- a/epictrack-api/tests/unit/apis/test_works.py +++ b/epictrack-api/tests/unit/apis/test_works.py @@ -408,7 +408,7 @@ def test_work_phase_template_upload_status(client, auth_header): work_response_json = work_response.json assert work_response.status_code == HTTPStatus.CREATED url = urljoin( - API_BASE_URL, f"works/work-phases/{work_response_json['current_work_phase_id']}" + API_BASE_URL, f"works/work-phases/{work_response_json['current_work_phase_id']}/template-upload-status" ) response = client.get(url, headers=auth_header) assert response.status_code == HTTPStatus.OK diff --git a/epictrack-web/src/components/workPlan/event/EventList.tsx b/epictrack-web/src/components/workPlan/event/EventList.tsx index 25fe2455e..3e9fb2b43 100644 --- a/epictrack-web/src/components/workPlan/event/EventList.tsx +++ b/epictrack-web/src/components/workPlan/event/EventList.tsx @@ -29,6 +29,7 @@ import ImportTaskEvent from "../task/ImportTaskEvent"; import { TemplateStatus, Work, + WorkPhase, WorkPhaseAdditionalInfo, } from "../../../models/work"; import { SnackbarKey, closeSnackbar } from "notistack"; @@ -315,12 +316,12 @@ const EventList = () => { ); const workPhases = workPhasesResult.data as WorkPhaseAdditionalInfo[]; setWorkPhases(workPhases); - if (selectedWorkPhase) { - const selectedWp = workPhases.filter( - (p) => p.work_phase.id === selectedWorkPhase.work_phase.id - )[0]; - setSelectedWorkPhase(selectedWp); - } + // if (selectedWorkPhase) { + // const selectedWp = workPhases.filter( + // (p) => p.work_phase.id === selectedWorkPhase.work_phase.id + // )[0]; + // setSelectedWorkPhase(selectedWp); + // } setLoading(false); } }, []); @@ -336,21 +337,14 @@ const EventList = () => { setShowTemplateForm(false); setShowMilestoneForm(false); getCombinedEvents(); + getWorkById(); getWorkPhases(); - getTemplateUploadStatus() - .then(() => { - return getWorkById(); - }) - .then(() => { - if ( - milestoneEvent?.event_configuration.event_position === - EventPosition.END - ) { - dispatch(showConfetti(true)); - } - setTaskEvent(undefined); - setMilestoneEvent(undefined); - }); + getTemplateUploadStatus(); + if ( + milestoneEvent?.event_configuration?.event_position === EventPosition.END + ) { + getWorkPhaseById(); + } }; const onTemplateFormSaveHandler = (templateId: number) => { @@ -509,6 +503,26 @@ const EventList = () => { } }, [selectedWorkPhase?.work_phase.phase.id]); + const getWorkPhaseById = React.useCallback(async () => { + const workPhaseId = selectedWorkPhase?.work_phase.id; + if (workPhaseId) { + try { + const workPhase = (await workService.getWorkPhaseById( + Number(workPhaseId) + )) as WorkPhase; + + if (workPhase?.is_completed) { + dispatch(showConfetti(true)); + } + } catch (error) { + console.error( + `Error fetching work phase with ID: ${workPhaseId}`, + error + ); + } + } + }, [selectedWorkPhase?.work_phase.id]); + React.useEffect(() => { getTemplateUploadStatus(); }, [selectedWorkPhase]); diff --git a/epictrack-web/src/components/workPlan/phase/PhaseAccordion.tsx b/epictrack-web/src/components/workPlan/phase/PhaseAccordion.tsx index 6bc9b0400..a7a6b9c62 100644 --- a/epictrack-web/src/components/workPlan/phase/PhaseAccordion.tsx +++ b/epictrack-web/src/components/workPlan/phase/PhaseAccordion.tsx @@ -78,21 +78,20 @@ const SummaryItem = (props: SummaryItemProps) => { const PhaseAccordion = ({ phase, ...rest }: PhaseAccordionProps) => { const [expanded, setExpanded] = React.useState(false); - const ctx = useContext(WorkplanContext); + const { selectedWorkPhase, setSelectedWorkPhase } = + useContext(WorkplanContext); const isSelectedPhase = React.useMemo( - () => phase.work_phase.id === ctx.selectedWorkPhase?.work_phase.id, - [ctx.selectedWorkPhase] + () => phase.work_phase.id === selectedWorkPhase?.work_phase.id, + [selectedWorkPhase] ); React.useEffect( - () => - setExpanded(phase.work_phase.id === ctx.selectedWorkPhase?.work_phase.id), - [phase, ctx.selectedWorkPhase] + () => setExpanded(phase.work_phase.id === selectedWorkPhase?.work_phase.id), + [phase, selectedWorkPhase] ); const onExpandHandler = (expand: boolean) => { setExpanded(expand); - ctx.setSelectedWorkPhase(phase); + setSelectedWorkPhase(phase); }; - const fromDate = React.useMemo( () => Moment(phase.work_phase.start_date).isSameOrAfter(Moment()) diff --git a/epictrack-web/src/constants/api-endpoint.ts b/epictrack-web/src/constants/api-endpoint.ts index 39564159e..a1acaf14e 100644 --- a/epictrack-web/src/constants/api-endpoint.ts +++ b/epictrack-web/src/constants/api-endpoint.ts @@ -35,7 +35,8 @@ const Endpoints = { DOWNLOAD_WORK_PLAN: "works/workplan/download", WORK_TEAM_MEMBERS: "works/:work_id/staff-roles", WORK_TEAM_MEMBER: "works/staff-roles/:work_staff_id", - CHECK_TEMPLATE_UPLOAD_STATUS: "works/work-phases/:work_phase_id", + CHECK_TEMPLATE_UPLOAD_STATUS: + "works/work-phases/:work_phase_id/template-upload-status", WORK_FIRST_NATION_NOTES: "works/:work_id/first-nation-notes", WORK_NOTES: "works/:work_id/notes", WORK_FIRST_NATIONS: "works/:work_id/first-nations", @@ -43,6 +44,7 @@ const Endpoints = { WORK_FIRST_NATION: "works/first-nations/:work_first_nation_id", WORK_IMPORT_FIRST_NATIONS: "works/:work_id/first-nations/import", GET_ALL_WORK_TYPES: "works/types", + GET_WORK_PHASE_BY_ID: "works/work-phases/:work_phase_id", }, WorkTypes: { GET_ALL: "work-types", diff --git a/epictrack-web/src/services/workService/workService.ts b/epictrack-web/src/services/workService/workService.ts index d80bdf743..eaa782ce9 100644 --- a/epictrack-web/src/services/workService/workService.ts +++ b/epictrack-web/src/services/workService/workService.ts @@ -4,9 +4,12 @@ import ServiceBase from "../common/serviceBase"; import { MasterBase } from "../../models/type"; import { StaffWorkRole } from "../../models/staff"; import { WorkFirstNation } from "../../models/firstNation"; -import { Work } from "../../models/work"; +import { Work, WorkPhase } from "../../models/work"; import { WorkType } from "../../models/workType"; - +import { AxiosResponse } from "axios"; +interface WorkPhaseResponse { + work_phase: WorkPhase; +} class WorkService implements ServiceBase { async getAll(is_active = undefined) { return await http.GetRequest(Endpoints.Works.WORKS, { is_active }); @@ -100,6 +103,28 @@ class WorkService implements ServiceBase { return await http.GetRequest(Endpoints.Works.WORKS + `/${workId}/phases`); } + async getWorkPhaseById(workPhaseId: number): Promise { + try { + const endpoint = Endpoints.Works.GET_WORK_PHASE_BY_ID.replace( + ":work_phase_id", + workPhaseId.toString() + ); + const result: AxiosResponse = await http.GetRequest( + endpoint + ); + + if (!result.data || !result.data.work_phase) { + console.error(`No work phase found with ID: ${workPhaseId}`); + return null; + } + + return result.data.work_phase; + } catch (error) { + console.error(`Error fetching work phase with ID: ${workPhaseId}`, error); + return null; + } + } + async downloadWorkplan(workPhaseId: number) { return await http.PostRequest( Endpoints.Works.DOWNLOAD_WORK_PLAN + `?work_phase_id=${workPhaseId}`,