diff --git a/epictrack-api/migrations/versions/097d41841ba4_convert_staff_emails_to_lower_case.py b/epictrack-api/migrations/versions/097d41841ba4_convert_staff_emails_to_lower_case.py new file mode 100644 index 000000000..8cbdbc1f5 --- /dev/null +++ b/epictrack-api/migrations/versions/097d41841ba4_convert_staff_emails_to_lower_case.py @@ -0,0 +1,43 @@ +"""Convert staff emails to lower case + +Revision ID: 097d41841ba4 +Revises: 65cb91595d6a +Create Date: 2024-01-11 14:04:10.156478 + +""" +import sqlalchemy as sa +from alembic import op +from sqlalchemy.sql import column, table + + +# revision identifiers, used by Alembic. +revision = '097d41841ba4' +down_revision = '65cb91595d6a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + conn = op.get_bind() + staff_table = table('staffs', + column('id', sa.Integer), + column('name', sa.String), + column('phone',sa.String), + column('email', sa.String), + column('is_active', sa.Boolean), + column('position_id', sa.Integer) + ) + + conn.execute( + staff_table.update()\ + .values(email=sa.func.lower(staff_table.c.email)) + ) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/epictrack-api/migrations/versions/52b37bc04925_merging_097d41841ba4_and_09a79ca8e8fd.py b/epictrack-api/migrations/versions/52b37bc04925_merging_097d41841ba4_and_09a79ca8e8fd.py new file mode 100644 index 000000000..dbaa52f00 --- /dev/null +++ b/epictrack-api/migrations/versions/52b37bc04925_merging_097d41841ba4_and_09a79ca8e8fd.py @@ -0,0 +1,24 @@ +"""merging 097d41841ba4 and 09a79ca8e8fd + +Revision ID: 52b37bc04925 +Revises: 097d41841ba4, 09a79ca8e8fd +Create Date: 2024-01-15 10:02:26.499111 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '52b37bc04925' +down_revision = ('097d41841ba4', '09a79ca8e8fd') +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/epictrack-api/src/api/models/work_status.py b/epictrack-api/src/api/models/work_status.py index bf3bbcefc..708d154a6 100644 --- a/epictrack-api/src/api/models/work_status.py +++ b/epictrack-api/src/api/models/work_status.py @@ -14,7 +14,7 @@ """Model to handle all operations related to WorkStatus.""" from __future__ import annotations -from typing import List, Dict +from typing import Dict, List from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, desc from sqlalchemy.orm import relationship diff --git a/epictrack-api/src/api/schemas/request/staff_request.py b/epictrack-api/src/api/schemas/request/staff_request.py index 1e509de7b..c8bf2e6ad 100644 --- a/epictrack-api/src/api/schemas/request/staff_request.py +++ b/epictrack-api/src/api/schemas/request/staff_request.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Staff resource's input validations""" -from marshmallow import fields, validate +from marshmallow import fields, pre_load, validate from api.schemas.validators import Phone @@ -47,6 +47,14 @@ class StaffExistanceQueryParamSchema(RequestQueryParameterSchema): missing=None, ) + @pre_load + def convert_email_to_lower(self, data, **kwargs): # pylint: disable=unused-argument + """Converts staff email into lower case string""" + data = dict(data) + if "email" in data: + data["email"] = data["email"].lower() + return data + class StaffByPositionsQueryParamSchema(BasicRequestQueryParameterSchema): """Staff by positions query parameter""" @@ -88,10 +96,16 @@ class StaffBodyParameterSchema(RequestBodyParameterSchema): ) is_active = fields.Boolean( - metadata={"description": "Active status of the staff"}, - required=True + metadata={"description": "Active status of the staff"}, required=True ) + @pre_load + def convert_email_to_lower(self, data, **kwargs): # pylint: disable=unused-argument + """Converts staff email into lower case string""" + if "email" in data: + data["email"] = data["email"].lower() + return data + class StaffEmailPathParameterSchema(RequestPathParameterSchema): """Staff email path parameter schema""" @@ -101,3 +115,11 @@ class StaffEmailPathParameterSchema(RequestPathParameterSchema): validate=validate.Email(), required=True, ) + + @pre_load + def convert_email_to_lower(self, data, **kwargs): # pylint: disable=unused-argument + """Converts staff email into lower case string""" + data = dict(data) + if "email" in data: + data["email"] = data["email"].lower() + return data diff --git a/epictrack-api/src/api/schemas/request/task_request.py b/epictrack-api/src/api/schemas/request/task_request.py index 0f3a967b5..3719c8807 100644 --- a/epictrack-api/src/api/schemas/request/task_request.py +++ b/epictrack-api/src/api/schemas/request/task_request.py @@ -87,8 +87,7 @@ class TaskBodyParameterSchema(RequestBodyParameterSchema): tips = fields.Str( metadata={"description": "Practical info on why/how to do the task"}, - # validate=validate.Length(max=150), - required=True, + allow_none=True ) number_of_days = fields.Int( diff --git a/epictrack-api/src/api/schemas/request/work_request.py b/epictrack-api/src/api/schemas/request/work_request.py index 41022b0aa..5fe2c9b55 100644 --- a/epictrack-api/src/api/schemas/request/work_request.py +++ b/epictrack-api/src/api/schemas/request/work_request.py @@ -1,275 +1,275 @@ -# 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. -"""Work resource's input validations""" -from marshmallow import fields, validate - -from .base import RequestBodyParameterSchema, RequestPathParameterSchema, RequestQueryParameterSchema - - -class WorkBodyParameterSchema(RequestBodyParameterSchema): - """Work request body schema""" - - title = fields.Str( - metadata={"description": "Title of work"}, - validate=validate.Length(max=150), - required=True, - ) - - report_description = fields.Str( - metadata={"description": "Report description of work"}, - validate=validate.Length(max=500), - required=True, - ) - epic_description = fields.Str( - metadata={"description": "EPIC description of work"}, - validate=validate.Length(max=2000), - allow_none=True, - load_default=None, - ) - start_date = fields.DateTime( - metadata={"description": "Start date for the work"}, required=True - ) - - ea_act_id = fields.Int( - metadata={"description": "EA Act id of the work"}, - validate=validate.Range(min=1), - required=True, - ) - - work_type_id = fields.Int( - metadata={"description": "WorkType id of the work"}, - validate=validate.Range(min=1), - required=True, - ) - - project_id = fields.Int( - metadata={"description": "Project id of the work"}, - validate=validate.Range(min=1), - required=True, - ) - - ministry_id = fields.Int( - metadata={"description": "Ministry id of the work"}, - validate=validate.Range(min=1), - required=False, - ) - federal_involvement_id = fields.Int( - metadata={"description": "Federal involvement id of the work"}, - validate=validate.Range(min=1), - required=True, - ) - - substitution_act_id = fields.Int( - metadata={"description": "Substitution act id of the work"}, - validate=validate.Range(min=1), - allow_none=True, - ) - - eao_team_id = fields.Int( - metadata={"description": "EAO Team id of the work"}, - validate=validate.Range(min=1), - allow_none=True, - ) - - responsible_epd_id = fields.Int( - metadata={"description": "Responsible EPD id of the work"}, - validate=validate.Range(min=1), - allow_none=True, - ) - - work_lead_id = fields.Int( - metadata={"description": "Work lead id of the work"}, - validate=validate.Range(min=1), - allow_none=True, - ) - - decision_by_id = fields.Int( - metadata={"description": "Decision maker id of the work"}, - validate=validate.Range(min=1), - allow_none=True, - ) - - is_active = fields.Bool(metadata={"description": "Active state of the work"}) - is_high_priority = fields.Bool( - metadata={"description": "Is a high priority work"}, default=False - ) - is_cac_recommended = fields.Bool( - metadata={"description": "Is CAC recommended for the work"}, default=False - ) - - -class WorkExistenceQueryParamSchema(RequestQueryParameterSchema): - """Work existence check query parameters""" - - title = fields.Str( - metadata={"description": "Title of the work"}, - validate=validate.Length(max=150), - required=True, - ) - - work_id = fields.Int( - metadata={"description": "The id of the work"}, - validate=validate.Range(min=1), - load_default=None, - ) - - -class WorkIdPathParameterSchema(RequestPathParameterSchema): - """work id path parameter schema""" - - work_id = fields.Int( - metadata={"description": "The id of the work"}, - validate=validate.Range(min=1), - required=True, - ) - - -class WorkPlanDownloadQueryParamSchema(RequestQueryParameterSchema): - """Workplan download query parameters""" - - work_phase_id = fields.Int( - metadata={"description": "Id of work phase"}, - validate=validate.Range(min=1), - required=True, - ) - - -class WorkIdPhaseIdPathParameterSchema(RequestPathParameterSchema): - """Work id and phase id path parameter schema""" - - work_phase_id = fields.Int( - metadata={"description": "Work phase ID"}, - validate=validate.Range(min=1), - required=True, - ) - - -class WorkFirstNationNotesBodySchema(RequestBodyParameterSchema): - """Work first nation notes body parameter schema""" - - notes = fields.Str( - metadata={"description": "First nation notes"}, - validate=validate.Length(min=1), - required=True, - ) - - -class WorkTypeIdQueryParamSchema(RequestQueryParameterSchema): - """Work type id query parameters""" - - work_type_id = fields.Int( - metadata={"description": "The id of the work type"}, - validate=validate.Range(min=1), - load_default=None, - allow_none=True - ) - - -class WorkFirstNationImportBodyParamSchema(RequestBodyParameterSchema): - """Work First nation import body parameter schema""" - - indigenous_nation_ids = fields.List( - fields.Int(validate=validate.Range(min=1)), - metadata={"description": "Ids of the first nations"}, - required=True, - validate=validate.Length(min=1), - ) - - -class WorkStatusParameterSchema(RequestBodyParameterSchema): - """Work status request body schema""" - - description = fields.Str( - metadata={"description": "description of status"}, - validate=validate.Length(max=500), - required=True, - ) - - notes = fields.Str( - metadata={"description": "Notes for the work status "}, - allow_none=True - ) - - posted_date = fields.DateTime( - metadata={"description": "posted date for the work status"}, required=False - ) - - -class WorkIssuesParameterSchema(RequestBodyParameterSchema): - """Work issues request body schema""" - - title = fields.Str( - metadata={"description": "Title Of the issue"}, - validate=validate.Length(max=50), - required=True, - ) - - is_active: bool = fields.Bool( - default=True, - description="Flag indicating whether the issue is active", - ) - - is_high_priority: bool = fields.Bool( - default=False, - description="Flag indicating whether the issue is of high priority", - ) - - start_date = fields.DateTime( - metadata={"description": "Start date for the issue"}, required=False - ) - - expected_resolution_date = fields.DateTime( - metadata={"description": "Expected Resolution date for the issue"}, required=False - ) - - -class WorkIssuesCreateParameterSchema(WorkIssuesParameterSchema): - """Work issues create request body schema""" - - updates = fields.List( - fields.Str, - metadata={"description": "List of updates for the issue"}, - required=False, - ) - - -class WorkIssuesUpdateSchema(RequestBodyParameterSchema): - """Work status update request body schema for PUT requests""" - - id = fields.Int( - metadata={"description": "ID of the update"}, - required=True - ) - - description = fields.Str( - metadata={"description": "Description of the update"}, - validate=validate.Length(max=500), - required=True - ) - - -class WorkNotesBodySchema(RequestBodyParameterSchema): - """Work notes body parameter schema""" - - notes = fields.Str( - metadata={"description": "Work status notes"}, - validate=validate.Length(min=1), - required=True, - ) - - note_type = fields.Str( - metadata={"description": "Type of work status notes"}, - validate=validate.OneOf(['status_notes', 'issue_notes']), # Add your predefined types - required=True, - ) +# 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. +"""Work resource's input validations""" +from marshmallow import fields, validate + +from .base import RequestBodyParameterSchema, RequestPathParameterSchema, RequestQueryParameterSchema + + +class WorkBodyParameterSchema(RequestBodyParameterSchema): + """Work request body schema""" + + title = fields.Str( + metadata={"description": "Title of work"}, + validate=validate.Length(max=150), + required=True, + ) + + report_description = fields.Str( + metadata={"description": "Report description of work"}, + validate=validate.Length(max=500), + required=True, + ) + epic_description = fields.Str( + metadata={"description": "EPIC description of work"}, + validate=validate.Length(max=2000), + allow_none=True, + load_default=None, + ) + start_date = fields.DateTime( + metadata={"description": "Start date for the work"}, required=True + ) + + ea_act_id = fields.Int( + metadata={"description": "EA Act id of the work"}, + validate=validate.Range(min=1), + required=True, + ) + + work_type_id = fields.Int( + metadata={"description": "WorkType id of the work"}, + validate=validate.Range(min=1), + required=True, + ) + + project_id = fields.Int( + metadata={"description": "Project id of the work"}, + validate=validate.Range(min=1), + required=True, + ) + + ministry_id = fields.Int( + metadata={"description": "Ministry id of the work"}, + validate=validate.Range(min=1), + required=False, + ) + federal_involvement_id = fields.Int( + metadata={"description": "Federal involvement id of the work"}, + validate=validate.Range(min=1), + required=True, + ) + + substitution_act_id = fields.Int( + metadata={"description": "Substitution act id of the work"}, + validate=validate.Range(min=1), + allow_none=True, + ) + + eao_team_id = fields.Int( + metadata={"description": "EAO Team id of the work"}, + validate=validate.Range(min=1), + allow_none=True, + ) + + responsible_epd_id = fields.Int( + metadata={"description": "Responsible EPD id of the work"}, + validate=validate.Range(min=1), + allow_none=True, + ) + + work_lead_id = fields.Int( + metadata={"description": "Work lead id of the work"}, + validate=validate.Range(min=1), + allow_none=True, + ) + + decision_by_id = fields.Int( + metadata={"description": "Decision maker id of the work"}, + validate=validate.Range(min=1), + allow_none=True, + ) + + is_active = fields.Bool(metadata={"description": "Active state of the work"}) + is_high_priority = fields.Bool( + metadata={"description": "Is a high priority work"}, default=False + ) + is_cac_recommended = fields.Bool( + metadata={"description": "Is CAC recommended for the work"}, default=False + ) + + +class WorkExistenceQueryParamSchema(RequestQueryParameterSchema): + """Work existence check query parameters""" + + title = fields.Str( + metadata={"description": "Title of the work"}, + validate=validate.Length(max=150), + required=True, + ) + + work_id = fields.Int( + metadata={"description": "The id of the work"}, + validate=validate.Range(min=1), + load_default=None, + ) + + +class WorkIdPathParameterSchema(RequestPathParameterSchema): + """work id path parameter schema""" + + work_id = fields.Int( + metadata={"description": "The id of the work"}, + validate=validate.Range(min=1), + required=True, + ) + + +class WorkPlanDownloadQueryParamSchema(RequestQueryParameterSchema): + """Workplan download query parameters""" + + work_phase_id = fields.Int( + metadata={"description": "Id of work phase"}, + validate=validate.Range(min=1), + required=True, + ) + + +class WorkIdPhaseIdPathParameterSchema(RequestPathParameterSchema): + """Work id and phase id path parameter schema""" + + work_phase_id = fields.Int( + metadata={"description": "Work phase ID"}, + validate=validate.Range(min=1), + required=True, + ) + + +class WorkFirstNationNotesBodySchema(RequestBodyParameterSchema): + """Work first nation notes body parameter schema""" + + notes = fields.Str( + metadata={"description": "First nation notes"}, + validate=validate.Length(min=1), + required=True, + ) + + +class WorkTypeIdQueryParamSchema(RequestQueryParameterSchema): + """Work type id query parameters""" + + work_type_id = fields.Int( + metadata={"description": "The id of the work type"}, + validate=validate.Range(min=1), + load_default=None, + allow_none=True + ) + + +class WorkFirstNationImportBodyParamSchema(RequestBodyParameterSchema): + """Work First nation import body parameter schema""" + + indigenous_nation_ids = fields.List( + fields.Int(validate=validate.Range(min=1)), + metadata={"description": "Ids of the first nations"}, + required=True, + validate=validate.Length(min=1), + ) + + +class WorkStatusParameterSchema(RequestBodyParameterSchema): + """Work status request body schema""" + + description = fields.Str( + metadata={"description": "description of status"}, + validate=validate.Length(max=500), + required=True, + ) + + notes = fields.Str( + metadata={"description": "Notes for the work status "}, + allow_none=True + ) + + posted_date = fields.DateTime( + metadata={"description": "posted date for the work status"}, required=False + ) + + +class WorkIssuesParameterSchema(RequestBodyParameterSchema): + """Work issues request body schema""" + + title = fields.Str( + metadata={"description": "Title Of the issue"}, + validate=validate.Length(max=50), + required=True, + ) + + is_active: bool = fields.Bool( + default=True, + description="Flag indicating whether the issue is active", + ) + + is_high_priority: bool = fields.Bool( + default=False, + description="Flag indicating whether the issue is of high priority", + ) + + start_date = fields.DateTime( + metadata={"description": "Start date for the issue"}, required=False + ) + + expected_resolution_date = fields.DateTime( + metadata={"description": "Expected Resolution date for the issue"}, required=False + ) + + +class WorkIssuesCreateParameterSchema(WorkIssuesParameterSchema): + """Work issues create request body schema""" + + updates = fields.List( + fields.Str, + metadata={"description": "List of updates for the issue"}, + required=False, + ) + + +class WorkIssuesUpdateSchema(RequestBodyParameterSchema): + """Work status update request body schema for PUT requests""" + + id = fields.Int( + metadata={"description": "ID of the update"}, + required=True + ) + + description = fields.Str( + metadata={"description": "Description of the update"}, + validate=validate.Length(max=500), + required=True + ) + + +class WorkNotesBodySchema(RequestBodyParameterSchema): + """Work notes body parameter schema""" + + notes = fields.Str( + metadata={"description": "Work status notes"}, + validate=validate.Length(min=1), + required=True, + ) + + note_type = fields.Str( + metadata={"description": "Type of work status notes"}, + validate=validate.OneOf(['status_notes', 'issue_notes']), # Add your predefined types + required=True, + ) diff --git a/epictrack-api/src/api/services/event.py b/epictrack-api/src/api/services/event.py index 78c4758b0..187a39674 100644 --- a/epictrack-api/src/api/services/event.py +++ b/epictrack-api/src/api/services/event.py @@ -22,18 +22,8 @@ from api.actions.action_handler import ActionHandler from api.exceptions import ResourceNotFoundError, UnprocessableEntityError from api.models import ( - PRIMARY_CATEGORIES, - CalendarEvent, - Event, - EventCategoryEnum, - EventConfiguration, - EventTypeEnum, - Work, - WorkCalendarEvent, - WorkPhase, - WorkStateEnum, - db, -) + PRIMARY_CATEGORIES, CalendarEvent, Event, EventCategoryEnum, EventConfiguration, EventTypeEnum, Work, + WorkCalendarEvent, WorkPhase, WorkStateEnum, db) from api.models.action import Action, ActionEnum from api.models.action_configuration import ActionConfiguration from api.models.event_template import EventPositionEnum @@ -42,13 +32,14 @@ from api.models.work_type import WorkType from api.services.outcome_configuration import OutcomeConfigurationService from api.utils import util -from . import authorisation +from ..utils.roles import Membership +from ..utils.roles import Role as KeycloakRole +from . import authorisation from .event_configuration import EventConfigurationService -from ..utils.roles import Membership, Role as KeycloakRole -# pylint:disable=not-callable +# pylint:disable=not-callable, too-many-lines class EventService: diff --git a/epictrack-api/src/api/services/staff.py b/epictrack-api/src/api/services/staff.py index 05a5f8dc1..6195ac1f3 100644 --- a/epictrack-api/src/api/services/staff.py +++ b/epictrack-api/src/api/services/staff.py @@ -121,6 +121,7 @@ def import_staffs(cls, file: IO): username = TokenInfo.get_username() data["created_by"] = username data = data.to_dict("records") + data["email"] = data["email"].lower() db.session.bulk_insert_mappings(Staff, data) db.session.commit() return "Inserted successfully" diff --git a/epictrack-api/src/api/services/task_template.py b/epictrack-api/src/api/services/task_template.py index 68bedd238..744899e4a 100644 --- a/epictrack-api/src/api/services/task_template.py +++ b/epictrack-api/src/api/services/task_template.py @@ -14,6 +14,7 @@ """Service to manage Task Templates.""" from typing import IO, List +import numpy as np import pandas as pd from flask import current_app from sqlalchemy.sql import exists @@ -47,6 +48,8 @@ def create_task_template(cls, data: dict, template_file: IO) -> TaskTemplate: {"responsibility_id": res.id}, regex=True, ) + task_data = task_data.replace({np.nan: None}) + task_data = task_data.replace({np.NaN: None}) tasks = task_data.to_dict("records") cls.create_bulk_tasks(tasks) TaskTemplate.commit() diff --git a/epictrack-web/src/components/workPlan/issues/Forms/CreateIssue.tsx b/epictrack-web/src/components/workPlan/issues/Forms/CreateIssue.tsx index 6189e9d60..531b59b9c 100644 --- a/epictrack-web/src/components/workPlan/issues/Forms/CreateIssue.tsx +++ b/epictrack-web/src/components/workPlan/issues/Forms/CreateIssue.tsx @@ -1,165 +1,165 @@ -import React from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import * as yup from "yup"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { Box, FormControlLabel, Grid, Stack, Tooltip } from "@mui/material"; -import ControlledTextField from "../../../shared/controlledInputComponents/ControlledTextField"; -import { ETFormLabelWithCharacterLimit, ETParagraph } from "../../../shared"; -import ControlledSwitch from "../../../shared/controlledInputComponents/ControlledSwitch"; -import { IssuesContext } from "../IssuesContext"; -import { IconProps } from "../../../icons/type"; -import Icons from "../../../icons"; -import { CreateIssueForm } from "../types"; -import moment from "moment"; -import ControlledDatePicker from "../../../shared/controlledInputComponents/ControlledDatePicker"; - -const InfoIcon: React.FC = Icons["InfoIcon"]; - -const CreateIssue = () => { - const { setCreateIssueFormIsOpen, addIssue } = - React.useContext(IssuesContext); - - const schema = yup.object().shape({ - title: yup.string().required("Title is required"), - description: yup.string().required("Description is required"), - is_active: yup.boolean(), - is_high_priority: yup.boolean(), - start_date: yup.string().required("Start date is required"), - expected_resolution_date: yup.string().nullable(), - }); - - const methods = useForm({ - resolver: yupResolver(schema), - defaultValues: { - title: "", - description: "", - is_active: true, - is_high_priority: false, - start_date: "", - expected_resolution_date: "", - }, - mode: "onSubmit", - }); - - const { handleSubmit, watch } = methods; - - const watchedTitle = watch("title"); - const titleCharacterLimit = 50; - const watchedDescription = watch("description"); - const descriptionCharacterLimit = 250; - - const onSubmitHandler = async (data: CreateIssueForm) => { - const { - title, - description, - start_date, - expected_resolution_date, - is_active, - is_high_priority, - } = await schema.validate(data); - - const dataToBeSubmitted = { - title, - description, - start_date: moment(start_date).format(), - expected_resolution_date: expected_resolution_date - ? moment(expected_resolution_date).format() - : undefined, - is_active: Boolean(is_active), - is_high_priority: Boolean(is_high_priority), - }; - - addIssue(dataToBeSubmitted); - setCreateIssueFormIsOpen(false); - }; - - return ( - - - - - Title - - - - - - Description - - - - - - } - label={ - - Active - - - - - - - } - /> - } - label={ - - High Priority - - - - - - - } - /> - - - - Start Date - - - - Expected Resolution Date - - - - - ); -}; - -export default CreateIssue; +import React from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import * as yup from "yup"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Box, FormControlLabel, Grid, Stack, Tooltip } from "@mui/material"; +import ControlledTextField from "../../../shared/controlledInputComponents/ControlledTextField"; +import { ETFormLabelWithCharacterLimit, ETParagraph } from "../../../shared"; +import ControlledSwitch from "../../../shared/controlledInputComponents/ControlledSwitch"; +import { IssuesContext } from "../IssuesContext"; +import { IconProps } from "../../../icons/type"; +import Icons from "../../../icons"; +import { CreateIssueForm } from "../types"; +import moment from "moment"; +import ControlledDatePicker from "../../../shared/controlledInputComponents/ControlledDatePicker"; + +const InfoIcon: React.FC = Icons["InfoIcon"]; + +const CreateIssue = () => { + const { setCreateIssueFormIsOpen, addIssue } = + React.useContext(IssuesContext); + + const schema = yup.object().shape({ + title: yup.string().required("Title is required"), + description: yup.string().required("Description is required"), + is_active: yup.boolean(), + is_high_priority: yup.boolean(), + start_date: yup.string().required("Start date is required"), + expected_resolution_date: yup.string().nullable(), + }); + + const methods = useForm({ + resolver: yupResolver(schema), + defaultValues: { + title: "", + description: "", + is_active: true, + is_high_priority: false, + start_date: "", + expected_resolution_date: "", + }, + mode: "onSubmit", + }); + + const { handleSubmit, watch } = methods; + + const watchedTitle = watch("title"); + const titleCharacterLimit = 50; + const watchedDescription = watch("description"); + const descriptionCharacterLimit = 250; + + const onSubmitHandler = async (data: CreateIssueForm) => { + const { + title, + description, + start_date, + expected_resolution_date, + is_active, + is_high_priority, + } = await schema.validate(data); + + const dataToBeSubmitted = { + title, + description, + start_date: moment(start_date).format(), + expected_resolution_date: expected_resolution_date + ? moment(expected_resolution_date).format() + : undefined, + is_active: Boolean(is_active), + is_high_priority: Boolean(is_high_priority), + }; + + addIssue(dataToBeSubmitted); + setCreateIssueFormIsOpen(false); + }; + + return ( + + + + + Title + + + + + + Description + + + + + + } + label={ + + Active + + + + + + + } + /> + } + label={ + + High Priority + + + + + + + } + /> + + + + Start Date + + + + Expected Resolution Date + + + + + ); +}; + +export default CreateIssue; diff --git a/epictrack-web/src/components/workPlan/status/StatusForm.tsx b/epictrack-web/src/components/workPlan/status/StatusForm.tsx index 14513a23a..e2c19ded3 100644 --- a/epictrack-web/src/components/workPlan/status/StatusForm.tsx +++ b/epictrack-web/src/components/workPlan/status/StatusForm.tsx @@ -16,6 +16,7 @@ const schema = yup.object().shape({ posted_date: yup.string().required("Date is required"), description: yup.string().required("Description is required"), }); + const CHARACTER_LIMIT = 500; const StatusForm = () => {