diff --git a/epictrack-api/migrations/versions/449afdbe4d0d_state_enums_for_work_and_project.py b/epictrack-api/migrations/versions/449afdbe4d0d_state_enums_for_work_and_project.py new file mode 100644 index 000000000..2e6ae09f9 --- /dev/null +++ b/epictrack-api/migrations/versions/449afdbe4d0d_state_enums_for_work_and_project.py @@ -0,0 +1,53 @@ +"""state enums for work and project + +Revision ID: 449afdbe4d0d +Revises: cff39cd2d471 +Create Date: 2023-10-24 20:43:26.313149 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '449afdbe4d0d' +down_revision = 'cff39cd2d471' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("UPDATE event_types set name='Interregnum', event_category_id=8 where id = 11") + op.execute("CREATE TYPE projectstateenum AS ENUM('UNDER_EAC_ASSESSMENT', 'UNDER_EXEMPTION_REQUEST', 'UNDER_AMENDMENT', 'UNDER_DISPUTE_RESOLUTION', 'PRE_CONSTRUCTION', 'CONSTRUCTION', 'OPERATION', 'CARE_AND_MAINTENANCE', 'DECOMMISSION', 'UNKNOWN')") + op.execute("CREATE TYPE workstateenum AS ENUM('SUSPENDED', 'IN_PROGRESS', 'WITHDRAWN', 'TERMINATED', 'CLOSED', 'COMPLETED')") + with op.batch_alter_table('projects', schema=None) as batch_op: + batch_op.add_column(sa.Column('project_state', sa.Enum('UNDER_EAC_ASSESSMENT', 'UNDER_EXEMPTION_REQUEST', 'UNDER_AMENDMENT', 'UNDER_DISPUTE_RESOLUTION', 'PRE_CONSTRUCTION', 'CONSTRUCTION', 'OPERATION', 'CARE_AND_MAINTENANCE', 'DECOMMISSION', 'UNKNOWN', name='projectstateenum'), nullable=True)) + + with op.batch_alter_table('projects_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('project_state', sa.Enum('UNDER_EAC_ASSESSMENT', 'UNDER_EXEMPTION_REQUEST', 'UNDER_AMENDMENT', 'UNDER_DISPUTE_RESOLUTION', 'PRE_CONSTRUCTION', 'CONSTRUCTION', 'OPERATION', 'CARE_AND_MAINTENANCE', 'DECOMMISSION', 'UNKNOWN', name='projectstateenum'), autoincrement=False, nullable=True)) + + with op.batch_alter_table('works', schema=None) as batch_op: + batch_op.add_column(sa.Column('work_state', sa.Enum('SUSPENDED', 'IN_PROGRESS', 'WITHDRAWN', 'TERMINATED', 'CLOSED', 'COMPLETED', name='workstateenum'), nullable=True)) + + with op.batch_alter_table('works_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('work_state', sa.Enum('SUSPENDED', 'IN_PROGRESS', 'WITHDRAWN', 'TERMINATED', 'CLOSED', 'COMPLETED', name='workstateenum'), autoincrement=False, nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('works_history', schema=None) as batch_op: + batch_op.drop_column('work_state') + + with op.batch_alter_table('works', schema=None) as batch_op: + batch_op.drop_column('work_state') + + with op.batch_alter_table('projects_history', schema=None) as batch_op: + batch_op.drop_column('project_state') + + with op.batch_alter_table('projects', schema=None) as batch_op: + batch_op.drop_column('project_state') + + # ### end Alembic commands ### diff --git a/epictrack-api/migrations/versions/4b5f3a264271_act_sections.py b/epictrack-api/migrations/versions/4b5f3a264271_act_sections.py new file mode 100644 index 000000000..1667b1677 --- /dev/null +++ b/epictrack-api/migrations/versions/4b5f3a264271_act_sections.py @@ -0,0 +1,44 @@ +"""act_sections + +Revision ID: 4b5f3a264271 +Revises: 449afdbe4d0d +Create Date: 2023-10-24 21:24:14.502652 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4b5f3a264271' +down_revision = '449afdbe4d0d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('act_sections', schema=None) as batch_op: + batch_op.add_column(sa.Column('ea_act_id', sa.Integer(), nullable=False)) + batch_op.create_foreign_key(None, 'ea_acts', ['ea_act_id'], ['id']) + + with op.batch_alter_table('act_sections_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('ea_act_id', sa.Integer(), autoincrement=False, nullable=False)) + batch_op.create_foreign_key(None, 'ea_acts', ['ea_act_id'], ['id']) + for sort_order, act in enumerate(["Time Limit Extension 38(1)(a)", "Time Limit Suspension 45(1)"]): + op.execute(f"INSERT INTO act_sections(name, ea_act_id, sort_order)VALUES('{act}',3,{sort_order + 1})") + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('act_sections_history', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('ea_act_id') + + with op.batch_alter_table('act_sections', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('ea_act_id') + + # ### end Alembic commands ### diff --git a/epictrack-api/migrations/versions/cff39cd2d471_event_types_event_categories_actions.py b/epictrack-api/migrations/versions/cff39cd2d471_event_types_event_categories_actions.py new file mode 100644 index 000000000..27eef7f02 --- /dev/null +++ b/epictrack-api/migrations/versions/cff39cd2d471_event_types_event_categories_actions.py @@ -0,0 +1,60 @@ +"""event_types, event_categories, actions + +Revision ID: cff39cd2d471 +Revises: eed10a65e3c3 +Create Date: 2023-10-24 17:59:01.979152 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cff39cd2d471' +down_revision = 'eed10a65e3c3' +branch_labels = None +depends_on = None + +actions = [ + "SetEventDate", + "AddEvent", + "SetPhasesStatus", + "SetEventsStatus", + "SetWorkState", + "SetProjectStatus", + "LockWorkStartDate", + "SetWorkDecisionMaker", + "AddPhase", + "SetPhaseLegislation", + "CreateWork" +] + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('work_phases', schema=None) as batch_op: + batch_op.add_column(sa.Column('legislated', sa.Boolean(), nullable=True)) + + with op.batch_alter_table('work_phases_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('legislated', sa.Boolean(), autoincrement=False, nullable=True)) + op.execute("TRUNCATE actions RESTART IDENTITY CASCADE") + for action in actions: + op.execute(f"INSERT INTO actions(name) VALUES('{action}')") + op.execute("INSERT INTO event_categories(name, sort_order)VALUES('Special Extension',8)") + op.execute("UPDATE event_types set name='PCP Time Limit Extension' where id = 25") + op.execute("alter sequence event_types_id_seq restart with 37") + op.execute("alter sequence event_categories_id_seq restart with 8") + op.execute("INSERT INTO event_types(name, event_category_id, sort_order) VALUES ('Date Capture', 1, 10)") + op.execute("INSERT INTO event_types(name, event_category_id, sort_order) VALUES ('Time Limit Resumption', 3, 2)") + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('work_phases_history', schema=None) as batch_op: + batch_op.drop_column('legislated') + + with op.batch_alter_table('work_phases', schema=None) as batch_op: + batch_op.drop_column('legislated') + + # ### end Alembic commands ### diff --git a/epictrack-api/src/api/models/act_section.py b/epictrack-api/src/api/models/act_section.py index 46f2beb66..9da504aa7 100644 --- a/epictrack-api/src/api/models/act_section.py +++ b/epictrack-api/src/api/models/act_section.py @@ -13,7 +13,9 @@ # limitations under the License. """Model to handle all operations related to act sections.""" -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, ForeignKey + +from sqlalchemy.orm import relationship from .code_table import CodeTableVersioned from .db import db @@ -26,4 +28,7 @@ class ActSection(db.Model, CodeTableVersioned): id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String()) + ea_act_id = Column(ForeignKey('ea_acts.id'), nullable=False) sort_order = Column(Integer, nullable=False) + + ea_act = relationship('EAAct', foreign_keys=[ea_act_id], lazy='select') diff --git a/epictrack-api/src/api/models/action.py b/epictrack-api/src/api/models/action.py index ec7ead214..4966b5b8c 100644 --- a/epictrack-api/src/api/models/action.py +++ b/epictrack-api/src/api/models/action.py @@ -22,14 +22,17 @@ class ActionEnum(enum.Enum): """Action enum""" - COMPLETE_CURRENT_PHASE = 1 - DISABLE_WORK_START_DATE = 2 - CLOSE_EVERYTHING = 3 - DUPLICATE_PHASE = 4 - DEACTIVATE_ALL_EVENTS = 5 - DELETE_ALL_EVENTS = 6 - CLOSE_WORK = 7 - CREATE_NEW_WORK = 8 + SET_EVENT_DATE = 1 + ADD_EVENT = 2 + SET_PHASES_STATUS = 3 + SET_EVENTS_STATUS = 4 + SET_WORK_STATE = 5 + SET_PROJECT_STATUS = 6 + LOCK_WORK_START_DATE = 7 + SET_WORK_DECISION_MAKER = 8 + ADD_PHASE = 9 + SET_PHASE_LEGISLATION = 10 + CREATE_WORK = 11 class Action(db.Model, CodeTableVersioned): diff --git a/epictrack-api/src/api/models/event_category.py b/epictrack-api/src/api/models/event_category.py index 7911940da..dc14a562c 100644 --- a/epictrack-api/src/api/models/event_category.py +++ b/epictrack-api/src/api/models/event_category.py @@ -29,6 +29,7 @@ class EventCategoryEnum(enum.Enum): PCP = 5 CALENDAR = 6 FINANCE = 7 + SPECIAL_EXTENSION = 8 class EventCategory(BaseModelVersioned): diff --git a/epictrack-api/src/api/models/project.py b/epictrack-api/src/api/models/project.py index 9916511a9..d8d7dcbab 100644 --- a/epictrack-api/src/api/models/project.py +++ b/epictrack-api/src/api/models/project.py @@ -12,14 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. """Model to manage Project.""" - -from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Text, func +import enum +from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Text, Enum, func from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import TSTZRANGE from .base_model import BaseModel, BaseModelVersioned +class ProjectStateEnum(enum.Enum): + """Enum for project state""" + + UNDER_EAC_ASSESSMENT = "UNDER_EAC_ASSESSMENT" + UNDER_EXEMPTION_REQUEST = "UNDER_EXEMPTION_REQUEST" + UNDER_AMENDMENT = "UNDER_AMENDMENT" + UNDER_DISPUTE_RESOLUTION = "UNDER_DISPUTE_RESOLUTION" + PRE_CONSTRUCTION = "PRE_CONSTRUCTION" + CONSTRUCTION = "CONSTRUCTION" + OPERATION = "OPERATION" + CARE_AND_MAINTENANCE = "CARE_AND_MAINTENANCE" + DECOMMISSION = "DECOMMISSION" + UNKNOWN = "UNKNOWN" + + class Project(BaseModelVersioned): """Model class for Project.""" @@ -37,6 +52,7 @@ class Project(BaseModelVersioned): address = Column(Text, nullable=True, default=None) fte_positions_construction = Column(Integer(), nullable=True) fte_positions_operation = Column(Integer(), nullable=True) + project_state = Column(Enum(ProjectStateEnum)) ea_certificate = Column(String(255), nullable=True, default=None) sub_type_id = Column(ForeignKey("sub_types.id"), nullable=False) diff --git a/epictrack-api/src/api/models/work.py b/epictrack-api/src/api/models/work.py index 544195f74..c746b98cb 100644 --- a/epictrack-api/src/api/models/work.py +++ b/epictrack-api/src/api/models/work.py @@ -12,13 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. """Model to handle all operations related to Work.""" - -from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, func +import enum +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, Enum, func from sqlalchemy.orm import relationship from .base_model import BaseModelVersioned +class WorkStateEnum(enum.Enum): + """Enum for WorkState""" + + SUSPENDED = "SUSPENDED" + IN_PROGRESS = "IN_PROGRESS" + WITHDRAWN = "WITHDRAWN" + TERMINATED = "TERMINATED" + CLOSED = "CLOSED" + COMPLETED = "COMPLETED" + + class Work(BaseModelVersioned): """Model class for Work.""" @@ -56,6 +67,7 @@ class Work(BaseModelVersioned): decision_by_id = Column(ForeignKey('staffs.id'), nullable=False) start_date_locked = Column(Boolean(), default=False) decision_maker_position_id = Column(ForeignKey('positions.id'), nullable=True) + work_state = Column(Enum(WorkStateEnum), default=WorkStateEnum.IN_PROGRESS) project = relationship('Project', foreign_keys=[project_id], lazy='select') ministry = relationship('Ministry', foreign_keys=[ministry_id], lazy='select') diff --git a/epictrack-api/src/api/models/work_phase.py b/epictrack-api/src/api/models/work_phase.py index 496d2d36c..ba657a784 100644 --- a/epictrack-api/src/api/models/work_phase.py +++ b/epictrack-api/src/api/models/work_phase.py @@ -31,6 +31,7 @@ class WorkPhase(BaseModelVersioned): work_id = Column(ForeignKey('works.id'), nullable=False) phase_id = Column(ForeignKey('phase_codes.id'), nullable=False) + legislated = Column(Boolean, default=False) task_added = Column(Boolean, default=False,) number_of_days = Column(Integer, default=0) is_completed = Column(Boolean, default=False) diff --git a/epictrack-api/src/api/schemas/request/task_request.py b/epictrack-api/src/api/schemas/request/task_request.py index ace33d03f..36d6fddc1 100644 --- a/epictrack-api/src/api/schemas/request/task_request.py +++ b/epictrack-api/src/api/schemas/request/task_request.py @@ -172,7 +172,10 @@ class TaskEventBodyParamSchema(RequestBodyParameterSchema): allow_none=True, ) - notes = fields.Str(metadata={"description": "Notes for the task"}) + notes = fields.Str( + metadata={"description": "Notes for the task"}, + allow_none=True + ) assignee_ids = fields.List( fields.Int(metadata={"description": "List of assignees of the task"}) diff --git a/epictrack-api/src/api/services/action_template.py b/epictrack-api/src/api/services/action_template.py index 9b0574d94..bb21d6cd0 100644 --- a/epictrack-api/src/api/services/action_template.py +++ b/epictrack-api/src/api/services/action_template.py @@ -22,8 +22,8 @@ class ActionTemplateService: # pylint: disable=too-few-public-methods @classmethod def get_action_params(cls, action_type: ActionEnum, request_data: dict): """Return the action params for the template""" - if action_type == ActionEnum.DUPLICATE_PHASE: - return cls._get_phase_param(request_data) + if action_type == ActionEnum.ADD_EVENT: + return request_data # cls._get_phase_param(request_data) return request_data @classmethod @@ -35,6 +35,8 @@ def _get_phase_param(cls, request_data: dict) -> dict: "ea_act_id": request_data.get("ea_act_id") } result = PhaseCode.find_by_params(param) + if not result: + return {} return { "phase_id": result[0].id } diff --git a/epictrack-api/src/api/services/event.py b/epictrack-api/src/api/services/event.py index 5103d5aa1..5caf1fbb6 100644 --- a/epictrack-api/src/api/services/event.py +++ b/epictrack-api/src/api/services/event.py @@ -134,7 +134,7 @@ def _process_event( all_work_phases, current_work_phase ) current_future_work_phases = all_work_phases[current_work_phase_index:] - if current_work_phase.phase.legislated: + if current_work_phase.legislated: phase_events = list( filter( lambda x, _work_phase_id=current_work_phase.id: x.event_configuration.work_phase_id == @@ -190,7 +190,7 @@ def _process_event( event, all_work_event_configurations, ) - if not current_work_phase.phase.legislated: + if not current_work_phase.legislated: cls._push_work_phases( current_future_work_phases, all_work_events, diff --git a/epictrack-api/src/api/services/event_template.py b/epictrack-api/src/api/services/event_template.py index ab3fd2123..026f11c62 100644 --- a/epictrack-api/src/api/services/event_template.py +++ b/epictrack-api/src/api/services/event_template.py @@ -46,20 +46,24 @@ def import_events_template(cls, configuration_file): outcome_dict = excel_dict.get("Outcomes") action_dict = excel_dict.get("Actions") for event_type in event_types: - event_dict = event_dict.replace({'event_type_id': rf'^{event_type.name}$'}, + name = escape_characters(event_type.name, ['(', ')']) + event_dict = event_dict.replace({'event_type_id': rf'^{name}$'}, {'event_type_id': event_type.id}, regex=True) for event_category in event_categories: - event_dict = event_dict.replace({'event_category_id': rf'^{event_category.name}$'}, + name = escape_characters(event_category.name, ['(', ')']) + event_dict = event_dict.replace({'event_category_id': rf'^{name}$'}, {'event_category_id': event_category.id}, regex=True) for work_type in work_types: - phase_dict = phase_dict.replace({'work_type_id': rf'^{work_type.name}$'}, + name = escape_characters(work_type.name, ['(', ')']) + phase_dict = phase_dict.replace({'work_type_id': rf'^{name}$'}, {'work_type_id': work_type.id}, regex=True) for ea_act in ea_acts: name = escape_characters(ea_act.name, ['(', ')']) phase_dict = phase_dict.replace({'ea_act_id': rf'^{name}$'}, {'ea_act_id': ea_act.id}, regex=True) for action in actions: - action_dict = action_dict.replace({'action_id': rf'^{action.name}$'}, + name = escape_characters(action.name, ['(', ')']) + action_dict = action_dict.replace({'action_id': rf'^{name}$'}, {'action_id': action.id}, regex=True) event_dict = event_dict.to_dict('records') diff --git a/epictrack-api/src/api/services/work.py b/epictrack-api/src/api/services/work.py index 87c735d0e..0b887573e 100644 --- a/epictrack-api/src/api/services/work.py +++ b/epictrack-api/src/api/services/work.py @@ -131,6 +131,7 @@ def create_work(cls, payload): "phase_id": phase.id, "start_date": f"{phase_start_date}", "end_date": f"{end_date}", + "legislated": phase.legislated, "number_of_days": phase.number_of_days, } ) diff --git a/epictrack-api/src/api/templates/event_templates/project_notification/001-Project_Notification.xlsx b/epictrack-api/src/api/templates/event_templates/project_notification/001-Project_Notification.xlsx index 750aac2ad..f357cd503 100644 Binary files a/epictrack-api/src/api/templates/event_templates/project_notification/001-Project_Notification.xlsx and b/epictrack-api/src/api/templates/event_templates/project_notification/001-Project_Notification.xlsx differ diff --git a/epictrack-web/src/components/shared/TrackDialog.tsx b/epictrack-web/src/components/shared/TrackDialog.tsx index 1049c3f32..d3231fc42 100644 --- a/epictrack-web/src/components/shared/TrackDialog.tsx +++ b/epictrack-web/src/components/shared/TrackDialog.tsx @@ -109,7 +109,13 @@ const TrackDialog: FC = ({ color: Palette.neutral.dark, }} > - {dialogContentText} + + {dialogContentText} + )} {props.children} diff --git a/epictrack-web/src/components/workPlan/event/EventForm.tsx b/epictrack-web/src/components/workPlan/event/EventForm.tsx index 780319e2e..abbfdcbfe 100644 --- a/epictrack-web/src/components/workPlan/event/EventForm.tsx +++ b/epictrack-web/src/components/workPlan/event/EventForm.tsx @@ -72,6 +72,7 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => { const [anticipatedLabel, setAnticipatedLabel] = React.useState("Anticipated Date"); const [actualDateLabel, setActualDateLabel] = React.useState("Actual Date"); + const isCreateMode = React.useMemo(() => !event, [event]); const titleRef = React.useRef(); const schema = React.useMemo( () => @@ -100,6 +101,13 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => { then: () => yup.string().required("Please select the decision maker"), otherwise: () => yup.string().nullable(), }), + act_section_id: yup.string().when([], { + is: () => + selectedConfiguration?.event_category_id === + EventCategory.EXTENSION, + then: () => yup.string().required("Please select the act section"), + otherwise: () => yup.string().nullable(), + }), }), [selectedConfiguration, actualAdded] ); @@ -182,19 +190,21 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => { } }; - const onSubmitHandler = async (data: MilestoneEvent) => { + const onSubmitHandler = async (submittedData: MilestoneEvent) => { try { - data.anticipated_date = Moment(data.anticipated_date).format(); - if (!!data.actual_date) { - data.actual_date = Moment(data.actual_date).format(); + submittedData.anticipated_date = Moment( + submittedData.anticipated_date + ).format(); + if (!!submittedData.actual_date) { + submittedData.actual_date = Moment(submittedData.actual_date).format(); } - data.notes = notes; + submittedData.notes = notes; // setSubmittedEvent(data); const showConfirmDialog = - (event === undefined && !!data.actual_date) || - (event != null && event.actual_date == null && !!data.actual_date); + (isCreateMode && !!submittedData.actual_date) || + (!isCreateMode && !event?.actual_date && !!submittedData.actual_date); if (!showConfirmDialog) { - saveEvent(data); + saveEvent(submittedData); } setShowEventLockDialog(showConfirmDialog); } catch (e: any) { @@ -211,33 +221,44 @@ const EventForm = ({ onSave, event, isFormFieldsLocked }: EventFormProps) => { const saveEvent = React.useCallback( async (data?: MilestoneEvent) => { - const dataToBeSubmitted = data || getValues(); - if (event) { - const createResult = await eventService.update( - dataToBeSubmitted, - Number(event.id) - ); - if (createResult.status === 200) { - showNotification("Milestone details updated", { - type: "success", - }); - if (onSave) { - onSave(); + try { + const dataToBeSubmitted = data || getValues(); + if (event) { + const createResult = await eventService.update( + dataToBeSubmitted, + Number(event.id) + ); + if (createResult.status === 200) { + showNotification("Milestone details updated", { + type: "success", + }); + if (onSave) { + onSave(); + } } - } - } else { - const createResult = await eventService.create( - dataToBeSubmitted, - Number(ctx.selectedWorkPhase?.id) - ); - if (createResult.status === 201) { - showNotification("Milestone details inserted", { - type: "success", - }); - if (onSave) { - onSave(); + } else { + const createResult = await eventService.create( + dataToBeSubmitted, + Number(ctx.selectedWorkPhase?.id) + ); + if (createResult.status === 201) { + showNotification("Milestone details inserted", { + type: "success", + }); + if (onSave) { + onSave(); + } } } + } catch (e) { + const error = getAxiosError(e); + const message = + error?.response?.status === 422 + ? error.response.data?.toString() + : COMMON_ERROR_MESSAGE; + showNotification(message, { + type: "error", + }); } }, [event, submittedEvent] diff --git a/epictrack-web/src/components/workPlan/event/EventListTable.tsx b/epictrack-web/src/components/workPlan/event/EventListTable.tsx index d51102b94..c1335b516 100644 --- a/epictrack-web/src/components/workPlan/event/EventListTable.tsx +++ b/epictrack-web/src/components/workPlan/event/EventListTable.tsx @@ -407,7 +407,7 @@ const EventListTable = ({ row.original.end_date === undefined && row.original.type === EVENT_TYPE.MILESTONE, indeterminate: - row.original.end_date !== undefined && + row.original.is_complete && row.original.type === EVENT_TYPE.MILESTONE, })} columns={columns} diff --git a/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx b/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx index ee0080137..8d325add6 100644 --- a/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx +++ b/epictrack-web/src/components/workPlan/event/components/ExtensionInput.tsx @@ -39,6 +39,7 @@ const ExtensionInput = (props: ExtensionInputProps) => { unregister("phase_end_date"); }; }, []); + React.useEffect(() => { let numberOfDays = Number(getValues("number_of_days")); if (numberOfDaysRef.current as any) { diff --git a/epictrack-web/src/models/event.ts b/epictrack-web/src/models/event.ts index aa37b02d9..3a18d8f5c 100644 --- a/epictrack-web/src/models/event.ts +++ b/epictrack-web/src/models/event.ts @@ -55,6 +55,7 @@ export enum EventCategory { PCP = 5, CALENDAR = 6, FINANCE = 7, + SPECIAL_EXTENSION = 8, } export enum EventType { OPEN_HOUSE = 23,