diff --git a/epictrack-api/src/api/resources/__init__.py b/epictrack-api/src/api/resources/__init__.py index 0b743c669..bf6f364fe 100644 --- a/epictrack-api/src/api/resources/__init__.py +++ b/epictrack-api/src/api/resources/__init__.py @@ -49,6 +49,7 @@ from .event_configuration import API as EVENT_CONFIGURATION_API from .responsibility import API as RESPONSIBILITY_API from .outcome_configuration import API as OUTCOME_CONFIGURATION_API +from .act_section import API as ACT_SECTION_API __all__ = ("API_BLUEPRINT", "OPS_BLUEPRINT") @@ -103,3 +104,4 @@ API.add_namespace(EVENT_CONFIGURATION_API, path="/event-configurations") API.add_namespace(RESPONSIBILITY_API, path="/responsibilities") API.add_namespace(OUTCOME_CONFIGURATION_API, path="/outcome-configurations") +API.add_namespace(ACT_SECTION_API, path="/act-sections") diff --git a/epictrack-api/src/api/resources/act_section.py b/epictrack-api/src/api/resources/act_section.py new file mode 100644 index 000000000..1a1112794 --- /dev/null +++ b/epictrack-api/src/api/resources/act_section.py @@ -0,0 +1,45 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Resource for Act Section endpoints.""" +from http import HTTPStatus + +from flask import jsonify, request +from flask_restx import Namespace, Resource, cors + +from api.services import ActSectionService +from api.utils import auth, constants, profiletime +from api.utils.caching import AppCache +from api.utils.util import cors_preflight + +from api.schemas import request as req +from api.schemas import response as res + +API = Namespace("act-sections", description="ActSections") + + +@cors_preflight("GET") +@API.route("", methods=["GET", "OPTIONS"]) +class ActSections(Resource): + """Endpoint resource to return sub types based on type id""" + + @staticmethod + @cors.crossdomain(origin="*") + @auth.require + @profiletime + @AppCache.cache.cached(timeout=constants.CACHE_DAY_TIMEOUT, query_string=True) + def get(): + """Return all sub_types based on type_id.""" + args = req.ActSectionQueryParameterSchema().load(request.args) + act_sections = ActSectionService.find_by_ea_act(args) + return jsonify(res.ActSectionResponseSchema(many=True).dump(act_sections)), HTTPStatus.OK diff --git a/epictrack-api/src/api/schemas/request/__init__.py b/epictrack-api/src/api/schemas/request/__init__.py index d1d666e9c..efced20c4 100644 --- a/epictrack-api/src/api/schemas/request/__init__.py +++ b/epictrack-api/src/api/schemas/request/__init__.py @@ -49,3 +49,4 @@ WorkBodyParameterSchema, WorkExistenceQueryParamSchema, WorkFirstNationImportBodyParamSchema, WorkFirstNationNotesBodySchema, WorkIdPathParameterSchema, WorkIdPhaseIdPathParameterSchema, WorkPlanDownloadQueryParamSchema, WorkTypeIdQueryParamSchema) +from .act_section_request import ActSectionQueryParameterSchema diff --git a/epictrack-api/src/api/schemas/request/act_section_request.py b/epictrack-api/src/api/schemas/request/act_section_request.py new file mode 100644 index 000000000..82da0d7a6 --- /dev/null +++ b/epictrack-api/src/api/schemas/request/act_section_request.py @@ -0,0 +1,26 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Act Sections resource's input validations""" +from marshmallow import fields, validate + +from .base import BasicRequestQueryParameterSchema + + +class ActSectionQueryParameterSchema(BasicRequestQueryParameterSchema): + """Type id path parameter schema""" + + ea_act_id = fields.Int( + metadata={"description": "The id of the EA Act"}, + validate=validate.Range(min=1) + ) diff --git a/epictrack-api/src/api/schemas/response/__init__.py b/epictrack-api/src/api/schemas/response/__init__.py index 1e2438c6d..6c4762894 100644 --- a/epictrack-api/src/api/schemas/response/__init__.py +++ b/epictrack-api/src/api/schemas/response/__init__.py @@ -33,3 +33,4 @@ WorkPhaseResponseSchema, WorkPhaseSkeletonResponseSchema, WorkPhaseTemplateAvailableResponse, WorkResourceResponseSchema, WorkResponseSchema, WorkStaffRoleReponseSchema) from .outcome_configuration_response import OutcomeConfigurationResponseSchema +from .act_section_response import ActSectionResponseSchema diff --git a/epictrack-api/src/api/schemas/response/act_section_response.py b/epictrack-api/src/api/schemas/response/act_section_response.py new file mode 100644 index 000000000..73d2b0f8f --- /dev/null +++ b/epictrack-api/src/api/schemas/response/act_section_response.py @@ -0,0 +1,17 @@ +"""ActSection model schema""" +from marshmallow import EXCLUDE + +from api.models import ActSection +from api.schemas.base import AutoSchemaBase + + +class ActSectionResponseSchema( + AutoSchemaBase +): # pylint: disable=too-many-ancestors,too-few-public-methods + """Type model schema class""" + + class Meta(AutoSchemaBase.Meta): + """Meta information""" + + model = ActSection + unknown = EXCLUDE diff --git a/epictrack-api/src/api/services/__init__.py b/epictrack-api/src/api/services/__init__.py index 06cee8d54..17be8a56e 100644 --- a/epictrack-api/src/api/services/__init__.py +++ b/epictrack-api/src/api/services/__init__.py @@ -34,3 +34,4 @@ from .work import WorkService from .work_phase import WorkPhaseService from .action_template import ActionTemplateService +from .act_section import ActSectionService diff --git a/epictrack-api/src/api/services/act_section.py b/epictrack-api/src/api/services/act_section.py new file mode 100644 index 000000000..ce74e5f08 --- /dev/null +++ b/epictrack-api/src/api/services/act_section.py @@ -0,0 +1,28 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Service to manage ActSection.""" +from flask import current_app + +from api.models import ActSection + + +class ActSectionService: # pylint:disable=too-few-public-methods + """Service to manage sub type related operations""" + + @classmethod + def find_by_ea_act(cls, args: dict): + """Find sub types by type_id""" + current_app.logger.debug(f"find act sections by params {args}") + act_sections = ActSection.find_by_params(args) + return act_sections diff --git a/epictrack-api/src/api/utils/auth.py b/epictrack-api/src/api/utils/auth.py index 1b189e7cf..142c2a066 100644 --- a/epictrack-api/src/api/utils/auth.py +++ b/epictrack-api/src/api/utils/auth.py @@ -3,7 +3,7 @@ from functools import wraps from http import HTTPStatus -from flask import current_app, g, request +from flask import g, request from flask_jwt_oidc import JwtManager from ..exceptions import BusinessError @@ -22,7 +22,7 @@ def require(cls, f): @wraps(f) def decorated(*args, **kwargs): g.authorization_header = request.headers.get("Authorization", None) - current_app.logger.info(f"AUTH HEADER {g.authorization_header}") + # current_app.logger.info(f"AUTH HEADER {g.authorization_header}") g.token_info = g.jwt_oidc_token_info return f(*args, **kwargs) diff --git a/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx b/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx index 8d325add6..c4c175e64 100644 --- a/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx +++ b/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx @@ -8,10 +8,15 @@ import ControlledSelectV2 from "../../../shared/controlledInputComponents/Contro import { Controller, useFormContext } from "react-hook-form"; import { ListType } from "../../../../models/code"; import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; -import { DATE_FORMAT } from "../../../../constants/application-constant"; +import { + COMMON_ERROR_MESSAGE, + DATE_FORMAT, +} from "../../../../constants/application-constant"; import { WorkplanContext } from "../../WorkPlanContext"; import { dateUtils } from "../../../../utils"; import { SyntheticEvent } from "react-draft-wysiwyg"; +import actSectionService from "../../../../services/actSectionService/actSectionService"; +import { showNotification } from "../../../shared/notificationProvider"; interface ExtensionInputProps { isFormFieldsLocked: boolean; @@ -49,9 +54,24 @@ const ExtensionInput = (props: ExtensionInputProps) => { "phase_end_date", Moment(ctx.selectedWorkPhase?.end_date).add(numberOfDays, "days").format() ); + getActSections(); }, []); const numberOfDaysRef = React.useRef(); const endDateRef = React.useRef(); + const getActSections = async () => { + try { + const result = await actSectionService.getActSectionsByEaAct( + ctx.work?.ea_act_id + ); + if (result.status === 200) { + setActSections(result.data as ListType[]); + } + } catch (e) { + showNotification(COMMON_ERROR_MESSAGE, { + type: "error", + }); + } + }; const onDayChange = (event: SyntheticEvent) => { if (endDateRef.current as any) { setValue( @@ -140,8 +160,8 @@ const ExtensionInput = (props: ExtensionInputProps) => { disabled={props.isFormFieldsLocked} helperText={errors?.act_section_id?.message?.toString()} options={actSections || []} - getOptionValue={(o: ListType) => o.id.toString()} - getOptionLabel={(o: ListType) => o.name} + getOptionValue={(o: ListType) => o?.id.toString()} + getOptionLabel={(o: ListType) => o?.name} {...register("act_section_id")} > diff --git a/epictrack-web/src/components/workPlan/task/TaskForm.tsx b/epictrack-web/src/components/workPlan/task/TaskForm.tsx index 4df6192b1..e8d7798be 100644 --- a/epictrack-web/src/components/workPlan/task/TaskForm.tsx +++ b/epictrack-web/src/components/workPlan/task/TaskForm.tsx @@ -149,7 +149,7 @@ const TaskForm = ({ start_date: Moment(data.start_date).format(), number_of_days: data.number_of_days.toString() === "" ? 0 : data.number_of_days, - notes: notes, + notes: notes ?? "", }; await saveTask(dataToSave); diff --git a/epictrack-web/src/constants/api-endpoint.ts b/epictrack-web/src/constants/api-endpoint.ts index b22539cc7..43b98e68e 100644 --- a/epictrack-web/src/constants/api-endpoint.ts +++ b/epictrack-web/src/constants/api-endpoint.ts @@ -64,5 +64,8 @@ const Endpoints = { OutcomeConfigurations: { CONFIGURATIONS: "/outcome-configurations", }, + ActSections: { + ACT_SECTIONS: "/act-sections", + }, }; export default Endpoints; diff --git a/epictrack-web/src/services/actSectionService/actSectionService.ts b/epictrack-web/src/services/actSectionService/actSectionService.ts new file mode 100644 index 000000000..af45d1b20 --- /dev/null +++ b/epictrack-web/src/services/actSectionService/actSectionService.ts @@ -0,0 +1,12 @@ +import Endpoints from "../../constants/api-endpoint"; +import http from "../../apiManager/http-request-handler"; + +class ActSectionService { + async getActSectionsByEaAct(eaActId?: number) { + return await http.GetRequest( + Endpoints.ActSections.ACT_SECTIONS + `?ea_act_id=${eaActId}` + ); + } +} + +export default new ActSectionService();