diff --git a/epictrack-api/migrations/versions/7deec389ae56_added_notes_and_pip_fields_to_nations.py b/epictrack-api/migrations/versions/7deec389ae56_added_notes_and_pip_fields_to_nations.py new file mode 100644 index 000000000..f6526d0ab --- /dev/null +++ b/epictrack-api/migrations/versions/7deec389ae56_added_notes_and_pip_fields_to_nations.py @@ -0,0 +1,46 @@ +"""added notes and pip fields to indigenous nations. + +Revision ID: 7deec389ae56 +Revises: d8b405f9d663 +Create Date: 2023-10-18 11:46:46.577009 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '7deec389ae56' +down_revision = 'd8b405f9d663' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('indigenous_nations', schema=None) as batch_op: + batch_op.add_column(sa.Column('notes', sa.String(), nullable=True)) + batch_op.add_column(sa.Column('pip_org_type_id', sa.Integer(), nullable=True)) + batch_op.create_foreign_key(None, 'pip_org_types', ['pip_org_type_id'], ['id']) + + with op.batch_alter_table('indigenous_nations_history', schema=None) as batch_op: + batch_op.add_column(sa.Column('notes', sa.String(), autoincrement=False, nullable=True)) + batch_op.add_column(sa.Column('pip_org_type_id', sa.Integer(), autoincrement=False, nullable=True)) + batch_op.create_foreign_key(None, 'pip_org_types', ['pip_org_type_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('indigenous_nations_history', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('pip_org_type_id') + batch_op.drop_column('notes') + + with op.batch_alter_table('indigenous_nations', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('pip_org_type_id') + batch_op.drop_column('notes') + + # ### end Alembic commands ### diff --git a/epictrack-api/migrations/versions/d8b405f9d663_create_pip_org_type_table.py b/epictrack-api/migrations/versions/d8b405f9d663_create_pip_org_type_table.py new file mode 100644 index 000000000..ade633783 --- /dev/null +++ b/epictrack-api/migrations/versions/d8b405f9d663_create_pip_org_type_table.py @@ -0,0 +1,81 @@ +"""create pip_org_type table + +Revision ID: d8b405f9d663 +Revises: 47b41964f6df +Create Date: 2023-10-18 11:45:36.531064 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'd8b405f9d663' +down_revision = '47b41964f6df' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pip_org_type = op.create_table('pip_org_types', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('created_by', sa.String(length=255), nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.Column('updated_by', sa.String(length=255), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), + sa.Column('is_active', sa.Boolean(), server_default='t', nullable=False), + sa.Column('is_deleted', sa.Boolean(), server_default='f', nullable=False), + sa.PrimaryKeyConstraint('id'), + sqlite_autoincrement=True + ) + op.create_table('pip_org_types_history', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('name', sa.String(), autoincrement=False, nullable=False), + sa.Column('created_by', sa.String(length=255), autoincrement=False, nullable=True), + sa.Column('created_at', sa.DateTime(timezone=True), autoincrement=False, nullable=True), + sa.Column('updated_by', sa.String(length=255), autoincrement=False, nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), autoincrement=False, nullable=True), + sa.Column('is_active', sa.Boolean(), autoincrement=False, nullable=False), + sa.Column('is_deleted', sa.Boolean(), autoincrement=False, nullable=False), + sa.Column('pk', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('during', postgresql.TSTZRANGE(), nullable=True), + sa.PrimaryKeyConstraint('id', 'pk'), + sqlite_autoincrement=True + ) + op.bulk_insert(pip_org_type, + [ + { + "name": "Clan" + }, + { + "name": "First Nation" + }, + { + "name": "First Nation Group" + }, + { + "name": "House" + }, + { + "name": "Keyoh" + }, + { + "name": "Metis" + }, + { + "name": "Tribal Council" + }, + { + "name": "Wilp" + } + ]) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('pip_org_types_history') + op.drop_table('pip_org_types') + # ### end Alembic commands ### diff --git a/epictrack-api/src/api/models/__init__.py b/epictrack-api/src/api/models/__init__.py index fecad6836..0f418f165 100644 --- a/epictrack-api/src/api/models/__init__.py +++ b/epictrack-api/src/api/models/__init__.py @@ -69,3 +69,4 @@ from .action_configuration import ActionCofiguration from .task_event_responsibility import TaskEventResponsibility from .act_section import ActSection +from .pip_org_type import PIPOrgType diff --git a/epictrack-api/src/api/models/indigenous_nation.py b/epictrack-api/src/api/models/indigenous_nation.py index 533d6e639..c93f0ed0a 100644 --- a/epictrack-api/src/api/models/indigenous_nation.py +++ b/epictrack-api/src/api/models/indigenous_nation.py @@ -30,6 +30,7 @@ class IndigenousNation(db.Model, CodeTableVersioned): name = Column(String(255), nullable=False) is_active = Column(Boolean, default=True, nullable=False) pip_link = Column(URLType, default=None, nullable=True) + notes = Column(String) relationship_holder_id = Column( ForeignKey("staffs.id"), nullable=True, default=None @@ -38,6 +39,11 @@ class IndigenousNation(db.Model, CodeTableVersioned): "Staff", foreign_keys=[relationship_holder_id], lazy="select" ) + pip_org_type_id = Column(ForeignKey("pip_org_types.id")) + pip_org_type = relationship( + "PIPOrgType", foreign_keys=[pip_org_type_id], lazy="select" + ) + def as_dict(self): """Return JSON Representation.""" result = CodeTableVersioned.as_dict(self) diff --git a/epictrack-api/src/api/models/pip_org_type.py b/epictrack-api/src/api/models/pip_org_type.py new file mode 100644 index 000000000..fab820f53 --- /dev/null +++ b/epictrack-api/src/api/models/pip_org_type.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. +"""Model to handle all operations related to PIP Organization Types.""" + +from sqlalchemy import Column, Integer, String + +from .code_table import CodeTableVersioned +from .db import db + + +class PIPOrgType(db.Model, CodeTableVersioned): + """Model class for PIP Organization Types.""" + + __tablename__ = 'pip_org_types' + + id = Column(Integer, primary_key=True, autoincrement=True) + name = Column(String(), nullable=False) diff --git a/epictrack-api/src/api/schemas/request/indigenous_nation_request.py b/epictrack-api/src/api/schemas/request/indigenous_nation_request.py index f7771d0ca..dc0cb40cd 100644 --- a/epictrack-api/src/api/schemas/request/indigenous_nation_request.py +++ b/epictrack-api/src/api/schemas/request/indigenous_nation_request.py @@ -38,6 +38,24 @@ class IndigenousNationBodyParameterSchema(RequestBodyParameterSchema): is_active = fields.Bool( metadata={"description": "Active state of the indigenous nation"}) + notes = fields.Str( + metadata={"description": "Notes for the indigenous nation"}, + allow_none=True + ) + + pip_org_type_id = fields.Int( + metadata={"description": "PIP organization type for the indigenous nation"}, + validate=validate.Range(min=1), + allow_none=True, + missing=None + ) + + pip_link = fields.Str( + metadata={"description": "PIP site URL for indigenous nation"}, + allow_none=True, + missing=None + ) + class IndigenousNationExistenceQueryParamSchema(RequestQueryParameterSchema): """IndigenousNation existance check query parameters""" diff --git a/epictrack-web/src/components/icons/index.tsx b/epictrack-web/src/components/icons/index.tsx index 6a32f22d7..04d900d44 100644 --- a/epictrack-web/src/components/icons/index.tsx +++ b/epictrack-web/src/components/icons/index.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from "react"; +import React from "react"; import { IconProps } from "./type"; const commonProps = { diff --git a/epictrack-web/src/components/indigenousNation/IndigneousNationForm.tsx b/epictrack-web/src/components/indigenousNation/IndigneousNationForm.tsx index d7c286578..69c14b6b3 100644 --- a/epictrack-web/src/components/indigenousNation/IndigneousNationForm.tsx +++ b/epictrack-web/src/components/indigenousNation/IndigneousNationForm.tsx @@ -1,16 +1,19 @@ import React from "react"; -import { TextField, Grid, Button } from "@mui/material"; +import { TextField, Grid } from "@mui/material"; import { FormProvider, useForm } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { ETFormLabel } from "../shared/index"; import { Staff } from "../../models/staff"; -import ControlledCheckbox from "../shared/controlledInputComponents/ControlledCheckbox"; import indigenousNationService from "../../services/indigenousNationService/indigenousNationService"; import { FirstNation } from "../../models/firstNation"; import staffService from "../../services/staffService/staffService"; import ControlledSelectV2 from "../shared/controlledInputComponents/ControlledSelectV2"; import { MasterContext } from "../shared/MasterContext"; +import { PIPOrgType } from "../../models/pipOrgType"; +import ControlledSwitch from "../shared/controlledInputComponents/ControlledSwitch"; +import RichTextEditor from "../shared/richTextEditor"; +import codeService, { Code } from "../../services/codeService"; const schema = yup.object().shape({ name: yup @@ -31,16 +34,23 @@ const schema = yup.object().shape({ return true; } ), + pip_url: yup.string(), }); export default function IndigenousNationForm({ ...props }) { const [staffs, setStaffs] = React.useState([]); + const [pipOrgTypes, setPipOrgTypes] = React.useState([]); + const [notes, setNotes] = React.useState(""); const ctx = React.useContext(MasterContext); React.useEffect(() => { ctx.setFormId("indigenous-nation-form"); }, []); + React.useEffect(() => { + ctx.setTitle(ctx.item ? (ctx.item as FirstNation)?.name : "Nation"); + }, [ctx.title, ctx.item]); + React.useEffect(() => { ctx.setId(props.indigenousNationID); }, [ctx.id]); @@ -60,6 +70,9 @@ export default function IndigenousNationForm({ ...props }) { React.useEffect(() => { reset(ctx.item); + if (ctx.item) { + setNotes((ctx.item as FirstNation)?.notes || ""); + } }, [ctx.item]); const getStaffs = async () => { @@ -68,14 +81,37 @@ export default function IndigenousNationForm({ ...props }) { setStaffs(staffsResult.data as never); } }; + + const codeTypes: { [x: string]: any } = { + pip_org_types: setPipOrgTypes, + }; + + const getCodes = async (code: Code) => { + const codeResult = await codeService.getCodes(code); + if (codeResult.status === 200) { + codeTypes[code]((codeResult.data as never)["codes"]); + } + }; + React.useEffect(() => { getStaffs(); + const promises: any[] = []; + Object.keys(codeTypes).forEach(async (key) => { + promises.push(getCodes(key as Code)); + }); + Promise.all(promises); }, []); + const onSubmitHandler = async (data: FirstNation) => { + data.notes = notes; ctx.onSave(data, () => { reset(); }); + ctx.setId(undefined); }; + + console.log(pipOrgTypes); + return ( <> @@ -87,8 +123,9 @@ export default function IndigenousNationForm({ ...props }) { onSubmit={handleSubmit(onSubmitHandler)} > - Name + Name Relationship Holder + + PIP Organization Type + (o ? o.name : "")} + getOptionValue={(o: PIPOrgType) => (o ? o.id.toString() : "")} + options={pipOrgTypes || []} + {...register("pip_org_type_id")} + > + + + PIP URL + + + + Notes + + - diff --git a/epictrack-web/src/models/firstNation.ts b/epictrack-web/src/models/firstNation.ts index fb9f20d6a..05e90ddff 100644 --- a/epictrack-web/src/models/firstNation.ts +++ b/epictrack-web/src/models/firstNation.ts @@ -1,12 +1,16 @@ import { ListType } from "./code"; import { Staff } from "./staff"; import { MasterBase } from "./type"; +import { PIPOrgType } from "./pipOrgType"; export interface FirstNation extends ListType, MasterBase { is_active: boolean; relationship_holder_id?: number; relationship_holder?: Staff; pip_link: string; + notes?: string; + pip_org_type_id: number; + pip_org_type: PIPOrgType; } export interface WorkFirstNation extends ListType, MasterBase { diff --git a/epictrack-web/src/models/pipOrgType.ts b/epictrack-web/src/models/pipOrgType.ts new file mode 100644 index 000000000..22efa05d5 --- /dev/null +++ b/epictrack-web/src/models/pipOrgType.ts @@ -0,0 +1,4 @@ +import { ListType } from "./code"; +import { MasterBase } from "./type"; + +export interface PIPOrgType extends ListType, MasterBase {} diff --git a/epictrack-web/src/services/codeService/index.ts b/epictrack-web/src/services/codeService/index.ts index 75aa34c65..ff68017c0 100644 --- a/epictrack-web/src/services/codeService/index.ts +++ b/epictrack-web/src/services/codeService/index.ts @@ -16,7 +16,8 @@ export type Code = | "federal_involvements" | "responsibilities" | "roles" - | "substitution_acts"; + | "substitution_acts" + | "pip_org_types"; const getCodes = async (codeType: Code, apiUrl?: string) => { return await http.GetRequest(Endpoints.Codes.GET_CODES + `/${codeType}`);