From 51200d3ae0ed174fe9f1b1325515b7e868325e25 Mon Sep 17 00:00:00 2001 From: Sanjay Babu Date: Thu, 23 Jan 2025 11:32:50 -0800 Subject: [PATCH 001/181] Frontend - SubmissionIntakeForm, Map updated with pin or draw, externalAPIService updated, types, enums updated --- frontend/src/components/housing/maps/Map.vue | 100 +++++++++++++++++- .../housing/submission/SubmissionForm.vue | 9 +- .../submission/SubmissionIntakeForm.vue | 86 +++++++++++++++ .../submission/SubmissionIntakeSchema.ts | 3 +- frontend/src/services/externalApiService.ts | 8 ++ frontend/src/types/Submission.ts | 2 + frontend/src/utils/constants/housing.ts | 6 +- frontend/src/utils/enums/housing.ts | 3 +- .../housing/projects/ProjectsList.spec.ts | 1 + 9 files changed, 212 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/housing/maps/Map.vue b/frontend/src/components/housing/maps/Map.vue index 758512ed4..0d4b9bdd7 100644 --- a/frontend/src/components/housing/maps/Map.vue +++ b/frontend/src/components/housing/maps/Map.vue @@ -3,8 +3,10 @@ import * as L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import '@geoman-io/leaflet-geoman-free'; import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'; -import { onMounted, onUpdated, watch } from 'vue'; +import { onMounted, onUpdated, ref, watch } from 'vue'; +import { useToast } from '@/lib/primevue'; +import { externalApiService } from '@/services'; import { BC_BOUNDARIES_LOWER, BC_BOUNDARIES_UPPER, @@ -14,22 +16,37 @@ import { OSM_URL_TEMPLATE } from '@/utils/constants/mapping'; +import type { GeoJSON } from 'geojson'; +import type { Ref } from 'vue'; + // Props const { disabled = false, latitude = undefined, - longitude = undefined + longitude = undefined, + pinOrDraw = false } = defineProps<{ disabled?: boolean; latitude?: number; longitude?: number; + pinOrDraw?: boolean; }>(); +// Constants +const POINT = 'Point'; + // Actions let marker: L.Marker; let map: L.Map; +const geoJSON: Ref = ref(undefined); +const toast = useToast(); +const oldLayer = ref(undefined); + +// Emits +const emit = defineEmits(['map:polygonUpdated', 'map:pinUpdated']); async function initMap() { + L.Icon.Default.prototype = new L.Icon(MAP_ICON_OPTIONS_RED); const osm = L.tileLayer(OSM_URL_TEMPLATE, OSM_TILE_LAYER_OPTIONS); map = L.map('map', { @@ -43,6 +60,83 @@ async function initMap() { map.on('drag', function () { map.panInsideBounds(bcBounds, { animate: false }); }); + + if (pinOrDraw) { + map.on('pm:create', async (e) => { + try { + const geo = e.layer as L.GeoJSON; + + if (oldLayer.value) { + //@ts-ignore - insufficient type definitions + map.removeLayer(oldLayer.value); + oldLayer.value = undefined; + } + oldLayer.value = e.layer; + + //@ts-ignore - insufficient type definitions + if (geo.toGeoJSON().geometry.type === POINT) { + //@ts-ignore - insufficient type definitions + let latitude = geo.toGeoJSON().geometry.coordinates[1]; + //@ts-ignore - insufficient type definitions + let longitude = geo.toGeoJSON().geometry.coordinates[0]; + + getNearestOccupant(longitude, latitude); + } else { + geoJSON.value = geo.toGeoJSON(); + + emit('map:polygonUpdated', { geoJSON: geoJSON.value }); + } + // Zoom in + if (geo.getBounds) map.fitBounds(geo.getBounds()); + //@ts-ignore - insufficient type definitions + else map.flyTo(geo.getLatLng(), 17); + } catch (e: any) { + toast.error('Error', e.message); + } + }); + initControls(); + } +} + +// Function to remove all markers from the map +function removeAllMarkers() { + map.eachLayer((layer) => { + if (layer instanceof L.Marker) { + map.removeLayer(layer); + } + }); +} + +// show parcel data from Geocoder +async function getNearestOccupant(longitude: string, latitude: string) { + const result = await externalApiService.getNearestOccupant(longitude, latitude); + const address = result.data.properties.occupantAliasAddress; + if (!address || address.length == 0) { + toast.warn('No address found'); + } + emit('map:pinUpdated', { + longitude: longitude, + latitude: latitude, + address: address + }); +} + +function initControls() { + map.pm.addControls({ + position: 'topleft', + + // Create + drawCircleMarker: false, + drawCircle: false, + drawText: false, + drawPolyline: false, + + // Edit + cutPolygon: false, + dragMode: false, + editMode: false, + rotateMode: false + }); } function disableInteraction() { @@ -83,6 +177,8 @@ defineExpose({ resizeMap }); onMounted(async () => { await initMap(); + // Clear all markers on mount + removeAllMarkers(); }); onUpdated(async () => { diff --git a/frontend/src/components/housing/submission/SubmissionForm.vue b/frontend/src/components/housing/submission/SubmissionForm.vue index 4a90e5a07..3ed5fd95e 100644 --- a/frontend/src/components/housing/submission/SubmissionForm.vue +++ b/frontend/src/components/housing/submission/SubmissionForm.vue @@ -324,6 +324,7 @@ onMounted(async () => { locality: submission.locality, province: submission.province, locationPIDs: submission.locationPIDs, + locationPIDsAuto: submission.locationPIDsAuto, latitude: submission.latitude, longitude: submission.longitude, geomarkUrl: submission.geomarkUrl, @@ -669,7 +670,13 @@ onMounted(async () => { :options="YES_NO_LIST" /> + ` + } + } + } +}); + +describe('NotesModal', () => { + beforeEach(() => { + sessionStorage.setItem( + StorageKey.CONFIG, + JSON.stringify({ + oidc: { + authority: 'abc', + clientId: '123' + } + }) + ); + vi.clearAllMocks(); + }); + + afterEach(() => { + sessionStorage.clear(); + }); + + it('renders component', async () => { + const wrapper = mount(NotesModal, wrapperSettings()); + expect(wrapper.exists()).toBe(true); + + const header = wrapper.find('.p-dialog-title'); + expect(header.exists()).toBe(true); + expect(header.text()).toContain('permitNotesModal.title'); + }); + + it('displays existing notes if present', async () => { + const wrapper = mount( + NotesModal, + wrapperSettings({ + ...testPermit + }) + ); + + const updatesSection = wrapper.find('.updates-section'); + expect(updatesSection.text()).toContain('Mock note'); + }); + + it('calls permitNoteService.createPermitNote when submitting a new note', async () => { + createPermitNoteSpy.mockResolvedValue({ + data: { + permitNoteId: 'noteUUID', + permitId: testPermit.permitId, + createdAt: currentDate, + note: 'New Test Note' + } + } as AxiosResponse); + + const wrapper = mount(NotesModal, wrapperSettings()); + + const formComponent = wrapper.findComponent({ name: 'Form' }); + await formComponent.vm.$emit('submit', { note: 'New Test Note' }, { resetForm: () => {} }); + + expect(permitNoteService.createPermitNote).toHaveBeenCalledWith({ + permitId: 'permitUUID', + note: 'New Test Note' + }); + + expect(wrapper.find('textarea').element.value).toBe(''); + }); + + it('handles service error gracefully when creating new note', async () => { + createPermitNoteSpy.mockRejectedValue(new Error('Fail')); + + const wrapper = mount(NotesModal, wrapperSettings()); + + const formComponent = wrapper.findComponent({ name: 'Form' }); + await formComponent.vm.$emit('submit', { note: 'Will fail' }, { resetForm: () => {} }); + + expect(permitNoteService.createPermitNote).toHaveBeenCalled(); + }); +}); From e3ebeb9d1847aef1c257c881e097a7f583d29fbd Mon Sep 17 00:00:00 2001 From: qhanson55 Date: Tue, 8 Apr 2025 10:48:30 -0700 Subject: [PATCH 070/181] Changed email template link to permit card hash --- frontend/src/router/index.ts | 23 ++++++++++++++++--- frontend/src/utils/templates.ts | 2 +- .../external/housing/project/ProjectView.vue | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 3b301ef0c..99c3f8729 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -86,6 +86,10 @@ export function entryRedirect(to: RouteLocationNormalizedGeneric) { } } +function convertRemToPixels(rem: number) { + return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); +} + const routes: Array = [ { path: '/', @@ -133,6 +137,8 @@ export default function getRouter() { const router = createRouter({ history: createWebHistory(), routes, + // TODO: Uncomment when navigating to last location/tab/etc. is fixed + // scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from) { // Scroll to hash // Slight delay to account for asynchronous loading @@ -141,16 +147,27 @@ export default function getRouter() { setTimeout(() => { resolve({ el: to.hash, - behavior: 'smooth' + behavior: 'smooth', + top: convertRemToPixels(5) }); }, 500); }); + // TODO: Uncomment when navigating to last location/tab/etc. is fixed + // } else if (savedPosition) { + // // Scroll to previous position on forward/back buttons + // return new Promise((resolve) => { + // setTimeout(() => { + // resolve({ + // ...savedPosition, + // behavior: 'smooth' + // }); + // }, 500); + // }); } else { // Do not scroll if same page if (to.name === from.name) return false; } - - // default scroll to top + // Default scroll to top return { top: 0 }; } }); diff --git a/frontend/src/utils/templates.ts b/frontend/src/utils/templates.ts index 198065af6..6f680834a 100644 --- a/frontend/src/utils/templates.ts +++ b/frontend/src/utils/templates.ts @@ -134,7 +134,7 @@ export const permitNoteNotificationTemplate = (replaceConfig: { [key: string]: s 'Project ID: {{ activityId }}

' + '{{ permitName }} (submitted on {{ submittedDate }}): A new note has been added to this application.

' + 'Regards,

' + '
From 8972e5fec977a8ff8db6f8ba5cb9683b3d8e51bd Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Wed, 30 Apr 2025 12:00:47 -0700 Subject: [PATCH 073/181] Use correct LTS version for unit tests. Update codeowners file --- .github/CODEOWNERS | 2 +- .github/workflows/unit-tests.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 317eddf77..c9ba8f196 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # These users will be the default owners for everything in the repo. # Unless a later match takes precedence, the following users will be # requested for review when someone opens a pull request. -* @kyle1morel @sanjaytkbabu @qhanson55 +* @bcgov/nr-pmt-pat diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 6949d6720..3e9da5e6c 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -21,7 +21,7 @@ jobs: node-version: - '18.x' - '20.x' - - '22.0' + - '22.x' steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -55,14 +55,14 @@ jobs: env: CI: true - name: Save Coverage Results - if: matrix.node-version == '20.x' + if: matrix.node-version == '22.x' uses: actions/upload-artifact@v4 with: name: coverage-app path: ${{ github.workspace }}/app/coverage retention-days: 1 - name: Monitor Coverage - if: "matrix.node-version == '20.x' && ! github.event.pull_request.head.repo.fork" + if: "matrix.node-version == '22.x' && ! github.event.pull_request.head.repo.fork" uses: slavcodev/coverage-monitor-action@v1 with: comment_mode: update @@ -120,14 +120,14 @@ jobs: env: CI: true - name: Save Coverage Results - if: matrix.node-version == '20.x' + if: matrix.node-version == '22.x' uses: actions/upload-artifact@v4 with: name: coverage-frontend path: ${{ github.workspace }}/frontend/coverage retention-days: 1 - name: Monitor Coverage - if: "matrix.node-version == '20.x' && ! github.event.pull_request.head.repo.fork" + if: "matrix.node-version == '22.x' && ! github.event.pull_request.head.repo.fork" uses: slavcodev/coverage-monitor-action@v1 with: comment_mode: update From 517ae194d293f1ea8302e5d0445848ec25c69011 Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Thu, 1 May 2025 09:42:47 -0700 Subject: [PATCH 074/181] Remove 18.x from matrix Node 18 is EOL --- .github/workflows/unit-tests.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 3e9da5e6c..bf29d8252 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -19,7 +19,6 @@ jobs: fail-fast: true matrix: node-version: - - '18.x' - '20.x' - '22.x' steps: @@ -86,7 +85,6 @@ jobs: fail-fast: true matrix: node-version: - - '18.x' - '20.x' - '22.x' steps: From 21d9791351970af26b53732192db646b30b2069d Mon Sep 17 00:00:00 2001 From: Sanjay Babu Date: Thu, 1 May 2025 15:34:17 -0700 Subject: [PATCH 075/181] Tag added to file upload --- frontend/src/services/documentService.ts | 6 +++++- frontend/tests/unit/service/documentService.spec.ts | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/services/documentService.ts b/frontend/src/services/documentService.ts index dfa26d654..eecd0f24f 100644 --- a/frontend/src/services/documentService.ts +++ b/frontend/src/services/documentService.ts @@ -5,6 +5,7 @@ import { appAxios } from './interceptors'; import { getFilenameAndExtension } from '@/utils/utils'; const PATH = '/document'; +const PROJECT_ID = 'Project ID'; export default { /** @@ -23,11 +24,14 @@ export default { } const newDocument = new File([document], newDocumentName, { type: document.type }); + // The tagset is used to filter the objects in the bucket + const tagset: Array<{ key: string; value: string }> = [{ key: PROJECT_ID, value: activityId }]; + // Create COMS object comsResponse = await comsService.createObject( newDocument, {}, - { bucketId }, + { bucketId, tagset }, { timeout: 0 } // Infinite timeout for big documents upload to avoid timeout error ); diff --git a/frontend/tests/unit/service/documentService.spec.ts b/frontend/tests/unit/service/documentService.spec.ts index b88c8b96d..cc9421935 100644 --- a/frontend/tests/unit/service/documentService.spec.ts +++ b/frontend/tests/unit/service/documentService.spec.ts @@ -37,6 +37,7 @@ const fileSpy = vi.spyOn(global, 'File'); // createDocument test data const testActivityId = 'testActivityId'; const testBucketId = 'testBucketId'; +const testTagset = [{ key: 'Project ID', value: testActivityId }]; const FILE_NAME = 'fileName'; const testFileData = { id: 'fileId', @@ -79,7 +80,12 @@ describe('noteService test', () => { // Testing await documentService.createDocument(testFile1 as File, testActivityId, testBucketId); expect(comsCreateSpy).toHaveBeenCalledTimes(1); - expect(comsCreateSpy).toHaveBeenCalledWith(testFile1, {}, { bucketId: testBucketId }, { timeout: 0 }); + expect(comsCreateSpy).toHaveBeenCalledWith( + testFile1, + {}, + { bucketId: testBucketId, tagset: testTagset }, + { timeout: 0 } + ); }); it('logs uploaded document to DB with right arguments', async () => { From aeb6bc7697f2759b0ff9833e02ab76dea9f58901 Mon Sep 17 00:00:00 2001 From: Sanjay Babu Date: Tue, 6 May 2025 11:26:42 -0700 Subject: [PATCH 076/181] User table updated, Business name autofilled on intake form, tests updated --- .../20250430000000_029-user-business-name.ts | 19 ++++++++++ app/src/db/models/contact.ts | 13 +++++++ app/src/db/models/user.ts | 2 ++ app/src/db/prisma/schema.prisma | 35 ++++++++++--------- app/src/services/contact.ts | 9 ++++- app/src/services/user.ts | 3 ++ app/src/types/Contact.ts | 1 + app/src/types/User.ts | 1 + app/tests/unit/controllers/user.spec.ts | 2 ++ app/tests/unit/services/user.spec.ts | 2 ++ .../submission/SubmissionIntakeForm.vue | 2 +- frontend/src/locales/en-CA.json | 2 +- frontend/src/types/Contact.ts | 1 + 13 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 app/src/db/migrations/20250430000000_029-user-business-name.ts diff --git a/app/src/db/migrations/20250430000000_029-user-business-name.ts b/app/src/db/migrations/20250430000000_029-user-business-name.ts new file mode 100644 index 000000000..3fb0b44a5 --- /dev/null +++ b/app/src/db/migrations/20250430000000_029-user-business-name.ts @@ -0,0 +1,19 @@ +import type { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + return Promise.resolve().then(() => + knex.schema.alterTable('user', (table) => { + table.text('bceid_business_name'); + }) + ); +} + +export async function down(knex: Knex): Promise { + return Promise.resolve() + + .then(() => + knex.schema.alterTable('user', (table) => { + table.dropColumn('bceid_business_name'); + }) + ); +} diff --git a/app/src/db/models/contact.ts b/app/src/db/models/contact.ts index 93e2f6c49..dc5cfc67c 100644 --- a/app/src/db/models/contact.ts +++ b/app/src/db/models/contact.ts @@ -10,9 +10,13 @@ const _contact = Prisma.validator()({}); const _contactWithActivitiesGraph = Prisma.validator()({ include: { activity_contact: true } }); +const _contactWithBusinessName = Prisma.validator()({ + include: { user: { select: { bceid_business_name: true } } } +}); type PrismaRelationContact = Omit, keyof Stamps>; type PrismaGraphContactActivities = Prisma.contactGetPayload; +type PrismaGraphContactBusinessName = Prisma.contactGetPayload; export default { toPrismaModel(input: Contact): PrismaRelationContact { @@ -47,6 +51,15 @@ export default { contact.activityContact = input.activity_contact.map((activity) => activity_contact.fromPrismaModel(activity)); } + return contact; + }, + + fromPrismaModelWithBusinessName(input: PrismaGraphContactBusinessName): Contact { + const contact = this.fromPrismaModel(input); + if (contact && input?.user?.bceid_business_name) { + contact.bceidBusinessName = input.user.bceid_business_name; + } + return contact; } }; diff --git a/app/src/db/models/user.ts b/app/src/db/models/user.ts index ad185fcd6..e9cf5f6b1 100644 --- a/app/src/db/models/user.ts +++ b/app/src/db/models/user.ts @@ -11,6 +11,7 @@ type PrismaRelationUser = Omit, keyof Stamps export default { toPrismaModel(input: User): PrismaRelationUser { return { + bceid_business_name: input.bceidBusinessName, user_id: input.userId as string, idp: input.idp, sub: input.sub, @@ -24,6 +25,7 @@ export default { fromPrismaModel(input: PrismaRelationUser): User { return { + bceidBusinessName: input.bceid_business_name, userId: input.user_id, idp: input.idp, sub: input.sub, diff --git a/app/src/db/prisma/schema.prisma b/app/src/db/prisma/schema.prisma index c68b7eee9..3c87ab07f 100644 --- a/app/src/db/prisma/schema.prisma +++ b/app/src/db/prisma/schema.prisma @@ -303,23 +303,24 @@ model submission { } model user { - user_id String @id @db.Uuid - idp String? - sub String @unique(map: "user_sub_unique") - email String? - first_name String? - full_name String? - last_name String? - active Boolean @default(true) - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - access_request access_request[] - contact contact[] - enquiry enquiry[] - submission submission[] - identity_provider identity_provider? @relation(fields: [idp], references: [idp], onDelete: Cascade, map: "user_idp_foreign") + user_id String @id @db.Uuid + idp String? + sub String @unique(map: "user_sub_unique") + email String? + first_name String? + full_name String? + last_name String? + active Boolean @default(true) + created_by String? @default("00000000-0000-0000-0000-000000000000") + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_by String? + updated_at DateTime? @db.Timestamptz(6) + bceid_business_name String? + access_request access_request[] + contact contact[] + enquiry enquiry[] + submission submission[] + identity_provider identity_provider? @relation(fields: [idp], references: [idp], onDelete: Cascade, map: "user_idp_foreign") @@index([email], map: "user_email_index") @@index([sub], map: "user_username_index") diff --git a/app/src/services/contact.ts b/app/src/services/contact.ts index 443099fc3..0ec9487b2 100644 --- a/app/src/services/contact.ts +++ b/app/src/services/contact.ts @@ -117,10 +117,17 @@ const service = { phone_number: { contains: params.phoneNumber, mode: 'insensitive' } } ] + }, + include: { + user: { + select: { + bceid_business_name: true + } + } } }); - return response ? response.map((x) => contact.fromPrismaModel(x)) : []; + return response ? response.map((x) => contact.fromPrismaModelWithBusinessName(x)) : []; } }; diff --git a/app/src/services/user.ts b/app/src/services/user.ts index a3abadcd7..5cffc5b04 100644 --- a/app/src/services/user.ts +++ b/app/src/services/user.ts @@ -23,6 +23,7 @@ const service = { */ _tokenToUser: (token: jwt.JwtPayload) => { return { + bceidBusinessName: token.bceid_business_name, sub: token.sub ? token.sub : token.preferred_username, firstName: token.given_name ?? token.given_names, fullName: token.name ?? token.display_name, @@ -81,6 +82,7 @@ const service = { } const newUser = { + bceidBusinessName: data.bceidBusinessName, userId: uuidv4(), sub: data.sub, fullName: data.fullName, @@ -299,6 +301,7 @@ const service = { } const obj = { + bceidBusinessName: data.bceidBusinessName, sub: data.sub, fullName: data.fullName, email: data.email, diff --git a/app/src/types/Contact.ts b/app/src/types/Contact.ts index 88369a895..f5ecd926d 100644 --- a/app/src/types/Contact.ts +++ b/app/src/types/Contact.ts @@ -2,6 +2,7 @@ import { IStamps } from '../interfaces/IStamps'; import { ActivityContact } from './ActivityContact'; export type Contact = { + bceidBusinessName?: string | null; contactId: string; // Primary Key userId: string | null; firstName: string | null; diff --git a/app/src/types/User.ts b/app/src/types/User.ts index 495a1f956..804c3dafd 100644 --- a/app/src/types/User.ts +++ b/app/src/types/User.ts @@ -1,6 +1,7 @@ import { IStamps } from '../interfaces/IStamps'; export type User = { + bceidBusinessName: string | null; userId?: string; // Primary Key idp: string | null; sub: string; diff --git a/app/tests/unit/controllers/user.spec.ts b/app/tests/unit/controllers/user.spec.ts index 06462d7cf..866b005f7 100644 --- a/app/tests/unit/controllers/user.spec.ts +++ b/app/tests/unit/controllers/user.spec.ts @@ -37,6 +37,7 @@ describe('searchUsers', () => { const users = [ { + bceidBusinessName: null, userId: '5e3f0c19-8664-4a43-ac9e-210da336e923', idp: 'IDIR', sub: 'cd90c6bf44074872a7116f4dd4f3a45b@idir', @@ -69,6 +70,7 @@ describe('searchUsers', () => { const users = [ { + bceidBusinessName: null, userId: '5e3f0c19-8664-4a43-ac9e-210da336e923', idp: 'IDIR', sub: 'cd90c6bf44074872a7116f4dd4f3a45b@idir', diff --git a/app/tests/unit/services/user.spec.ts b/app/tests/unit/services/user.spec.ts index 5b2fc42f3..36a3030ce 100644 --- a/app/tests/unit/services/user.spec.ts +++ b/app/tests/unit/services/user.spec.ts @@ -30,6 +30,7 @@ const idirIdentityProvider: IDPType = { }; const prismaIdirUser: PrismaUser = { + bceid_business_name: null, user_id: uuidv4(), idp: IdentityProvider.IDIR, sub: 'sub', @@ -45,6 +46,7 @@ const prismaIdirUser: PrismaUser = { }; const idirUser: User = { + bceidBusinessName: null, userId: prismaIdirUser.user_id, idp: prismaIdirUser.idp, sub: prismaIdirUser.sub, diff --git a/frontend/src/components/housing/submission/SubmissionIntakeForm.vue b/frontend/src/components/housing/submission/SubmissionIntakeForm.vue index ae5869cf6..b21836559 100644 --- a/frontend/src/components/housing/submission/SubmissionIntakeForm.vue +++ b/frontend/src/components/housing/submission/SubmissionIntakeForm.vue @@ -684,7 +684,7 @@ watch( :bold="false" :disabled="!editable" :options="YES_NO_LIST" - @on-change="() => setFieldValue('basic.registeredName', null)" + @on-change="() => setFieldValue('basic.registeredName', contactStore.getContact?.bceidBusinessName)" /> Date: Tue, 6 May 2025 14:21:27 -0700 Subject: [PATCH 077/181] Ats client payload updated --- frontend/src/components/user/ATSUserCreateModal.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/user/ATSUserCreateModal.vue b/frontend/src/components/user/ATSUserCreateModal.vue index 08d3a6296..9df6a3eb6 100644 --- a/frontend/src/components/user/ATSUserCreateModal.vue +++ b/frontend/src/components/user/ATSUserCreateModal.vue @@ -4,7 +4,7 @@ import { onBeforeMount, ref } from 'vue'; import { Spinner } from '@/components/layout'; import { Button, Column, DataTable, Dialog, useToast } from '@/lib/primevue'; import { atsService } from '@/services'; -import { BasicResponse, Initiative } from '@/utils/enums/application'; +import { BasicResponse } from '@/utils/enums/application'; import { setEmptyStringsToNull } from '@/utils/utils'; import type { Ref } from 'vue'; @@ -25,6 +25,9 @@ const { submissionOrEnquiry } = defineProps<{ submissionOrEnquiry: Enquiry | Submission; }>(); +// Constants +const ATS_REGION_NAME: string = 'Navigator'; + // Emits const emit = defineEmits(['atsUserLink:link']); @@ -57,7 +60,7 @@ async function createATSClient() { address: address, firstName: submissionOrEnquiry.contacts[0]?.firstName, surName: submissionOrEnquiry?.contacts[0]?.lastName, - regionName: Initiative.HOUSING, + regionName: ATS_REGION_NAME, optOutOfBCStatSurveyInd: BasicResponse.NO.toUpperCase() }; From 20fb27457bb3e2ade7d38dcf3e8e120da2f8aa43 Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Thu, 13 Mar 2025 16:15:30 -0700 Subject: [PATCH 078/181] Rename anything related to submission to housing project DB migration for table rename, rbac, statistics, route/controller/service layers, FE service, variables all over --- .../{submission.ts => housingProject.ts} | 161 ++++++----- app/src/controllers/index.ts | 2 +- app/src/controllers/map.ts | 9 +- app/src/controllers/note.ts | 8 +- app/src/controllers/reporting.ts | 4 +- .../db/migrations/20240904000000_009-rbac.ts | 14 +- .../20250313000000_027-submission_rename.ts | 262 ++++++++++++++++++ .../{submission.ts => housing_project.ts} | 48 ++-- app/src/db/models/index.ts | 2 +- app/src/db/prisma/schema.prisma | 163 ++++++----- app/src/docs/v1.api-spec.yaml | 190 ++++++------- app/src/middleware/authorization.ts | 8 +- app/src/routes/v1/housingProject.ts | 179 ++++++++++++ app/src/routes/v1/index.ts | 6 +- app/src/routes/v1/map.ts | 6 +- app/src/routes/v1/reporting.ts | 6 +- app/src/routes/v1/submission.ts | 184 ------------ .../{submission.ts => housingProject.ts} | 152 +++++----- app/src/services/index.ts | 2 +- app/src/services/reporting.ts | 16 +- app/src/types/BringForward.ts | 2 +- .../{Submission.ts => HousingProject.ts} | 6 +- ...ssionIntake.ts => HousingProjectIntake.ts} | 4 +- ...s.ts => HousingProjectSearchParameters.ts} | 6 +- app/src/types/index.ts | 6 +- app/src/utils/constants/housing.ts | 4 +- app/src/utils/enums/application.ts | 2 +- app/src/utils/enums/housing.ts | 2 +- .../{submission.ts => housingProject.ts} | 42 +-- app/src/validators/index.ts | 2 +- app/tests/unit/controllers/note.spec.ts | 12 +- app/tests/unit/controllers/reporting.spec.ts | 16 +- app/tests/unit/controllers/submission.spec.ts | 238 ++++++++-------- .../components/contact/ContactHistoryList.vue | 14 +- .../components/file/AdvancedFileUpload.vue | 8 +- .../src/components/file/DeleteDocument.vue | 6 +- frontend/src/components/file/FileUpload.vue | 6 +- .../housing/enquiry/EnquiryForm.vue | 8 +- .../housing/enquiry/EnquiryIntakeForm.vue | 4 +- .../SubmissionBringForwardCalendar.vue | 10 +- .../SubmissionDraftListProponent.vue | 8 +- .../housing/submission/SubmissionForm.vue | 146 +++++----- .../submission/SubmissionIntakeForm.vue | 42 +-- .../submission/SubmissionListNavigator.vue | 34 +-- .../submission/SubmissionStatistics.vue | 12 +- .../submission/SubmissionsNavigator.vue | 40 +-- frontend/src/components/permit/NotesModal.vue | 6 +- .../src/components/permit/PermitModal.vue | 10 +- frontend/src/components/roadmap/Roadmap.vue | 2 +- .../components/user/ATSUserCreateModal.vue | 34 +-- .../src/components/user/ATSUserLinkModal.vue | 12 +- frontend/src/router/externalHousing.ts | 4 +- frontend/src/router/internalHousing.ts | 2 +- ...ionService.ts => housingProjectService.ts} | 60 ++-- frontend/src/services/index.ts | 2 +- frontend/src/services/mapService.ts | 4 +- frontend/src/services/reportingService.ts | 6 +- ...missionStore.ts => housingProjectStore.ts} | 22 +- frontend/src/store/index.ts | 2 +- frontend/src/types/BringForward.ts | 2 +- .../{Submission.ts => HousingProject.ts} | 6 +- ...ssionIntake.ts => HousingProjectIntake.ts} | 2 +- ...s.ts => HousingProjectSearchParameters.ts} | 6 +- frontend/src/types/index.ts | 6 +- frontend/src/utils/enums/application.ts | 2 +- .../views/external/housing/HousingView.vue | 18 +- .../housing/enquiry/EnquiryIntakeView.vue | 6 +- .../housing/permit/PermitStatusView.vue | 2 +- .../project/ProjectConfirmationView.vue | 4 +- .../housing/project/ProjectIntakeView.vue | 2 +- .../external/housing/project/ProjectView.vue | 14 +- .../internal/contact/ContactPageView.vue | 14 +- .../internal/housing/enquiry/EnquiryView.vue | 20 +- .../internal/housing/project/ProjectView.vue | 35 +-- .../contact/ContactHistoryList.spec.ts | 4 +- .../housing/enquiry/EnquiryForm.spec.ts | 4 +- .../housing/submission/SubmissionForm.spec.ts | 13 +- .../SubmissionListNavigator.spec.ts | 8 +- .../submission/SubmissionsNavigator.spec.ts | 12 +- .../shasIntake/EnquiryIntakeForm.spec.ts | 8 +- .../shasIntake/SubmissionIntakeForm.spec.ts | 4 +- .../SubmissionBringForwardCalendar.spec.ts | 2 +- .../user/ATSUserCreateModal.spec.ts | 12 +- .../components/user/ATSUserLinkModal.spec.ts | 10 +- .../unit/service/reportingService.spec.ts | 6 +- .../unit/service/submissionService.spec.ts | 54 ++-- .../external/housing/HousingView.spec.ts | 14 +- .../housing/permit/PermitStatusView.spec.ts | 12 +- .../project/ProjectConfirmationView.spec.ts | 2 +- .../housing/project/ProjectIntakeView.spec.ts | 2 +- .../housing/project/ProjectView.spec.ts | 70 ++--- .../internal/contact/ContactPageView.spec.ts | 4 +- .../housing/project/ProjectView.spec.ts | 34 +-- 93 files changed, 1483 insertions(+), 1199 deletions(-) rename app/src/controllers/{submission.ts => housingProject.ts} (67%) create mode 100644 app/src/db/migrations/20250313000000_027-submission_rename.ts rename app/src/db/models/{submission.ts => housing_project.ts} (76%) create mode 100644 app/src/routes/v1/housingProject.ts delete mode 100644 app/src/routes/v1/submission.ts rename app/src/services/{submission.ts => housingProject.ts} (65%) rename app/src/types/{Submission.ts => HousingProject.ts} (94%) rename app/src/types/{SubmissionIntake.ts => HousingProjectIntake.ts} (95%) rename app/src/types/{SubmissionSearchParameters.ts => HousingProjectSearchParameters.ts} (54%) rename app/src/validators/{submission.ts => housingProject.ts} (87%) rename frontend/src/services/{submissionService.ts => housingProjectService.ts} (50%) rename frontend/src/store/{submissionStore.ts => housingProjectStore.ts} (84%) rename frontend/src/types/{Submission.ts => HousingProject.ts} (94%) rename frontend/src/types/{SubmissionIntake.ts => HousingProjectIntake.ts} (97%) rename frontend/src/types/{SubmissionSearchParameters.ts => HousingProjectSearchParameters.ts} (54%) diff --git a/app/src/controllers/submission.ts b/app/src/controllers/housingProject.ts similarity index 67% rename from app/src/controllers/submission.ts rename to app/src/controllers/housingProject.ts index e5911d446..f8693193e 100644 --- a/app/src/controllers/submission.ts +++ b/app/src/controllers/housingProject.ts @@ -7,7 +7,7 @@ import { draftService, emailService, enquiryService, - submissionService, + housingProjectService, permitService } from '../services'; import { Initiative } from '../utils/enums/application'; @@ -28,49 +28,60 @@ import type { CurrentContext, Draft, Email, + HousingProject, + HousingProjectIntake, + HousingProjectSearchParameters, Permit, - StatisticsFilters, - Submission, - SubmissionIntake, - SubmissionSearchParameters + StatisticsFilters } from '../types'; const controller = { /** * @function assignPriority - * Assigns a priority level to a submission based on given criteria + * Assigns a priority level to a housing project based on given criteria * Criteria defined below */ - assignPriority: (submission: Partial) => { + assignPriority: (housingProject: Partial) => { const matchesPriorityOneCriteria = // Priority 1 Criteria: - submission.singleFamilyUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || // 1. More than 50 units (any) - submission.singleFamilyUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || - submission.multiFamilyUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || - submission.multiFamilyUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || - submission.otherUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || - submission.otherUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || - submission.hasRentalUnits === 'Yes' || // 2. Supports Rental Units - submission.financiallySupportedBC === 'Yes' || // 3. Social Housing - submission.financiallySupportedIndigenous === 'Yes'; // 4. Indigenous Led + // 1. More than 50 units (any) + housingProject.singleFamilyUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || + housingProject.singleFamilyUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || + housingProject.multiFamilyUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || + housingProject.multiFamilyUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || + housingProject.otherUnits === NumResidentialUnits.GREATER_THAN_FIVE_HUNDRED || + housingProject.otherUnits === NumResidentialUnits.FIFTY_TO_FIVE_HUNDRED || + // 2. Supports Rental Units + housingProject.hasRentalUnits === 'Yes' || + // 3. Social Housing + housingProject.financiallySupportedBC === 'Yes' || + // 4. Indigenous Led + housingProject.financiallySupportedIndigenous === 'Yes'; const matchesPriorityTwoCriteria = // Priority 2 Criteria: - submission.singleFamilyUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || // 1. Single Family >= 10 Units - submission.multiFamilyUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || // 2. Has 1 or more MultiFamily Units - submission.multiFamilyUnits === NumResidentialUnits.ONE_TO_NINE || - submission.otherUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || // 3. Has 1 or more Other Units - submission.otherUnits === NumResidentialUnits.ONE_TO_NINE; + // 1. Single Family >= 10 Units + housingProject.singleFamilyUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || + // 2. Has 1 or more MultiFamily Units + housingProject.multiFamilyUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || + housingProject.multiFamilyUnits === NumResidentialUnits.ONE_TO_NINE || + // 3. Has 1 or more Other Units + housingProject.otherUnits === NumResidentialUnits.TEN_TO_FOURTY_NINE || + housingProject.otherUnits === NumResidentialUnits.ONE_TO_NINE; if (matchesPriorityOneCriteria) { - submission.queuePriority = 1; + housingProject.queuePriority = 1; } else if (matchesPriorityTwoCriteria) { - submission.queuePriority = 2; + housingProject.queuePriority = 2; } else { // Prioriy 3 Criteria: - submission.queuePriority = 3; // Everything Else + housingProject.queuePriority = 3; // Everything Else } }, - generateSubmissionData: async (data: SubmissionIntake, intakeStatus: string, currentContext: CurrentContext) => { + generateHousingProjectData: async ( + data: HousingProjectIntake, + intakeStatus: string, + currentContext: CurrentContext + ) => { const activityId = data.activityId ?? (await activityService.createActivity(Initiative.HOUSING, generateCreateStamps(currentContext)))?.activityId; @@ -164,34 +175,34 @@ const controller = { })); } - // Put new submission together - const submissionData = { - submission: { + // Put new housing project together + const housingProjectData = { + housingProject: { ...basic, ...housing, ...location, ...permits, - submissionId: uuidv4(), + housingProjectId: uuidv4(), activityId: activityId, submittedAt: data.submittedAt ?? new Date().toISOString(), // eslint-disable-next-line @typescript-eslint/no-explicit-any submittedBy: getCurrentUsername(currentContext), intakeStatus: intakeStatus, applicationStatus: data.applicationStatus ?? ApplicationStatus.NEW, - submissionType: data?.submissionType ?? SubmissionType.GUIDANCE - } as Submission, + housingProjectType: data?.housingProjectType ?? SubmissionType.GUIDANCE + } as HousingProject, appliedPermits, investigatePermits }; - controller.assignPriority(submissionData.submission); + controller.assignPriority(housingProjectData.housingProject); - return submissionData; + return housingProjectData; }, /** * @function emailConfirmation - * Send an email with the confirmation of submission + * Send an email with the confirmation of housing project */ emailConfirmation: async (req: Request, res: Response, next: NextFunction) => { try { @@ -204,10 +215,10 @@ const controller = { getActivityIds: async (req: Request, res: Response, next: NextFunction) => { try { - let response = await submissionService.getSubmissions(); + let response = await housingProjectService.getHousingProjects(); if (req.currentAuthorization?.attributes.includes('scope:self')) { response = response.filter( - (x: Submission) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() + (x: HousingProject) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() ); } res.status(200).json(response.map((x) => x.activityId)); @@ -216,11 +227,11 @@ const controller = { } }, - createSubmission: async (req: Request, res: Response, next: NextFunction) => { + createHousingProject: async (req: Request, res: Response, next: NextFunction) => { try { // TODO: Remove when create PUT calls get switched to POST if (req.body === undefined) req.body = {}; - const { submission, appliedPermits, investigatePermits } = await controller.generateSubmissionData( + const { housingProject, appliedPermits, investigatePermits } = await controller.generateHousingProjectData( req.body, IntakeStatus.SUBMITTED, req.currentContext @@ -228,11 +239,11 @@ const controller = { // Create contacts if (req.body.contacts) - await contactService.upsertContacts(req.body.contacts, req.currentContext, submission.activityId); + await contactService.upsertContacts(req.body.contacts, req.currentContext, housingProject.activityId); - // Create new submission - const result = await submissionService.createSubmission({ - ...submission, + // Create new housing project + const result = await housingProjectService.createHousingProject({ + ...housingProject, ...generateCreateStamps(req.currentContext) }); @@ -240,18 +251,18 @@ const controller = { await Promise.all(appliedPermits.map(async (x: Permit) => await permitService.createPermit(x))); await Promise.all(investigatePermits.map(async (x: Permit) => await permitService.createPermit(x))); - res.status(201).json({ activityId: result.activityId, submissionId: result.submissionId }); + res.status(201).json({ activityId: result.activityId, housingProjectId: result.housingProjectId }); } catch (e: unknown) { next(e); } }, - deleteSubmission: async (req: Request<{ submissionId: string }>, res: Response, next: NextFunction) => { + deleteHousingProject: async (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction) => { try { - const response = await submissionService.deleteSubmission(req.params.submissionId); + const response = await housingProjectService.deleteHousingProject(req.params.housingProjectId); if (!response) { - return res.status(404).json({ message: 'Submission not found' }); + return res.status(404).json({ message: 'Housing Project not found' }); } res.status(200).json(response); @@ -265,7 +276,7 @@ const controller = { const response = await draftService.deleteDraft(req.params.draftId); if (!response) { - return res.status(404).json({ message: 'Submission draft not found' }); + return res.status(404).json({ message: 'Housing Project draft not found' }); } res.status(200).json(response); @@ -286,7 +297,7 @@ const controller = { getDrafts: async (req: Request, res: Response, next: NextFunction) => { try { - let response = await draftService.getDrafts(DraftCode.SUBMISSION); + let response = await draftService.getDrafts(DraftCode.HOUSING_PROJECT); if (req.currentAuthorization?.attributes.includes('scope:self')) { response = response.filter((x: Draft) => x?.createdBy === req.currentContext.userId); @@ -300,19 +311,19 @@ const controller = { getStatistics: async (req: Request, res: Response, next: NextFunction) => { try { - const response = await submissionService.getStatistics(req.query); + const response = await housingProjectService.getStatistics(req.query); res.status(200).json(response[0]); } catch (e: unknown) { next(e); } }, - getSubmission: async (req: Request<{ submissionId: string }>, res: Response, next: NextFunction) => { + getHousingProject: async (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction) => { try { - const response = await submissionService.getSubmission(req.params.submissionId); + const response = await housingProjectService.getHousingProject(req.params.housingProjectId); if (!response) { - return res.status(404).json({ message: 'Submission not found' }); + return res.status(404).json({ message: 'Housing Project not found' }); } if (req.currentAuthorization?.attributes.includes('scope:self')) { @@ -332,13 +343,13 @@ const controller = { } }, - getSubmissions: async (req: Request, res: Response, next: NextFunction) => { + getHousingProjects: async (req: Request, res: Response, next: NextFunction) => { try { - let response = await submissionService.getSubmissions(); + let response = await housingProjectService.getHousingProjects(); if (req.currentAuthorization?.attributes.includes('scope:self')) { response = response.filter( - (x: Submission) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() + (x: HousingProject) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() ); } @@ -348,13 +359,13 @@ const controller = { } }, - searchSubmissions: async ( - req: Request, + searchHousingProjects: async ( + req: Request, res: Response, next: NextFunction ) => { try { - let response = await submissionService.searchSubmissions({ + let response = await housingProjectService.searchHousingProjects({ ...req.query, includeUser: isTruthy(req.query.includeUser), includeDeleted: isTruthy(req.query.includeDeleted) @@ -362,7 +373,7 @@ const controller = { if (req.currentAuthorization?.attributes.includes('scope:self')) { response = response.filter( - (x: Submission) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() + (x: HousingProject) => x?.submittedBy.toUpperCase() === getCurrentUsername(req.currentContext)?.toUpperCase() ); } @@ -372,9 +383,9 @@ const controller = { } }, - submitDraft: async (req: Request, res: Response, next: NextFunction) => { + submitDraft: async (req: Request, res: Response, next: NextFunction) => { try { - const { submission, appliedPermits, investigatePermits } = await controller.generateSubmissionData( + const { housingProject, appliedPermits, investigatePermits } = await controller.generateHousingProjectData( req.body, IntakeStatus.SUBMITTED, req.currentContext @@ -382,11 +393,11 @@ const controller = { // Create contacts if (req.body.contacts) - await contactService.upsertContacts(req.body.contacts, req.currentContext, submission.activityId); + await contactService.upsertContacts(req.body.contacts, req.currentContext, housingProject.activityId); - // Create new submission - const result = await submissionService.createSubmission({ - ...submission, + // Create new housing project + const result = await housingProjectService.createHousingProject({ + ...housingProject, ...generateCreateStamps(req.currentContext) }); @@ -397,7 +408,7 @@ const controller = { // Delete old draft if (req.body.draftId) await draftService.deleteDraft(req.body.draftId); - res.status(201).json({ activityId: result.activityId, submissionId: result.submissionId }); + res.status(201).json({ activityId: result.activityId, housingProjectId: result.housingProjectId }); } catch (e: unknown) { next(e); } @@ -424,7 +435,7 @@ const controller = { response = await draftService.createDraft({ draftId: uuidv4(), activityId: activityId, - draftCode: DraftCode.SUBMISSION, + draftCode: DraftCode.HOUSING_PROJECT, data: req.body.data, ...generateCreateStamps(req.currentContext) }); @@ -437,19 +448,19 @@ const controller = { }, updateIsDeletedFlag: async ( - req: Request<{ submissionId: string }, never, { isDeleted: boolean }>, + req: Request<{ housingProjectId: string }, never, { isDeleted: boolean }>, res: Response, next: NextFunction ) => { try { - const response = await submissionService.updateIsDeletedFlag( - req.params.submissionId, + const response = await housingProjectService.updateIsDeletedFlag( + req.params.housingProjectId, req.body.isDeleted, generateUpdateStamps(req.currentContext) ); if (!response) { - return res.status(404).json({ message: 'Submission not found' }); + return res.status(404).json({ message: 'Housing Project not found' }); } res.status(200).json(response); @@ -458,22 +469,22 @@ const controller = { } }, - updateSubmission: async (req: Request, res: Response, next: NextFunction) => { + updateHousingProject: async (req: Request, res: Response, next: NextFunction) => { try { - // If Navigator created empty submission we need to assign contactIds on save + // If Navigator created empty housing project we need to assign contactIds on save req.body.contacts = req.body.contacts.map((x) => { if (!x.contactId) x.contactId = uuidv4(); return x; }); await contactService.upsertContacts(req.body.contacts, req.currentContext, req.body.activityId); - const response = await submissionService.updateSubmission({ + const response = await housingProjectService.updateHousingProject({ ...req.body, ...generateUpdateStamps(req.currentContext) }); if (!response) { - return res.status(404).json({ message: 'Submission not found' }); + return res.status(404).json({ message: 'Housing Project not found' }); } res.status(200).json(response); diff --git a/app/src/controllers/index.ts b/app/src/controllers/index.ts index 816eb7758..701456aeb 100644 --- a/app/src/controllers/index.ts +++ b/app/src/controllers/index.ts @@ -3,6 +3,7 @@ export { default as atsController } from './ats'; export { default as contactController } from './contact'; export { default as documentController } from './document'; export { default as enquiryController } from './enquiry'; +export { default as housingProjectController } from './housingProject'; export { default as mapController } from './map'; export { default as noteController } from './note'; export { default as permitController } from './permit'; @@ -10,6 +11,5 @@ export { default as permitNoteController } from './permitNote'; export { default as reportingController } from './reporting'; export { default as roadmapController } from './roadmap'; export { default as ssoController } from './sso'; -export { default as submissionController } from './submission'; export { default as userController } from './user'; export { default as yarsController } from './yars'; diff --git a/app/src/controllers/map.ts b/app/src/controllers/map.ts index b1ecd0fed..f0a5a4214 100644 --- a/app/src/controllers/map.ts +++ b/app/src/controllers/map.ts @@ -1,13 +1,14 @@ -import { mapService, submissionService } from '../services'; +import { housingProjectService, mapService } from '../services'; + import type { NextFunction, Request, Response } from 'express'; const controller = { - getPIDs: async (req: Request<{ submissionId: string }>, res: Response, next: NextFunction) => { + getPIDs: async (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction) => { try { - const submission = await submissionService.getSubmission(req.params.submissionId); + const housingProject = await housingProjectService.getHousingProject(req.params.housingProjectId); let response; - if (submission?.geoJSON) response = await mapService.getPIDs(submission?.geoJSON); + if (housingProject?.geoJSON) response = await mapService.getPIDs(housingProject?.geoJSON); res.status(200).json(response); } catch (e: unknown) { diff --git a/app/src/controllers/note.ts b/app/src/controllers/note.ts index cf51f42a8..ee57a22d4 100644 --- a/app/src/controllers/note.ts +++ b/app/src/controllers/note.ts @@ -1,5 +1,5 @@ import { generateCreateStamps, generateUpdateStamps } from '../db/utils/utils'; -import { enquiryService, noteService, submissionService, userService } from '../services'; +import { enquiryService, housingProjectService, noteService, userService } from '../services'; import type { NextFunction, Request, Response } from 'express'; import type { BringForward, Note } from '../types'; @@ -40,7 +40,7 @@ const controller = { let response = new Array(); const notes = await noteService.listBringForward(req.query.bringForwardState); if (notes && notes.length) { - const submissions = await submissionService.searchSubmissions({ + const housingProjects = await housingProjectService.searchHousingProjects({ activityId: notes.map((x) => x.activityId) }); const users = await userService.searchUsers({ @@ -55,10 +55,10 @@ const controller = { response = notes.map((note) => ({ activityId: note.activityId, noteId: note.noteId as string, - submissionId: submissions.find((s) => s.activityId === note.activityId)?.submissionId as string, + housingProjectId: housingProjects.find((s) => s.activityId === note.activityId)?.housingProjectId as string, enquiryId: enquiries.find((s) => s.activityId === note.activityId)?.enquiryId as string, title: note.title, - projectName: submissions.find((s) => s.activityId === note.activityId)?.projectName ?? null, + projectName: housingProjects.find((s) => s.activityId === note.activityId)?.projectName ?? null, createdByFullName: users.find((u) => u?.userId === note.createdBy)?.fullName ?? null, bringForwardDate: note.bringForwardDate as string })); diff --git a/app/src/controllers/reporting.ts b/app/src/controllers/reporting.ts index bcb751640..73bfba357 100644 --- a/app/src/controllers/reporting.ts +++ b/app/src/controllers/reporting.ts @@ -3,9 +3,9 @@ import { reportingService } from '../services'; import type { NextFunction, Request, Response } from 'express'; const controller = { - getSubmissionPermitData: async (req: Request, res: Response, next: NextFunction) => { + getHousingProjectPermitData: async (req: Request, res: Response, next: NextFunction) => { try { - const response = await reportingService.getSubmissionPermitData(); + const response = await reportingService.getHousingProjectPermitData(); res.status(200).json(response); } catch (e: unknown) { next(e); diff --git a/app/src/db/migrations/20240904000000_009-rbac.ts b/app/src/db/migrations/20240904000000_009-rbac.ts index dd900d720..70fe76a12 100644 --- a/app/src/db/migrations/20240904000000_009-rbac.ts +++ b/app/src/db/migrations/20240904000000_009-rbac.ts @@ -29,7 +29,7 @@ const resources = [ name: Resource.SSO }, { - name: Resource.SUBMISSION + name: Resource.HOUSING_PROJECT }, { name: Resource.USER @@ -614,7 +614,7 @@ export async function up(knex: Knex): Promise { await addRolePolicies(Resource.PERMIT); await addRolePolicies(Resource.ROADMAP); await addRolePolicies(Resource.SSO); - await addRolePolicies(Resource.SUBMISSION); + await addRolePolicies(Resource.HOUSING_PROJECT); await addRolePolicies(Resource.USER); return knex('yars.role_policy').insert(items); @@ -698,7 +698,7 @@ export async function up(knex: Knex): Promise { await addResourceRoles(navigator_group_id[0].group_id, Resource.PERMIT, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(navigator_group_id[0].group_id, Resource.ROADMAP, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(navigator_group_id[0].group_id, Resource.SSO, [Action.READ]); - await addResourceRoles(navigator_group_id[0].group_id, Resource.SUBMISSION, [Action.CREATE, Action.READ, Action.UPDATE]); + await addResourceRoles(navigator_group_id[0].group_id, Resource.HOUSING_PROJECT, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(navigator_group_id[0].group_id, Resource.USER, [Action.READ]); // Add all navigator read only role mappings @@ -708,7 +708,7 @@ export async function up(knex: Knex): Promise { await addResourceRoles(navigator_read_group_id[0].group_id, Resource.PERMIT, [Action.READ]); await addResourceRoles(navigator_read_group_id[0].group_id, Resource.ROADMAP, [Action.READ]); await addResourceRoles(navigator_read_group_id[0].group_id, Resource.SSO, [Action.READ]); - await addResourceRoles(navigator_read_group_id[0].group_id, Resource.SUBMISSION, [Action.READ]); + await addResourceRoles(navigator_read_group_id[0].group_id, Resource.HOUSING_PROJECT, [Action.READ]); await addResourceRoles(navigator_read_group_id[0].group_id, Resource.USER, [Action.READ]); // Add all supervisor role mappings @@ -719,7 +719,7 @@ export async function up(knex: Knex): Promise { await addResourceRoles(superviser_group_id[0].group_id, Resource.PERMIT, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(superviser_group_id[0].group_id, Resource.ROADMAP, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(superviser_group_id[0].group_id, Resource.SSO, [Action.READ]); - await addResourceRoles(superviser_group_id[0].group_id, Resource.SUBMISSION, [Action.CREATE, Action.READ, Action.UPDATE]); + await addResourceRoles(superviser_group_id[0].group_id, Resource.HOUSING_PROJECT, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(superviser_group_id[0].group_id, Resource.USER, [Action.READ]); // Add all admin role mappings @@ -730,7 +730,7 @@ export async function up(knex: Knex): Promise { await addResourceRoles(admin_group_id[0].group_id, Resource.PERMIT, [Action.READ]); await addResourceRoles(admin_group_id[0].group_id, Resource.ROADMAP, [Action.READ]); await addResourceRoles(admin_group_id[0].group_id, Resource.SSO, [Action.READ]); - await addResourceRoles(admin_group_id[0].group_id, Resource.SUBMISSION, [Action.READ]); + await addResourceRoles(admin_group_id[0].group_id, Resource.HOUSING_PROJECT, [Action.READ]); await addResourceRoles(admin_group_id[0].group_id, Resource.USER, [Action.CREATE, Action.READ, Action.UPDATE]); // Add all proponent role mappings @@ -738,7 +738,7 @@ export async function up(knex: Knex): Promise { await addResourceRoles(proponent_group_id[0].group_id, Resource.ENQUIRY, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(proponent_group_id[0].group_id, Resource.NOTE, [Action.CREATE, Action.READ, Action.UPDATE]); await addResourceRoles(proponent_group_id[0].group_id, Resource.PERMIT, [Action.CREATE, Action.READ, Action.UPDATE]); - await addResourceRoles(proponent_group_id[0].group_id, Resource.SUBMISSION, [Action.CREATE, Action.READ, Action.UPDATE]); + await addResourceRoles(proponent_group_id[0].group_id, Resource.HOUSING_PROJECT, [Action.CREATE, Action.READ, Action.UPDATE]); } return knex('yars.group_role').insert(items); }) diff --git a/app/src/db/migrations/20250313000000_027-submission_rename.ts b/app/src/db/migrations/20250313000000_027-submission_rename.ts new file mode 100644 index 000000000..7c1633348 --- /dev/null +++ b/app/src/db/migrations/20250313000000_027-submission_rename.ts @@ -0,0 +1,262 @@ +/* eslint-disable max-len */ +import type { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + return ( + Promise.resolve() + .then(() => knex.schema.renameTable('submission', 'housing_project')) + + .then(() => + knex.schema.alterTable('housing_project', function (table) { + table.renameColumn('submission_id', 'housing_project_id'); + table.renameColumn('submission_type', 'housing_project_type'); + }) + ) + + .then(() => + knex('draft_code').where('draft_code', '=', 'SUBMISSION').update({ + draft_code: 'HOUSING_PROJECT' + }) + ) + + .then(() => + knex('yars.resource').where('name', '=', 'SUBMISSION').update({ + name: 'HOUSING_PROJECT' + }) + ) + + // Drop public schema functions + .then(() => + knex.schema.raw(`drop function public.get_activity_statistics( + date_from text, + date_to text, + month_year text, + user_id uuid + )`) + ) + + // Create public schema functions + .then(() => + knex.schema.raw(`create or replace function public.get_activity_statistics( + date_from text, + date_to text, + month_year text, + user_id uuid + ) + returns table (total_submissions bigint, total_submissions_between bigint, total_submissions_monthyear bigint, total_submissions_assignedto bigint, intake_submitted bigint, intake_assigned bigint, intake_completed bigint, state_new bigint, state_inprogress bigint, state_delayed bigint, state_completed bigint, supported_bc bigint, supported_indigenous bigint, supported_non_profit bigint, supported_housing_coop bigint, waiting_on bigint, queue_1 bigint, queue_2 bigint, queue_3 bigint, escalation bigint, general_enquiry bigint, guidance bigint, inapplicable bigint, status_request bigint, multi_permits_needed bigint) + language plpgsql + as $$ + begin + return query + with housing_project_counts as ( + select + count(*) as housing_project_count, + (select count(*) from public.housing_project where "submitted_at" between cast(date_from as timestamp) and cast(date_to as timestamp)) as housing_project_count_between, + (select count(*) from public.housing_project where extract(month from cast(month_year as timestamp)) = extract(month from "submitted_at") and extract(year from cast(month_year as timestamp)) = extract(year from "submitted_at")) as housing_project_count_monthyear, + (select count(*) from public.housing_project where "assigned_user_id" = user_id) as housing_project_count_assignedto, + count(*) filter (where hp."intake_status" = 'Submitted') as intake_submitted_housing_project_count, + count(*) filter (where hp."intake_status" = 'Assigned') as intake_assigned_housing_project_count, + count(*) filter (where hp."intake_status" = 'Completed') as intake_completed_housing_project_count, + count(*) filter (where hp."application_status" = 'New') as state_new_housing_project_count, + count(*) filter (where hp."application_status" = 'In Progress') as state_inprogress_housing_project_count, + count(*) filter (where hp."application_status" = 'Delayed') as state_delayed_housing_project_count, + count(*) filter (where hp."application_status" = 'Completed') as state_completed_housing_project_count, + count(*) filter (where hp."financially_supported_bc" = 'Yes') as supported_bc_housing_project_count, + count(*) filter (where hp."financially_supported_indigenous" = 'Yes') as supported_indigenous_housing_project_count, + count(*) filter (where hp."financially_supported_non_profit" = 'Yes') as supported_non_profit_housing_project_count, + count(*) filter (where hp."financially_supported_housing_coop" = 'Yes') as supported_housing_coop_housing_project_count, + count(*) filter (where hp."waiting_on" is not null) as waiting_on_housing_project_count, + count(*) filter (where hp."queue_priority" = 1) as queue_1_housing_project_count, + count(*) filter (where hp."queue_priority" = 2) as queue_2_housing_project_count, + count(*) filter (where hp."queue_priority" = 3) as queue_3_housing_project_count, + count(*) filter (where hp."housing_project_type" = 'Guidance') as guidance_housing_project_count, + count(*) filter (where hp."housing_project_type" = 'Inapplicable') as inapplicable_housing_project_count, + count(distinct hp.activity_id) filter (where permit_counts.permit_count > 1) as multi_permits_needed + from public.housing_project hp + join public.activity a on hp.activity_id = a.activity_id + left join ( + select p.activity_id, count(*) as permit_count + from public.permit p + where p.needed = 'Yes' + group by p.activity_id + ) permit_counts on permit_counts.activity_id = hp.activity_id + where a.is_deleted = false and hp.intake_status <> 'Draft' + ), + enquiry_counts as ( + select + count(*) as enquiry_count, + (select count(*) from public.enquiry where "submitted_at" between cast(date_from as timestamp) and cast(date_to as timestamp)) as enquiry_count_between, + (select count(*) from public.enquiry where extract(month from cast(month_year as timestamp)) = extract(month from "submitted_at") and extract(year from cast(month_year as timestamp)) = extract(year from "submitted_at")) as enquiry_count_monthyear, + (select count(*) from public.enquiry where "assigned_user_id" = user_id) as enquiry_count_assignedto, + count(*) filter (where e."intake_status" = 'Submitted') as intake_submitted_enquiry_count, + count(*) filter (where e."intake_status" = 'Assigned') as intake_assigned_enquiry_count, + count(*) filter (where e."intake_status" = 'Completed') as intake_completed_enquiry_count, + count(*) filter (where e."waiting_on" is not null) waiting_on_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Escalation') escalation_enquiry_count, + count(*) filter (where e."enquiry_type" = 'General enquiry') general_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Inapplicable') as inapplicable_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Status request') as status_request_enquiry_count + from public.enquiry e + join public.activity a on e.activity_id = a.activity_id + where a.is_deleted = false and e.intake_status <> 'Draft') + select + (housing_project_counts.housing_project_count + enquiry_counts.enquiry_count) AS total_submissions, + (housing_project_counts.housing_project_count_between + enquiry_counts.enquiry_count_between) AS total_submissions_between, + (housing_project_counts.housing_project_count_monthyear + enquiry_counts.enquiry_count_monthyear) AS total_submissions_monthyear, + (housing_project_counts.housing_project_count_assignedto + enquiry_counts.enquiry_count_assignedto) AS total_submissions_assignedto, + (housing_project_counts.intake_submitted_housing_project_count + enquiry_counts.intake_submitted_enquiry_count) AS intake_submitted, + (housing_project_counts.intake_assigned_housing_project_count + enquiry_counts.intake_assigned_enquiry_count) AS intake_assigned, + (housing_project_counts.intake_completed_housing_project_count + enquiry_counts.intake_completed_enquiry_count) AS intake_completed, + (housing_project_counts.state_new_housing_project_count) AS state_new, + (housing_project_counts.state_inprogress_housing_project_count) AS state_inprogress, + (housing_project_counts.state_delayed_housing_project_count) AS state_delayed, + (housing_project_counts.state_completed_housing_project_count) AS state_completed, + (housing_project_counts.supported_bc_housing_project_count) AS supported_bc, + (housing_project_counts.supported_indigenous_housing_project_count) AS supported_indigenous, + (housing_project_counts.supported_non_profit_housing_project_count) AS supported_non_profit, + (housing_project_counts.supported_housing_coop_housing_project_count) AS supported_housing_coop, + (housing_project_counts.waiting_on_housing_project_count + enquiry_counts.waiting_on_enquiry_count) AS waiting_on, + (housing_project_counts.queue_1_housing_project_count) AS queue_1, + (housing_project_counts.queue_2_housing_project_count) AS queue_2, + (housing_project_counts.queue_3_housing_project_count) AS queue_3, + (enquiry_counts.escalation_enquiry_count) AS escalation, + (enquiry_counts.general_enquiry_count) AS general_enquiry, + (housing_project_counts.guidance_housing_project_count) AS guidance, + (housing_project_counts.inapplicable_housing_project_count + enquiry_counts.inapplicable_enquiry_count) AS inapplicable, + (enquiry_counts.status_request_enquiry_count) AS status_request, + (housing_project_counts.multi_permits_needed) AS multi_permits_needed + from housing_project_counts, enquiry_counts; + end; $$`) + ) + ); +} + +export async function down(knex: Knex): Promise { + return ( + Promise.resolve() + // Drop public schema functions + .then(() => + knex.schema.raw(`drop function public.get_activity_statistics( + date_from text, + date_to text, + month_year text, + user_id uuid + )`) + ) + + // Create public schema functions + .then(() => + knex.schema.raw(`create or replace function public.get_activity_statistics( + date_from text, + date_to text, + month_year text, + user_id uuid + ) + returns table (total_submissions bigint, total_submissions_between bigint, total_submissions_monthyear bigint, total_submissions_assignedto bigint, intake_submitted bigint, intake_assigned bigint, intake_completed bigint, state_new bigint, state_inprogress bigint, state_delayed bigint, state_completed bigint, supported_bc bigint, supported_indigenous bigint, supported_non_profit bigint, supported_housing_coop bigint, waiting_on bigint, queue_1 bigint, queue_2 bigint, queue_3 bigint, escalation bigint, general_enquiry bigint, guidance bigint, inapplicable bigint, status_request bigint, multi_permits_needed bigint) + language plpgsql + as $$ + begin + return query + with submission_counts as ( + select + count(*) as submission_count, + (select count(*) from public.submission where "submitted_at" between cast(date_from as timestamp) and cast(date_to as timestamp)) as submission_count_between, + (select count(*) from public.submission where extract(month from cast(month_year as timestamp)) = extract(month from "submitted_at") and extract(year from cast(month_year as timestamp)) = extract(year from "submitted_at")) as submission_count_monthyear, + (select count(*) from public.submission where "assigned_user_id" = user_id) as submission_count_assignedto, + count(*) filter (where s."intake_status" = 'Submitted') as intake_submitted_submission_count, + count(*) filter (where s."intake_status" = 'Assigned') as intake_assigned_submission_count, + count(*) filter (where s."intake_status" = 'Completed') as intake_completed_submission_count, + count(*) filter (where s."application_status" = 'New') as state_new_submission_count, + count(*) filter (where s."application_status" = 'In Progress') as state_inprogress_submission_count, + count(*) filter (where s."application_status" = 'Delayed') as state_delayed_submission_count, + count(*) filter (where s."application_status" = 'Completed') as state_completed_submission_count, + count(*) filter (where s."financially_supported_bc" = 'Yes') as supported_bc_submission_count, + count(*) filter (where s."financially_supported_indigenous" = 'Yes') as supported_indigenous_submission_count, + count(*) filter (where s."financially_supported_non_profit" = 'Yes') as supported_non_profit_submission_count, + count(*) filter (where s."financially_supported_housing_coop" = 'Yes') as supported_housing_coop_submission_count, + count(*) filter (where s."waiting_on" is not null) as waiting_on_submission_count, + count(*) filter (where s."queue_priority" = 1) as queue_1_submission_count, + count(*) filter (where s."queue_priority" = 2) as queue_2_submission_count, + count(*) filter (where s."queue_priority" = 3) as queue_3_submission_count, + count(*) filter (where s."submission_type" = 'Guidance') as guidance_submission_count, + count(*) filter (where s."submission_type" = 'Inapplicable') as inapplicable_submission_count, + count(distinct s.activity_id) filter (where permit_counts.permit_count > 1) as multi_permits_needed + from public.submission s + join public.activity a on s.activity_id = a.activity_id + left join ( + select p.activity_id, count(*) as permit_count + from public.permit p + where p.needed = 'Yes' + group by p.activity_id + ) permit_counts on permit_counts.activity_id = s.activity_id + where a.is_deleted = false and s.intake_status <> 'Draft' + ), + enquiry_counts as ( + select + count(*) as enquiry_count, + (select count(*) from public.enquiry where "submitted_at" between cast(date_from as timestamp) and cast(date_to as timestamp)) as enquiry_count_between, + (select count(*) from public.enquiry where extract(month from cast(month_year as timestamp)) = extract(month from "submitted_at") and extract(year from cast(month_year as timestamp)) = extract(year from "submitted_at")) as enquiry_count_monthyear, + (select count(*) from public.enquiry where "assigned_user_id" = user_id) as enquiry_count_assignedto, + count(*) filter (where e."intake_status" = 'Submitted') as intake_submitted_enquiry_count, + count(*) filter (where e."intake_status" = 'Assigned') as intake_assigned_enquiry_count, + count(*) filter (where e."intake_status" = 'Completed') as intake_completed_enquiry_count, + count(*) filter (where e."waiting_on" is not null) waiting_on_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Escalation') escalation_enquiry_count, + count(*) filter (where e."enquiry_type" = 'General enquiry') general_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Inapplicable') as inapplicable_enquiry_count, + count(*) filter (where e."enquiry_type" = 'Status request') as status_request_enquiry_count + from public.enquiry e + join public.activity a on e.activity_id = a.activity_id + where a.is_deleted = false and e.intake_status <> 'Draft') + select + (submission_counts.submission_count + enquiry_counts.enquiry_count) AS total_submissions, + (submission_counts.submission_count_between + enquiry_counts.enquiry_count_between) AS total_submissions_between, + (submission_counts.submission_count_monthyear + enquiry_counts.enquiry_count_monthyear) AS total_submissions_monthyear, + (submission_counts.submission_count_assignedto + enquiry_counts.enquiry_count_assignedto) AS total_submissions_assignedto, + (submission_counts.intake_submitted_submission_count + enquiry_counts.intake_submitted_enquiry_count) AS intake_submitted, + (submission_counts.intake_assigned_submission_count + enquiry_counts.intake_assigned_enquiry_count) AS intake_assigned, + (submission_counts.intake_completed_submission_count + enquiry_counts.intake_completed_enquiry_count) AS intake_completed, + (submission_counts.state_new_submission_count) AS state_new, + (submission_counts.state_inprogress_submission_count) AS state_inprogress, + (submission_counts.state_delayed_submission_count) AS state_delayed, + (submission_counts.state_completed_submission_count) AS state_completed, + (submission_counts.supported_bc_submission_count) AS supported_bc, + (submission_counts.supported_indigenous_submission_count) AS supported_indigenous, + (submission_counts.supported_non_profit_submission_count) AS supported_non_profit, + (submission_counts.supported_housing_coop_submission_count) AS supported_housing_coop, + (submission_counts.waiting_on_submission_count + enquiry_counts.waiting_on_enquiry_count) AS waiting_on, + (submission_counts.queue_1_submission_count) AS queue_1, + (submission_counts.queue_2_submission_count) AS queue_2, + (submission_counts.queue_3_submission_count) AS queue_3, + (enquiry_counts.escalation_enquiry_count) AS escalation, + (enquiry_counts.general_enquiry_count) AS general_enquiry, + (submission_counts.guidance_submission_count) AS guidance, + (submission_counts.inapplicable_submission_count + enquiry_counts.inapplicable_enquiry_count) AS inapplicable, + (enquiry_counts.status_request_enquiry_count) AS status_request, + (submission_counts.multi_permits_needed) AS multi_permits_needed + from submission_counts, enquiry_counts; + end; $$`) + ) + + .then(() => + knex('yars.resource').where('name', '=', 'HOUSING_PROJECT').update({ + name: 'SUBMISSION' + }) + ) + + .then(() => + knex('draft_code').where('draft_code', '=', 'HOUSING_PROJECT').update({ + draft_code: 'SUBMISSION' + }) + ) + + .then(() => + knex.schema.alterTable('housing_project', function (table) { + table.renameColumn('housing_project_type', 'submission_type'); + table.renameColumn('housing_project_id', 'submission_id'); + }) + ) + + .then(() => knex.schema.renameTable('housing_project', 'submission')) + ); +} diff --git a/app/src/db/models/submission.ts b/app/src/db/models/housing_project.ts similarity index 76% rename from app/src/db/models/submission.ts rename to app/src/db/models/housing_project.ts index 38b66d3c3..4a9949cef 100644 --- a/app/src/db/models/submission.ts +++ b/app/src/db/models/housing_project.ts @@ -6,11 +6,11 @@ import user from './user'; import { BasicResponse } from '../../utils/enums/application'; import type { Stamps } from '../stamps'; -import type { Submission } from '../../types'; +import type { HousingProject } from '../../types'; // Define types -const _submission = Prisma.validator()({}); -const _submissionWithContactGraph = Prisma.validator()({ +const _housingProject = Prisma.validator()({}); +const _housingProjectWithContactGraph = Prisma.validator()({ include: { activity: { include: { @@ -23,7 +23,7 @@ const _submissionWithContactGraph = Prisma.validator()({ +const _housingProjectWithUserGraph = Prisma.validator()({ include: { activity: { include: { @@ -38,15 +38,15 @@ const _submissionWithUserGraph = Prisma.validator( } }); -type PrismaRelationSubmission = Omit, keyof Stamps>; -type PrismaGraphSubmission = Prisma.submissionGetPayload; -type PrismaGraphSubmissionWithContact = Prisma.submissionGetPayload; -type PrismaGraphSubmissionWithUser = Prisma.submissionGetPayload; +type PrismaRelationHousingProject = Omit, keyof Stamps>; +type PrismaGraphHousingProject = Prisma.housing_projectGetPayload; +type PrismaGraphHousingProjectWithContact = Prisma.housing_projectGetPayload; +type PrismaGraphHousingProjectWithUser = Prisma.housing_projectGetPayload; export default { - toPrismaModel(input: Submission): PrismaRelationSubmission { + toPrismaModel(input: HousingProject): PrismaRelationHousingProject { return { - submission_id: input.submissionId, + housing_project_id: input.housingProjectId, activity_id: input.activityId, assigned_user_id: input.assignedUserId, project_name: input.projectName, @@ -96,13 +96,13 @@ export default { indigenous_description: input.indigenousDescription, non_profit_description: input.nonProfitDescription, housing_coop_description: input.housingCoopDescription, - submission_type: input.submissionType + housing_project_type: input.housingProjectType }; }, - fromPrismaModel(input: PrismaGraphSubmission): Submission { + fromPrismaModel(input: PrismaGraphHousingProject): HousingProject { return { - submissionId: input.submission_id, + housingProjectId: input.housing_project_id, activityId: input.activity_id, assignedUserId: input.assigned_user_id, submittedAt: input.submitted_at?.toISOString() as string, @@ -151,7 +151,7 @@ export default { indigenousDescription: input.indigenous_description, nonProfitDescription: input.non_profit_description, housingCoopDescription: input.housing_coop_description, - submissionType: input.submission_type, + housingProjectType: input.housing_project_type, relatedEnquiries: null, createdBy: input.created_by, updatedAt: input.updated_at?.toISOString() as string, @@ -160,21 +160,21 @@ export default { }; }, - fromPrismaModelWithContact(input: PrismaGraphSubmissionWithContact): Submission { - const submission = this.fromPrismaModel(input); - if (submission && input.activity.activity_contact) { - submission.contacts = input.activity.activity_contact.map((x) => contact.fromPrismaModel(x.contact)); + fromPrismaModelWithContact(input: PrismaGraphHousingProjectWithContact): HousingProject { + const housingProject = this.fromPrismaModel(input); + if (housingProject && input.activity.activity_contact) { + housingProject.contacts = input.activity.activity_contact.map((x) => contact.fromPrismaModel(x.contact)); } - return submission; + return housingProject; }, - fromPrismaModelWithUser(input: PrismaGraphSubmissionWithUser): Submission { - const submission = this.fromPrismaModelWithContact(input); - if (submission && input.user) { - submission.user = user.fromPrismaModel(input.user); + fromPrismaModelWithUser(input: PrismaGraphHousingProjectWithUser): HousingProject { + const housingProject = this.fromPrismaModelWithContact(input); + if (housingProject && input.user) { + housingProject.user = user.fromPrismaModel(input.user); } - return submission; + return housingProject; } }; diff --git a/app/src/db/models/index.ts b/app/src/db/models/index.ts index 908e40085..b43183875 100644 --- a/app/src/db/models/index.ts +++ b/app/src/db/models/index.ts @@ -5,10 +5,10 @@ export { default as document } from './document'; export { default as draft } from './draft'; export { default as email_log } from './email_log'; export { default as enquiry } from './enquiry'; +export { default as housing_project } from './housing_project'; export { default as identity_provider } from './identity_provider'; export { default as note } from './note'; export { default as permit } from './permit'; export { default as permit_note } from './permit_note'; export { default as permit_type } from './permit_type'; -export { default as submission } from './submission'; export { default as user } from './user'; diff --git a/app/src/db/prisma/schema.prisma b/app/src/db/prisma/schema.prisma index 3c87ab07f..fb384e2ff 100644 --- a/app/src/db/prisma/schema.prisma +++ b/app/src/db/prisma/schema.prisma @@ -37,9 +37,9 @@ model activity { document document[] draft draft[] enquiry enquiry[] + housing_project housing_project[] note note[] permit permit[] - submission submission[] @@schema("public") } @@ -239,88 +239,24 @@ model permit_type { @@schema("public") } -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -model submission { - submission_id String @id @db.Uuid - activity_id String - assigned_user_id String? @db.Uuid - submitted_at DateTime @db.Timestamptz(6) - submitted_by String - location_pids String? - company_name_registered String? - project_name String? - single_family_units String? - street_address String? - latitude Decimal? @db.Decimal(8, 6) - longitude Decimal? @db.Decimal(9, 6) - queue_priority Int? - related_permits String? - ast_notes String? - ast_updated Boolean @default(false) - added_to_ats Boolean @default(false) - ats_client_id Int? - ltsa_completed Boolean @default(false) - bc_online_completed Boolean @default(false) - natural_disaster Boolean @default(false) - financially_supported Boolean @default(false) - financially_supported_bc String? - financially_supported_indigenous String? - financially_supported_non_profit String? - financially_supported_housing_coop String? - aai_updated Boolean @default(false) - waiting_on String? - intake_status String? - application_status String? - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - has_rental_units String? - project_description String? - project_applicant_type String? - is_developed_in_bc String? - multi_family_units String? - other_units String? - other_units_description String? - rental_units String? - project_location String? - project_location_description String? - locality String? - province String? - has_applied_provincial_permits String? - check_provincial_permits String? - indigenous_description String? - non_profit_description String? - housing_coop_description String? - submission_type String? - consent_to_feedback Boolean @default(false) - geo_json Json? @db.Json - geomark_url String? - activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "submission_activity_id_foreign") - user user? @relation(fields: [assigned_user_id], references: [user_id], onDelete: Cascade, map: "submission_assigned_user_id_foreign") - - @@schema("public") -} - model user { - user_id String @id @db.Uuid - idp String? - sub String @unique(map: "user_sub_unique") - email String? - first_name String? - full_name String? - last_name String? - active Boolean @default(true) - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - bceid_business_name String? - access_request access_request[] - contact contact[] - enquiry enquiry[] - submission submission[] - identity_provider identity_provider? @relation(fields: [idp], references: [idp], onDelete: Cascade, map: "user_idp_foreign") + user_id String @id @db.Uuid + idp String? + sub String @unique(map: "user_sub_unique") + email String? + first_name String? + full_name String? + last_name String? + active Boolean @default(true) + created_by String? @default("00000000-0000-0000-0000-000000000000") + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_by String? + updated_at DateTime? @db.Timestamptz(6) + access_request access_request[] + contact contact[] + enquiry enquiry[] + housing_project housing_project[] + identity_provider identity_provider? @relation(fields: [idp], references: [idp], onDelete: Cascade, map: "user_idp_foreign") @@index([email], map: "user_email_index") @@index([sub], map: "user_username_index") @@ -525,6 +461,69 @@ model email_log { @@schema("public") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model housing_project { + housing_project_id String @id(map: "submission_pkey") @db.Uuid + activity_id String + assigned_user_id String? @db.Uuid + submitted_at DateTime @db.Timestamptz(6) + submitted_by String + location_pids String? + company_name_registered String? + project_name String? + single_family_units String? + street_address String? + latitude Decimal? @db.Decimal(8, 6) + longitude Decimal? @db.Decimal(9, 6) + queue_priority Int? + related_permits String? + ast_notes String? + ast_updated Boolean @default(false) + added_to_ats Boolean @default(false) + ats_client_id Int? + ltsa_completed Boolean @default(false) + bc_online_completed Boolean @default(false) + natural_disaster Boolean @default(false) + financially_supported Boolean @default(false) + financially_supported_bc String? + financially_supported_indigenous String? + financially_supported_non_profit String? + financially_supported_housing_coop String? + aai_updated Boolean @default(false) + waiting_on String? + intake_status String? + application_status String? + created_by String? @default("00000000-0000-0000-0000-000000000000") + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_by String? + updated_at DateTime? @db.Timestamptz(6) + has_rental_units String? + project_description String? + project_applicant_type String? + is_developed_in_bc String? + multi_family_units String? + other_units String? + other_units_description String? + rental_units String? + project_location String? + project_location_description String? + locality String? + province String? + has_applied_provincial_permits String? + check_provincial_permits String? + indigenous_description String? + non_profit_description String? + housing_coop_description String? + housing_project_type String? + consent_to_feedback Boolean @default(false) + geo_json Json? @db.Json + activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "submission_activity_id_foreign") + user user? @relation(fields: [assigned_user_id], references: [user_id], onDelete: Cascade, map: "submission_assigned_user_id_foreign") + + @@schema("public") +} + view group_role_policy_vw { row_number BigInt @unique group_id Int? diff --git a/app/src/docs/v1.api-spec.yaml b/app/src/docs/v1.api-spec.yaml index 1103b5f1e..a4cc9931a 100644 --- a/app/src/docs/v1.api-spec.yaml +++ b/app/src/docs/v1.api-spec.yaml @@ -187,13 +187,13 @@ paths: $ref: '#/components/parameters/Query-UserId' responses: '200': - description: A list of submissions matching the search criteria + description: A list of contacts matching the search criteria content: application/json: schema: type: array items: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-Contact' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -505,13 +505,13 @@ paths: $ref: '#/components/parameters/IntakeStatus' responses: '200': - description: A list of submissions matching the search criteria + description: A list of enquiries matching the search criteria content: application/json: schema: type: array items: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-Enquiry' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -856,15 +856,15 @@ paths: $ref: '#/components/responses/UnprocessableEntity' default: $ref: '#/components/responses/Error' - /reporting/submission/permit: + /reporting/housingProject/permit: get: - summary: Query and retrieve all submission data related to permits - operationId: getSubmissionPermitData + summary: Query and retrieve all housing project data related to permits + operationId: getHousingProjectPermitData tags: - Reporting responses: '200': - description: A list of submissions joined with permits and permit type + description: A list of housing projects joined with permits and permit type content: application/json: schema: @@ -904,21 +904,21 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission: + /housingProject: get: - summary: Get a list of submissions - operationId: getSubmissions + summary: Get a list of housing projects + operationId: getHousingProjects tags: - - Submission + - Housing Project responses: '200': - description: A list of submissions + description: A list of housing projects content: application/json: schema: type: array items: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -926,13 +926,13 @@ paths: default: $ref: '#/components/responses/Error' put: - summary: Create a submission - operationId: createSubmission + summary: Create a housing project + operationId: createHousingProject tags: - - Submission + - Housing Project responses: '201': - description: Submission created successfully + description: Housing Project created successfully content: application/json: schema: @@ -945,21 +945,21 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission/{submissionId}: + /housingProject/{housingProjectId}: get: - summary: Get a specific submission - operationId: getSubmission + summary: Get a specific housing project + operationId: getHousingProject tags: - - Submission + - Housing Project parameters: - $ref: '#/components/parameters/Path-SubmissionId' responses: '200': - description: Details of the specific submission + description: Details of the specific housing project content: application/json: schema: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -971,10 +971,10 @@ paths: default: $ref: '#/components/responses/Error' put: - summary: Update a submission - operationId: updateSubmission + summary: Update a housing project + operationId: updateHousingProject tags: - - Submission + - Housing Project parameters: - $ref: '#/components/parameters/Path-SubmissionId' requestBody: @@ -985,11 +985,11 @@ paths: $ref: '#/components/schemas/Request-SubmissionUpdate' responses: '200': - description: Submission updated successfully + description: Housing Project updated successfully content: application/json: schema: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -1001,19 +1001,19 @@ paths: default: $ref: '#/components/responses/Error' delete: - summary: Delete a submission - operationId: deleteSubmission + summary: Delete a housing project + operationId: deleteHousingProject tags: - - Submission + - Housing Project parameters: - $ref: '#/components/parameters/Path-SubmissionId' responses: '200': - description: Submission deleted successfully + description: Housing Project deleted successfully content: application/json: schema: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -1024,11 +1024,11 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission/{submissionId}/delete: + /housingProject/{housingProjectId}/delete: patch: summary: Soft delete - description: A soft delete of a submission using a is_deleted flag - operationId: submission-updateIsDeletedFlag + description: A soft delete of a housing project using a is_deleted flag + operationId: housingProject-updateIsDeletedFlag tags: - Submission parameters: @@ -1052,7 +1052,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -1063,15 +1063,15 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission/draft: + /housingProject/draft: put: - summary: Create or update a draft submission + summary: Create or update a draft housing project description: >- Creates or updates an intake and set status to Draft - so unfinished/unvalidatd submissions can be saved - operationId: submission-createDraft + so unfinished/unvalidatd housing projects can be saved + operationId: housingProject-createDraft tags: - - Submission + - Housing Project requestBody: required: true content: @@ -1080,7 +1080,7 @@ paths: $ref: '#/components/schemas/Request-SaveSubmissionDraft' responses: '201': - description: Draft submission created successfully + description: Draft housing project created successfully content: application/json: schema: @@ -1091,11 +1091,11 @@ paths: $ref: '#/components/responses/Forbidden' default: $ref: '#/components/responses/Error' - /submission/draft/submit: + /housingProject/draft/submit: put: - summary: Submit a draft submission + summary: Submit a draft housing project description: Creates or updates an intake and set status to Submitted - operationId: submission-updateDraft + operationId: housingProject-updateDraft tags: - Submission requestBody: @@ -1106,11 +1106,11 @@ paths: $ref: '#/components/schemas/Request-SaveSubmissionDraft' responses: '200': - description: Draft submission updated successfully + description: Draft housing project updated successfully content: application/json: schema: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -1119,7 +1119,7 @@ paths: $ref: '#/components/responses/NotFound' default: $ref: '#/components/responses/Error' - /submission/email: + /housingProject/email: put: summary: Send confirmation Email operationId: emailConfirmation @@ -1146,10 +1146,10 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission/search: + /housingProject/search: get: - summary: Search submissions - description: Returns a list of submissions based on the given queries + summary: Search housing projects + description: Returns a list of housing projects based on the given queries operationId: searchSubmissions tags: - Submission @@ -1173,7 +1173,7 @@ paths: items: $ref: '#/components/parameters/Query-CreatedBy' - in: query - name: submissionId + name: housingProjectId required: false schema: type: array @@ -1193,18 +1193,18 @@ paths: required: false schema: type: array - description: The submission types to filter on + description: The housing project types to filter on items: $ref: '#/components/parameters/Query-SubmissionType' responses: '200': - description: A list of submissions matching the search criteria + description: A list of housing projects matching the search criteria content: application/json: schema: type: array items: - $ref: '#/components/schemas/DB-Submission' + $ref: '#/components/schemas/DB-HousingProject' "401": $ref: "#/components/responses/Unauthorized" '403': @@ -1213,10 +1213,10 @@ paths: $ref: "#/components/responses/UnprocessableEntity" default: $ref: '#/components/responses/Error' - /submission/statistics: + /housingProject/statistics: get: - summary: Get submission and enquiry statistics - description: Gets a set of submission and enquiry related statistics + summary: Get housing project and enquiry statistics + description: Gets a set of housing project and enquiry related statistics operationId: getStatistics tags: - Submission @@ -1343,7 +1343,7 @@ components: example: da5c5031-0e84-4234-8559-1ba846997482 Path-SubmissionId: in: path - name: submissionId + name: housingProjectId description: UUID of a Submission required: true schema: @@ -1791,10 +1791,10 @@ components: type: string description: Type of permit type example: Alteration - DB-Submission: + DB-HousingProject: type: object required: - - submissionId + - housingProjectId - activityId - submittedAt - submittedBy @@ -1804,9 +1804,9 @@ components: - naturalDisaster - aaiUpdated properties: - submissionId: + housingProjectId: type: string - description: UUID for the submission + description: UUID for the housing project format: uuid example: d95f1de6-698b-4d9d-b938-487eb446ace8 activityId: @@ -1815,7 +1815,7 @@ components: example: D95F1DE6 assignedUserId: type: string - description: User assigned to the submission + description: User assigned to the housing project example: Smith, John submittedAt: type: string @@ -1824,7 +1824,7 @@ components: example: '2024-04-03T00:57:09.070Z' submittedBy: type: string - description: Who made the submission + description: Who made the housing project example: JSMITH locationPIDs: type: string @@ -1987,7 +1987,7 @@ components: description: Description for COOP Housing consentToFeedback: type: boolean - description: Whether or not user wants feedback on submission + description: Whether or not user consents to feedback requests DB-User: type: object properties: @@ -2415,9 +2415,9 @@ components: type: array items: $ref: '#/components/schemas/DB-Permit' - submissionId: + housingProjectId: type: string - description: UUID for the submission + description: UUID for the housing project format: uuid example: d95f1de6-698b-4d9d-b938-487eb446ace8 activityId: @@ -2431,7 +2431,7 @@ components: example: '2024-04-03T00:57:09.070Z' submittedBy: type: string - description: Who made the submission + description: Who made the housing project example: JSMITH intakeStatus: type: string @@ -2452,7 +2452,7 @@ components: - activityId - intakeStatus - includeUser - - submissionId + - housingProjectId - submissionType properties: activityId: @@ -2465,11 +2465,11 @@ components: example: Assigned includeUser: type: string - description: If set it retrieves only this user's submissions + description: If set it retrieves only this user's housing projects example: Smith, John - submissionId: + housingProjectId: type: string - description: UUID of submission + description: UUID of housing project format: Uuid example: ac246e31-c807-496c-bc93-cd8bc2f1b2b4 submissionType: @@ -2480,7 +2480,7 @@ components: title: Request to Update Submission type: object required: - - submissionId + - housingProjectId - activityId - queuePriority - submissionType @@ -2497,9 +2497,9 @@ components: - bcOnlineCompleted - aaiUpdated properties: - submissionId: + housingProjectId: type: string - description: UUID for the submission + description: UUID for the housing project format: uuid example: d95f1de6-698b-4d9d-b938-487eb446ace8 activityId: @@ -2673,7 +2673,7 @@ components: example: Submitted assignedUserId: type: string - description: User ID assigned to the submission + description: User ID assigned to the housing project format: uuid example: 123e4567-e89b-12d3-a456-426614174000 applicationStatus: @@ -2717,9 +2717,9 @@ components: description: UUID of a note format: uuid example: 17e5c858-66c1-49ba-bb2e-2b286525f532 - submissionId: + housingProjectId: type: string - description: UUID of a submission + description: UUID of a housing project format: uuid example: 17e5c858-66c1-49ba-bb2e-2b2865635421 enquiryId: @@ -2770,11 +2770,11 @@ components: type: object required: - activityId - - submissionId + - housingProjectId properties: - submissionId: + housingProjectId: type: string - description: UUID for the submission + description: UUID for the housing project format: uuid example: d95f1de6-698b-4d9d-b938-487eb446ace8 activityId: @@ -2891,15 +2891,15 @@ components: type: object required: - activityId - - submissionId + - housingProjectId properties: activityId: type: string description: Activity ID example: D95F1DE6 - submissionId: + housingProjectId: type: string - description: UUID for the submission + description: UUID for the housing project format: uuid example: d95f1de6-698b-4d9d-b938-487eb446ace8 Response-SubmissionPermitData: @@ -2911,32 +2911,32 @@ components: example: New Project consentToFeedback: type: boolean - description: Whether or not user wants feedback on submission + description: Whether or not user consents to feedback requests firstName: type: string - description: First name of the contact person related to submission + description: First name of the contact person related to housing project example: John lastName: type: string - description: Last name of the contact person related to submission + description: Last name of the contact person related to housing project example: Smith phoneNumber: type: string - description: Phone number for contact related to submission + description: Phone number for contact related to housing project example: (123) 456-7890 email: type: string format: email - description: Email address for contact related to submission + description: Email address for contact related to housing project example: john.smith@example.com contactPreference: type: string enum: [Phone call, Email, Either] - description: Preferred method of contact related to submission + description: Preferred method of contact related to housing project example: Email activityId: type: string - description: Activity ID related to submission and permit + description: Activity ID related to housing project and permit example: D95F1DE6 locality: type: string @@ -2944,7 +2944,7 @@ components: example: Maple Ridge streetAddress: type: string - description: Street address of project + description: Street address of project example: 2975 Jutland Rd latitude: type: number diff --git a/app/src/middleware/authorization.ts b/app/src/middleware/authorization.ts index 14e790c92..90458b554 100644 --- a/app/src/middleware/authorization.ts +++ b/app/src/middleware/authorization.ts @@ -6,9 +6,9 @@ import { documentService, draftService, enquiryService, + housingProjectService, noteService, permitService, - submissionService, userService, yarsService } from '../services'; @@ -111,9 +111,9 @@ const paramMap = new Map any>([ ['documentId', documentService.getDocument], ['draftId', draftService.getDraft], ['enquiryId', enquiryService.getEnquiry], + ['housingProjectId', housingProjectService.getHousingProject], ['noteId', noteService.getNote], - ['permitId', permitService.getPermit], - ['submissionId', submissionService.getSubmission] + ['permitId', permitService.getPermit] ]); /** @@ -163,7 +163,7 @@ export const hasAccessPermit = (param: string) => { if (func) data = await func(id); if (!data || data?.createdBy !== userId) { - const submission = (await submissionService.searchSubmissions({ activityId: [data.activityId] }))[0]; + const submission = (await housingProjectService.searchHousingProjects({ activityId: [data.activityId] }))[0]; if ( !submission || submission?.submittedBy.toUpperCase() !== getCurrentUsername(req.currentContext)?.toUpperCase() diff --git a/app/src/routes/v1/housingProject.ts b/app/src/routes/v1/housingProject.ts new file mode 100644 index 000000000..008da463b --- /dev/null +++ b/app/src/routes/v1/housingProject.ts @@ -0,0 +1,179 @@ +import express from 'express'; + +import { housingProjectController } from '../../controllers'; +import { hasAccess, hasAuthorization } from '../../middleware/authorization'; +import { requireSomeAuth } from '../../middleware/requireSomeAuth'; +import { requireSomeGroup } from '../../middleware/requireSomeGroup'; +import { Action, Resource } from '../../utils/enums/application'; +import { housingProjectValidator } from '../../validators'; + +import type { NextFunction, Request, Response } from 'express'; +import type { + Draft, + Email, + StatisticsFilters, + HousingProject, + HousingProjectIntake, + HousingProjectSearchParameters +} from '../../types'; + +const router = express.Router(); +router.use(requireSomeAuth); +router.use(requireSomeGroup); + +/** Gets a list of housing projects */ +router.get( + '/', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.getHousingProjects(req, res, next); + } +); + +/** Get a list of all the activityIds */ +router.get( + '/activityIds', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.getActivityIds(req, res, next); + } +); + +/** Search housing projects */ +router.get( + '/search', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + housingProjectValidator.searcHousingProjects, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.searchHousingProjects(req, res, next); + } +); + +/** Gets housing project statistics*/ +router.get( + '/statistics', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + housingProjectValidator.getStatistics, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.getStatistics(req, res, next); + } +); + +/** Gets a list of housing project drafts */ +router.get( + '/draft/:draftId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + hasAccess('draftId'), + (req: Request<{ draftId: string }>, res: Response, next: NextFunction): void => { + housingProjectController.getDraft(req, res, next); + } +); + +/** Gets a housing project draft */ +router.get( + '/draft', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.getDrafts(req, res, next); + } +); + +/** Creates or updates an intake and set status to Draft */ +router.put( + '/draft', + hasAuthorization(Resource.HOUSING_PROJECT, Action.CREATE), + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.updateDraft(req, res, next); + } +); + +/** Creates or updates an intake and set status to Submitted */ +router.put( + '/draft/submit', + hasAuthorization(Resource.HOUSING_PROJECT, Action.CREATE), + housingProjectValidator.createHousingProject, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.submitDraft(req, res, next); + } +); + +// Send an email with the confirmation of housing project +router.put( + '/email', + hasAuthorization(Resource.HOUSING_PROJECT, Action.CREATE), + housingProjectValidator.emailConfirmation, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.emailConfirmation(req, res, next); + } +); + +/** Creates a blank housing project */ +router.put( + '/', + hasAuthorization(Resource.HOUSING_PROJECT, Action.CREATE), + housingProjectValidator.createHousingProject, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.createHousingProject(req, res, next); + } +); + +/** Hard deletes a housing project */ +router.delete( + '/:housingProjectId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.DELETE), + hasAccess('housingProjectId'), + housingProjectValidator.deleteHousingProject, + (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction): void => { + housingProjectController.deleteHousingProject(req, res, next); + } +); + +/** Hard deletes a housing project draft */ +router.delete( + '/draft/:draftId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.DELETE), + hasAccess('draftId'), + housingProjectValidator.deleteDraft, + (req: Request<{ draftId: string }>, res: Response, next: NextFunction): void => { + housingProjectController.deleteDraft(req, res, next); + } +); + +/** Gets a specific housing project */ +router.get( + '/:housingProjectId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + //hasAccess('housingProjectId'), // TODO: Temp fix to check submittedBy in controller until we're off chefs + housingProjectValidator.getHousingProject, + (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction): void => { + housingProjectController.getHousingProject(req, res, next); + } +); + +/** Updates a housing project*/ +router.put( + '/:housingProjectId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.UPDATE), + hasAccess('housingProjectId'), + housingProjectValidator.updateHousingProject, + (req: Request, res: Response, next: NextFunction): void => { + housingProjectController.updateHousingProject(req, res, next); + } +); + +/** Updates is_deleted flag for a housing project */ +router.patch( + '/:housingProjectId/delete', + hasAuthorization(Resource.HOUSING_PROJECT, Action.DELETE), + hasAccess('housingProjectId'), + housingProjectValidator.updateIsDeletedFlag, + ( + req: Request<{ housingProjectId: string }, never, { isDeleted: boolean }>, + res: Response, + next: NextFunction + ): void => { + housingProjectController.updateIsDeletedFlag(req, res, next); + } +); + +export default router; diff --git a/app/src/routes/v1/index.ts b/app/src/routes/v1/index.ts index b51d2310a..9331ddeac 100644 --- a/app/src/routes/v1/index.ts +++ b/app/src/routes/v1/index.ts @@ -8,13 +8,13 @@ import contact from './contact'; import docs from './docs'; import document from './document'; import enquiry from './enquiry'; +import housingProject from './housingProject'; import map from './map'; import note from './note'; import permit from './permit'; import reporting from './reporting'; import roadmap from './roadmap'; import sso from './sso'; -import submission from './submission'; import user from './user'; import yars from './yars'; @@ -39,7 +39,7 @@ router.get('/', (_req, res) => { '/reporting', '/roadmap', '/sso', - '/submission', + '/housingProject', '/user', '/yars' ] @@ -58,7 +58,7 @@ router.use('/permit', permit); router.use('/reporting', reporting); router.use('/roadmap', roadmap); router.use('/sso', sso); -router.use('/submission', submission); +router.use('/housingProject', housingProject); router.use('/user', user); router.use('/yars', yars); diff --git a/app/src/routes/v1/map.ts b/app/src/routes/v1/map.ts index 0c8f702e5..a46eae8de 100644 --- a/app/src/routes/v1/map.ts +++ b/app/src/routes/v1/map.ts @@ -13,9 +13,9 @@ router.use(requireSomeAuth); router.use(requireSomeGroup); router.get( - '/pids/:submissionId', - hasAuthorization(Resource.SUBMISSION, Action.READ), - (req: Request<{ submissionId: string }>, res: Response, next: NextFunction): void => { + '/pids/:housingProjectId', + hasAuthorization(Resource.HOUSING_PROJECT, Action.READ), + (req: Request<{ housingProjectId: string }>, res: Response, next: NextFunction): void => { mapController.getPIDs(req, res, next); } ); diff --git a/app/src/routes/v1/reporting.ts b/app/src/routes/v1/reporting.ts index 6b5397aea..e68e67d30 100644 --- a/app/src/routes/v1/reporting.ts +++ b/app/src/routes/v1/reporting.ts @@ -12,12 +12,12 @@ const router = express.Router(); router.use(requireSomeAuth); router.use(requireSomeGroup); -/** Get all submission and permit data for csv download */ +/** Get all housing project and permit data for csv download */ router.get( - '/submission/permit', + '/housingProject/permit', hasAuthorization(Resource.REPORTING, Action.READ), (req: Request, res: Response, next: NextFunction): void => { - reportingController.getSubmissionPermitData(req, res, next); + reportingController.getHousingProjectPermitData(req, res, next); } ); diff --git a/app/src/routes/v1/submission.ts b/app/src/routes/v1/submission.ts deleted file mode 100644 index f610e4e35..000000000 --- a/app/src/routes/v1/submission.ts +++ /dev/null @@ -1,184 +0,0 @@ -import express from 'express'; - -import { submissionController } from '../../controllers'; -import { hasAccess, hasAuthorization } from '../../middleware/authorization'; -import { requireSomeAuth } from '../../middleware/requireSomeAuth'; -import { requireSomeGroup } from '../../middleware/requireSomeGroup'; -import { Action, Resource } from '../../utils/enums/application'; -import { submissionValidator } from '../../validators'; - -import type { NextFunction, Request, Response } from 'express'; -import type { - Draft, - Email, - StatisticsFilters, - Submission, - SubmissionIntake, - SubmissionSearchParameters -} from '../../types'; - -const router = express.Router(); -router.use(requireSomeAuth); -router.use(requireSomeGroup); - -/** Gets a list of submissions */ -router.get( - '/', - hasAuthorization(Resource.SUBMISSION, Action.READ), - (req: Request, res: Response, next: NextFunction): void => { - submissionController.getSubmissions(req, res, next); - } -); - -/** Get a list of all the activityIds */ -router.get( - '/activityIds', - hasAuthorization(Resource.SUBMISSION, Action.READ), - (req: Request, res: Response, next: NextFunction): void => { - submissionController.getActivityIds(req, res, next); - } -); - -/** Search submissions */ -router.get( - '/search', - hasAuthorization(Resource.SUBMISSION, Action.READ), - submissionValidator.searchSubmissions, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.searchSubmissions(req, res, next); - } -); - -/** Gets submission statistics*/ -router.get( - '/statistics', - hasAuthorization(Resource.SUBMISSION, Action.READ), - submissionValidator.getStatistics, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.getStatistics(req, res, next); - } -); - -/** Gets a list of submission drafts */ -router.get( - '/draft/:draftId', - hasAuthorization(Resource.SUBMISSION, Action.READ), - hasAccess('draftId'), - (req: Request<{ draftId: string }>, res: Response, next: NextFunction): void => { - submissionController.getDraft(req, res, next); - } -); - -/** Gets a list of submission drafts */ -router.get( - '/draft', - hasAuthorization(Resource.SUBMISSION, Action.READ), - (req: Request, res: Response, next: NextFunction): void => { - submissionController.getDrafts(req, res, next); - } -); - -/** Creates or updates an intake and set status to Draft */ -router.put( - '/draft', - hasAuthorization(Resource.SUBMISSION, Action.CREATE), - (req: Request, res: Response, next: NextFunction): void => { - submissionController.updateDraft(req, res, next); - } -); - -/** Creates or updates an intake and set status to Submitted */ -router.put( - '/draft/submit', - hasAuthorization(Resource.SUBMISSION, Action.CREATE), - submissionValidator.createSubmission, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.submitDraft(req, res, next); - } -); - -// Send an email with the confirmation of submission -router.put( - '/email', - hasAuthorization(Resource.SUBMISSION, Action.CREATE), - submissionValidator.emailConfirmation, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.emailConfirmation(req, res, next); - } -); - -/** Creates a blank submission */ -router.put( - '/', - hasAuthorization(Resource.SUBMISSION, Action.CREATE), - submissionValidator.createSubmission, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.createSubmission(req, res, next); - } -); - -/** Hard deletes a submission */ -router.delete( - '/:submissionId', - hasAuthorization(Resource.SUBMISSION, Action.DELETE), - hasAccess('submissionId'), - submissionValidator.deleteSubmission, - (req: Request<{ submissionId: string }>, res: Response, next: NextFunction): void => { - submissionController.deleteSubmission(req, res, next); - } -); - -/** Hard deletes a submission draft */ -router.delete( - '/draft/:draftId', - hasAuthorization(Resource.SUBMISSION, Action.DELETE), - hasAccess('draftId'), - submissionValidator.deleteDraft, - (req: Request<{ draftId: string }>, res: Response, next: NextFunction): void => { - submissionController.deleteDraft(req, res, next); - } -); - -/** Search all submissions */ -router.get( - '/search', - hasAuthorization(Resource.SUBMISSION, Action.READ), - (req: Request, res: Response, next: NextFunction): void => { - submissionController.searchSubmissions(req, res, next); - } -); - -/** Gets a specific submission */ -router.get( - '/:submissionId', - hasAuthorization(Resource.SUBMISSION, Action.READ), - //hasAccess('submissionId'), // TODO: Temp fix to check submittedBy in controller until we're off chefs - submissionValidator.getSubmission, - (req: Request<{ submissionId: string }>, res: Response, next: NextFunction): void => { - submissionController.getSubmission(req, res, next); - } -); - -/** Updates a submission*/ -router.put( - '/:submissionId', - hasAuthorization(Resource.SUBMISSION, Action.UPDATE), - hasAccess('submissionId'), - submissionValidator.updateSubmission, - (req: Request, res: Response, next: NextFunction): void => { - submissionController.updateSubmission(req, res, next); - } -); - -/** Updates is_deleted flag for a submission */ -router.patch( - '/:submissionId/delete', - hasAuthorization(Resource.SUBMISSION, Action.DELETE), - hasAccess('submissionId'), - submissionValidator.updateIsDeletedFlag, - (req: Request<{ submissionId: string }, never, { isDeleted: boolean }>, res: Response, next: NextFunction): void => { - submissionController.updateIsDeletedFlag(req, res, next); - } -); - -export default router; diff --git a/app/src/services/submission.ts b/app/src/services/housingProject.ts similarity index 65% rename from app/src/services/submission.ts rename to app/src/services/housingProject.ts index bd86f2578..6d29cebf3 100644 --- a/app/src/services/submission.ts +++ b/app/src/services/housingProject.ts @@ -4,14 +4,14 @@ import config from 'config'; import { Prisma } from '@prisma/client'; import prisma from '../db/dataConnection'; -import { submission } from '../db/models'; +import { housing_project } from '../db/models'; import { BasicResponse, Initiative } from '../utils/enums/application'; import { ApplicationStatus } from '../utils/enums/housing'; import { getChefsApiKey } from '../utils/utils'; import type { AxiosInstance, AxiosRequestConfig } from 'axios'; import type { IStamps } from '../interfaces/IStamps'; -import type { Submission, SubmissionSearchParameters } from '../types'; +import type { HousingProject, HousingProjectSearchParameters } from '../types'; /** * @function chefsAxios @@ -30,13 +30,13 @@ function chefsAxios(formId: string, options: AxiosRequestConfig = {}): AxiosInst const service = { /** - * @function createSubmission - * Creates a new submission - * @returns {Promise>} The result of running the transaction + * @function createHousingProject + * Creates a new housing project + * @returns {Promise>} The result of running the transaction */ - createSubmission: async (data: Partial) => { - const s = submission.toPrismaModel(data as Submission); - const response = await prisma.submission.create({ + createHousingProject: async (data: Partial) => { + const s = housing_project.toPrismaModel(data as HousingProject); + const response = await prisma.housing_project.create({ data: { ...s, geo_json: s.geo_json as Prisma.InputJsonValue, @@ -55,16 +55,16 @@ const service = { } } }); - return submission.fromPrismaModelWithContact(response); + return housing_project.fromPrismaModelWithContact(response); }, /** - * @function createSubmissionsFromExport - * Creates the given activities and submissions from exported CHEFS data - * @param {Array>} submissions Array of Submissions + * @function createHousingProjectsFromExport + * Creates the given activities and housing projects from exported CHEFS data + * @param {Array>} housingProjects Array of housing projects * @returns {Promise} The result of running the transaction */ - createSubmissionsFromExport: async (submissions: Array>) => { + createHousingProjectsFromExport: async (housingProjects: Array>) => { await prisma.$transaction(async (trx) => { const initiative = await trx.initiative.findFirstOrThrow({ where: { @@ -73,16 +73,16 @@ const service = { }); await trx.activity.createMany({ - data: submissions.map((x) => ({ + data: housingProjects.map((x) => ({ activity_id: x.activityId as string, initiative_id: initiative.initiative_id, is_deleted: false })) }); - await trx.submission.createMany({ - data: submissions.map((x) => ({ - submission_id: x.submissionId as string, + await trx.housing_project.createMany({ + data: housingProjects.map((x) => ({ + housing_project_id: x.housingProjectId as string, activity_id: x.activityId as string, application_status: ApplicationStatus.NEW, company_name_registered: x.companyNameRegistered, @@ -123,17 +123,17 @@ const service = { }, /** - * @function deleteSubmission - * Deletes the submission, followed by the associated activity + * @function deleteHousingProject + * Deletes the housing project, followed by the associated activity * This action will cascade delete across all linked items - * @param {string} submissionId Submission ID - * @returns {Promise} The result of running the delete operation + * @param {string} housingProjectId Hosuing Project ID + * @returns {Promise} The result of running the delete operation */ - deleteSubmission: async (submissionId: string) => { + deleteHousingProject: async (housingProjectId: string) => { const response = await prisma.$transaction(async (trx) => { - const del = await trx.submission.delete({ + const del = await trx.housing_project.delete({ where: { - submission_id: submissionId + housing_project_id: housingProjectId }, include: { activity: { @@ -157,11 +157,11 @@ const service = { return del; }); - return submission.fromPrismaModelWithContact(response); + return housing_project.fromPrismaModelWithContact(response); }, /** - * @function getSubmission + * @function getFormExport * Gets a full data export for the requested CHEFS form * @param {string} formId CHEFS form id * @returns {Promise} The result of running the get operation @@ -179,7 +179,7 @@ const service = { /** * @function getStatistics - * Gets a set of submission related statistics + * Gets a set of housing project related statistics * @returns {Promise} The result of running the query */ getStatistics: async (filters: { dateFrom: string; dateTo: string; monthYear: string; userId: string }) => { @@ -203,16 +203,16 @@ const service = { }, /** - * @function getSubmission - * Gets a specific submission from the PCNS database - * @param {string} submissionId PCNS Submission ID - * @returns {Promise} The result of running the findFirst operation + * @function getHousingProject + * Gets a specific housing project from the PCNS database + * @param {string} housingProjectId PCNS housing project ID + * @returns {Promise} The result of running the findFirst operation */ - getSubmission: async (submissionId: string) => { + getHousingProject: async (housingProjectId: string) => { try { - const result = await prisma.submission.findFirst({ + const result = await prisma.housing_project.findFirst({ where: { - submission_id: submissionId + housing_project_id: housingProjectId }, include: { activity: { @@ -227,20 +227,20 @@ const service = { } }); - return result ? submission.fromPrismaModelWithContact(result) : null; + return result ? housing_project.fromPrismaModelWithContact(result) : null; } catch (e: unknown) { throw e; } }, /** - * @function getSubmissions - * Gets a list of submissions - * @returns {Promise<(Submission | null)[]>} The result of running the findMany operation + * @function getHousingProjects + * Gets a list of housing projects + * @returns {Promise<(HousingProject | null)[]>} The result of running the findMany operation */ - getSubmissions: async () => { + getHousingProjects: async () => { try { - const result = await prisma.submission.findMany({ + const result = await prisma.housing_project.findMany({ include: { activity: { include: { @@ -258,26 +258,28 @@ const service = { } }); - return result.map((x) => submission.fromPrismaModelWithUser(x)); + return result.map((x) => housing_project.fromPrismaModelWithUser(x)); } catch (e: unknown) { throw e; } }, + /* eslint-disable max-len */ /** - * @function searchSubmissions - * Search and filter for specific submission + * @function searchHousingProjects + * Search and filter for specific housing projects * @param {string[]} [params.activityId] Optional array of uuids representing the activity ID - * @param {string[]} [params.createdBy] Optional array of uuids representing users who created submissions + * @param {string[]} [params.createdBy] Optional array of uuids representing users who created housing projects + * @param {string[]} [params.housingProjectId] Optional array of uuids representing the housing project ID + * @param {string[]} [params.housingProjectType] Optional array of strings representing the housing project type * @param {string[]} [params.intakeStatus] Optional array of strings representing the intake status - * @param {boolean} [params.includeDeleted] Optional bool representing whether deleted submissions should be included + * @param {boolean} [params.includeDeleted] Optional bool representing whether deleted housing projects should be included * @param {boolean} [params.includeUser] Optional boolean representing whether the linked user should be included - * @param {string[]} [params.submissionId] Optional array of uuids representing the submission ID - * @param {string[]} [params.submissionType] Optional array of strings representing the submission type - * @returns {Promise<(Submission | null)[]>} The result of running the findMany operation + * @returns {Promise<(HousingProject | null)[]>} The result of running the findMany operation */ - searchSubmissions: async (params: SubmissionSearchParameters) => { - let result = await prisma.submission.findMany({ + /* eslint-enable max-len */ + searchHousingProjects: async (params: HousingProjectSearchParameters) => { + let result = await prisma.housing_project.findMany({ include: { activity: { include: { @@ -299,40 +301,40 @@ const service = { created_by: { in: params.createdBy } }, { - intake_status: { in: params.intakeStatus } + housing_project_id: { in: params.housingProjectId } }, { - submission_id: { in: params.submissionId } + housing_project_type: { in: params.housingProjectType } }, { - submission_type: { in: params.submissionType } + intake_status: { in: params.intakeStatus } }, params.includeDeleted ? {} : { activity: { is_deleted: false } } ] } }); - // Remove soft deleted submissions + // Remove soft deleted housing projects if (!params.includeDeleted) result = result.filter((x) => !x.activity.is_deleted); - const submissions = params.includeUser - ? result.map((x) => submission.fromPrismaModelWithUser(x)) - : result.map((x) => submission.fromPrismaModelWithContact(x)); + const housingProjects = params.includeUser + ? result.map((x) => housing_project.fromPrismaModelWithUser(x)) + : result.map((x) => housing_project.fromPrismaModelWithContact(x)); - return submissions; + return housingProjects; }, /** * @function updateIsDeletedFlag * Updates is_deleted flag for the corresponding activity - * @param {string} submissionId Submission ID + * @param {string} housingProjectId Housing project ID * @param {string} isDeleted flag - * @returns {Promise} The result of running the delete operation + * @returns {Promise} The result of running the delete operation */ - updateIsDeletedFlag: async (submissionId: string, isDeleted: boolean, updateStamp: Partial) => { - const deleteSubmission = await prisma.submission.findUnique({ + updateIsDeletedFlag: async (housingProjectId: string, isDeleted: boolean, updateStamp: Partial) => { + const deleteHousingProject = await prisma.housing_project.findUnique({ where: { - submission_id: submissionId + housing_project_id: housingProjectId }, include: { activity: { @@ -347,27 +349,27 @@ const service = { } }); - if (deleteSubmission) { + if (deleteHousingProject) { await prisma.activity.update({ data: { is_deleted: isDeleted, updated_at: updateStamp.updatedAt, updated_by: updateStamp.updatedBy }, where: { - activity_id: deleteSubmission?.activity_id + activity_id: deleteHousingProject?.activity_id } }); - return submission.fromPrismaModelWithContact(deleteSubmission); + return housing_project.fromPrismaModelWithContact(deleteHousingProject); } }, /** - * @function updateSubmission - * Updates a specific submission - * @param {Submission} data Submission to update - * @returns {Promise} The result of running the update operation + * @function updateHousingProject + * Updates a specific housing project + * @param {HousingProject} data Housing project to update + * @returns {Promise} The result of running the update operation */ - updateSubmission: async (data: Submission) => { + updateHousingProject: async (data: HousingProject) => { try { - const s = submission.toPrismaModel(data); - const result = await prisma.submission.update({ + const s = housing_project.toPrismaModel(data); + const result = await prisma.housing_project.update({ data: { ...s, geo_json: s.geo_json as Prisma.InputJsonValue, @@ -375,7 +377,7 @@ const service = { updated_by: data.updatedBy }, where: { - submission_id: data.submissionId + housing_project_id: data.housingProjectId }, include: { activity: { @@ -389,7 +391,7 @@ const service = { } } }); - return submission.fromPrismaModelWithContact(result); + return housing_project.fromPrismaModelWithContact(result); } catch (e: unknown) { throw e; } diff --git a/app/src/services/index.ts b/app/src/services/index.ts index f351e9574..67dab48b1 100644 --- a/app/src/services/index.ts +++ b/app/src/services/index.ts @@ -7,6 +7,7 @@ export { default as documentService } from './document'; export { default as draftService } from './draft'; export { default as emailService } from './email'; export { default as enquiryService } from './enquiry'; +export { default as housingProjectService } from './housingProject'; export { default as initiativeService } from './initiative'; export { default as mapService } from './map'; export { default as noteService } from './note'; @@ -14,6 +15,5 @@ export { default as permitService } from './permit'; export { default as permitNoteService } from './permitNote'; export { default as reportingService } from './reporting'; export { default as ssoService } from './sso'; -export { default as submissionService } from './submission'; export { default as userService } from './user'; export { default as yarsService } from './yars'; diff --git a/app/src/services/reporting.ts b/app/src/services/reporting.ts index 49d348711..b39326e73 100644 --- a/app/src/services/reporting.ts +++ b/app/src/services/reporting.ts @@ -1,11 +1,11 @@ import prisma from '../db/dataConnection'; const service = { - getSubmissionPermitData: async () => { + getHousingProjectPermitData: async () => { const result = await prisma.$queryRaw` select project_name, case - when s.consent_to_feedback then 'Yes' + when hp.consent_to_feedback then 'Yes' else 'No' end as consent_to_feedback, c.first_name, @@ -14,13 +14,13 @@ const service = { c.email, c.contact_preference, c.contact_applicant_relationship, - s.activity_id, + hp.activity_id, street_address, locality, latitude, longitude, location_pids, - submission_type, + housing_project_type, intake_status, application_status, p.issued_permit_id, @@ -41,11 +41,11 @@ const service = { pt.tracked_in_ats, pt.source_system, pt.source_system_acronym - from submission as s - join activity as a on s.activity_id = a.activity_id - join activity_contact as ac on s.activity_id = ac.activity_id + from housing_project as hp + join activity as a on hp.activity_id = a.activity_id + join activity_contact as ac on hp.activity_id = ac.activity_id join contact as c on ac.contact_id = c.contact_id - join permit as p on s.activity_id = p.activity_id + join permit as p on hp.activity_id = p.activity_id join permit_type pt on p.permit_type_id = pt.permit_type_id where a.is_deleted = false`; diff --git a/app/src/types/BringForward.ts b/app/src/types/BringForward.ts index a895eadd0..bde93ebc2 100644 --- a/app/src/types/BringForward.ts +++ b/app/src/types/BringForward.ts @@ -1,7 +1,7 @@ export type BringForward = { activityId: string; noteId: string; - submissionId: string; + housingProjectId: string; title: string; projectName: string | null; bringForwardDate: string; diff --git a/app/src/types/Submission.ts b/app/src/types/HousingProject.ts similarity index 94% rename from app/src/types/Submission.ts rename to app/src/types/HousingProject.ts index 728e3f0aa..929b8414f 100644 --- a/app/src/types/Submission.ts +++ b/app/src/types/HousingProject.ts @@ -5,8 +5,8 @@ import { IStamps } from '../interfaces/IStamps'; import type { Contact } from './Contact'; import type { User } from './User'; -export type Submission = { - submissionId: string; // Primary key +export type HousingProject = { + housingProjectId: string; // Primary key activityId: string; assignedUserId: string | null; submittedAt: string; @@ -55,7 +55,7 @@ export type Submission = { indigenousDescription: string | null; nonProfitDescription: string | null; housingCoopDescription: string | null; - submissionType: string | null; + housingProjectType: string | null; relatedEnquiries: string | null; contacts: Array; user: User | null; diff --git a/app/src/types/SubmissionIntake.ts b/app/src/types/HousingProjectIntake.ts similarity index 95% rename from app/src/types/SubmissionIntake.ts rename to app/src/types/HousingProjectIntake.ts index 45c189df3..f5e431e0a 100644 --- a/app/src/types/SubmissionIntake.ts +++ b/app/src/types/HousingProjectIntake.ts @@ -2,12 +2,12 @@ import { Contact } from './Contact'; import { Permit } from './Permit'; import { ApplicationStatus, SubmissionType } from '../utils/enums/housing'; -export type SubmissionIntake = { +export type HousingProjectIntake = { activityId?: string; draftId?: string; submittedAt?: string; applicationStatus?: ApplicationStatus; - submissionType?: SubmissionType; + housingProjectType?: SubmissionType; basic?: { consentToFeedback?: boolean; diff --git a/app/src/types/SubmissionSearchParameters.ts b/app/src/types/HousingProjectSearchParameters.ts similarity index 54% rename from app/src/types/SubmissionSearchParameters.ts rename to app/src/types/HousingProjectSearchParameters.ts index f1dc90b40..ac0c842fc 100644 --- a/app/src/types/SubmissionSearchParameters.ts +++ b/app/src/types/HousingProjectSearchParameters.ts @@ -1,9 +1,9 @@ -export type SubmissionSearchParameters = { +export type HousingProjectSearchParameters = { activityId?: Array; createdBy?: Array; + housingProjectId?: Array; + housingProjectType?: Array; intakeStatus?: Array; includeUser?: boolean; - submissionId?: Array; - submissionType?: Array; includeDeleted?: boolean; }; diff --git a/app/src/types/index.ts b/app/src/types/index.ts index 0ba5eb094..3ce772620 100644 --- a/app/src/types/index.ts +++ b/app/src/types/index.ts @@ -19,6 +19,9 @@ export type { Enquiry } from './Enquiry'; export type { EnquiryIntake } from './EnquiryIntake'; export type { EnquirySearchParameters } from './EnquirySearchParameters'; export type { EmailAttachment } from './EmailAttachment'; +export type { HousingProject } from './HousingProject'; +export type { HousingProjectIntake } from './HousingProjectIntake'; +export type { HousingProjectSearchParameters } from './HousingProjectSearchParameters'; export type { IdentityProvider } from './IdentityProvider'; export type { IdirSearchParameters } from './IdirSearchParameters'; export type { IdpAttributes } from './IdpAttributes'; @@ -29,9 +32,6 @@ export type { Permit } from './Permit'; export type { PermitNote } from './PermitNote'; export type { PermitType } from './PermitType'; export type { StatisticsFilters } from './StatisticsFilters'; -export type { Submission } from './Submission'; -export type { SubmissionIntake } from './SubmissionIntake'; -export type { SubmissionSearchParameters } from './SubmissionSearchParameters'; export type { User } from './User'; export type { UserAccessRequest } from './UserAccessRequest'; export type { UserSearchParameters } from './UserSearchParameters'; diff --git a/app/src/utils/constants/housing.ts b/app/src/utils/constants/housing.ts index 2e64b3ea8..69a3b10f8 100644 --- a/app/src/utils/constants/housing.ts +++ b/app/src/utils/constants/housing.ts @@ -60,6 +60,8 @@ export const ENQUIRY_TYPE_LIST = [ SubmissionType.INAPPLICABLE ]; +export const HOUSING_PROJECT_TYPE_LIST = [SubmissionType.GUIDANCE, SubmissionType.INAPPLICABLE]; + export const INTAKE_STATUS_LIST = [IntakeStatus.SUBMITTED, IntakeStatus.ASSIGNED, IntakeStatus.COMPLETED]; export const NOTE_TYPE_LIST = [NoteType.GENERAL, NoteType.BRING_FORWARD, NoteType.ENQUIRY, NoteType.ROADMAP]; @@ -112,5 +114,3 @@ export const PERMIT_STATUS_LIST = [ export const PROJECT_LOCATION_LIST = [ProjectLocation.LOCATION_COORDINATES, ProjectLocation.STREET_ADDRESS]; export const QUEUE_PRIORITY = [1, 2, 3]; - -export const SUBMISSION_TYPE_LIST = [SubmissionType.GUIDANCE, SubmissionType.INAPPLICABLE]; diff --git a/app/src/utils/enums/application.ts b/app/src/utils/enums/application.ts index c90080428..577a94585 100644 --- a/app/src/utils/enums/application.ts +++ b/app/src/utils/enums/application.ts @@ -51,12 +51,12 @@ export enum Resource { CONTACT = 'CONTACT', DOCUMENT = 'DOCUMENT', ENQUIRY = 'ENQUIRY', + HOUSING_PROJECT = 'HOUSING_PROJECT', NOTE = 'NOTE', PERMIT = 'PERMIT', REPORTING = 'REPORTING', ROADMAP = 'ROADMAP', SSO = 'SSO', - SUBMISSION = 'SUBMISSION', USER = 'USER' } diff --git a/app/src/utils/enums/housing.ts b/app/src/utils/enums/housing.ts index e97d9e866..e66a4cba7 100644 --- a/app/src/utils/enums/housing.ts +++ b/app/src/utils/enums/housing.ts @@ -21,7 +21,7 @@ export enum ContactPreference { } export enum DraftCode { - SUBMISSION = 'SUBMISSION' + HOUSING_PROJECT = 'HOUSING_PROJECT' } export enum EnquirySubmittedMethod { diff --git a/app/src/validators/submission.ts b/app/src/validators/housingProject.ts similarity index 87% rename from app/src/validators/submission.ts rename to app/src/validators/housingProject.ts index e110dce8a..25c1b4349 100644 --- a/app/src/validators/submission.ts +++ b/app/src/validators/housingProject.ts @@ -12,15 +12,15 @@ import { validate } from '../middleware/validation'; import { YES_NO_LIST, YES_NO_UNSURE_LIST } from '../utils/constants/application'; import { APPLICATION_STATUS_LIST, + HOUSING_PROJECT_TYPE_LIST, INTAKE_STATUS_LIST, - NUM_RESIDENTIAL_UNITS_LIST, - SUBMISSION_TYPE_LIST + NUM_RESIDENTIAL_UNITS_LIST } from '../utils/constants/housing'; import { BasicResponse } from '../utils/enums/application'; import { IntakeStatus } from '../utils/enums/housing'; const schema = { - createSubmission: { + createHousingProject: { body: Joi.object({ draftId: uuidv4.allow(null), activityId: Joi.string().min(8).max(8).allow(null), @@ -46,9 +46,9 @@ const schema = { to: Joi.array().items(email).required() }) }, - deleteSubmission: { + deleteHousingProject: { params: Joi.object({ - submissionId: uuidv4.required() + housingProjectId: uuidv4.required() }) }, deleteDraft: { @@ -64,39 +64,39 @@ const schema = { userId: uuidv4.allow(null) }) }, - getSubmission: { + getHousingProject: { params: Joi.object({ - submissionId: uuidv4.required() + housingProjectId: uuidv4.required() }) }, - searchSubmissions: { + searchHousingProjects: { query: Joi.object({ activityId: Joi.array().items(Joi.string()), createdBy: Joi.array().items(Joi.string()), intakeStatus: Joi.array().items(...INTAKE_STATUS_LIST), includeUser: Joi.boolean(), includeDeleted: Joi.boolean(), - submissionId: Joi.array().items(uuidv4), - submissionType: Joi.array().items(...SUBMISSION_TYPE_LIST) + housingProjectId: Joi.array().items(uuidv4), + housingProjectType: Joi.array().items(...HOUSING_PROJECT_TYPE_LIST) }) }, updateIsDeletedFlag: { params: Joi.object({ - submissionId: uuidv4.required() + housingProjectId: uuidv4.required() }), body: Joi.object({ isDeleted: Joi.boolean().required() }) }, - updateSubmission: { + updateHousingProject: { body: Joi.object({ - submissionId: uuidv4.required(), + housingProjectId: uuidv4.required(), activityId: activityId, consentToFeedback: Joi.boolean(), queuePriority: Joi.number().required().integer().min(0).max(3), - submissionType: Joi.string() + housingProjectType: Joi.string() .required() - .valid(...SUBMISSION_TYPE_LIST), + .valid(...HOUSING_PROJECT_TYPE_LIST), submittedAt: Joi.string().required(), relatedEnquiries: Joi.string().allow(null), companyNameRegistered: Joi.string().allow(null), @@ -187,19 +187,19 @@ const schema = { contacts: contacts }), params: Joi.object({ - submissionId: uuidv4.required() + housingProjectId: uuidv4.required() }) } }; export default { - createSubmission: validate(schema.createSubmission), + createHousingProject: validate(schema.createHousingProject), emailConfirmation: validate(schema.emailConfirmation), - deleteSubmission: validate(schema.deleteSubmission), + deleteHousingProject: validate(schema.deleteHousingProject), deleteDraft: validate(schema.deleteDraft), getStatistics: validate(schema.getStatistics), - getSubmission: validate(schema.getSubmission), - searchSubmissions: validate(schema.searchSubmissions), + getHousingProject: validate(schema.getHousingProject), + searcHousingProjects: validate(schema.searchHousingProjects), updateIsDeletedFlag: validate(schema.updateIsDeletedFlag), - updateSubmission: validate(schema.updateSubmission) + updateHousingProject: validate(schema.updateHousingProject) }; diff --git a/app/src/validators/index.ts b/app/src/validators/index.ts index d49bf1182..171db2a68 100644 --- a/app/src/validators/index.ts +++ b/app/src/validators/index.ts @@ -3,9 +3,9 @@ export { default as atsValidator } from './ats'; export { default as contactValidator } from './contact'; export { default as documentValidator } from './document'; export { default as enquiryValidator } from './enquiry'; +export { default as housingProjectValidator } from './housingProject'; export { default as noteValidator } from './note'; export { default as permitValidator } from './permit'; export { default as permitNoteValidator } from './permitNote'; export { default as roadmapValidator } from './roadmap'; -export { default as submissionValidator } from './submission'; export { default as userValidator } from './user'; diff --git a/app/tests/unit/controllers/note.spec.ts b/app/tests/unit/controllers/note.spec.ts index 59398e38f..b094dd8b0 100644 --- a/app/tests/unit/controllers/note.spec.ts +++ b/app/tests/unit/controllers/note.spec.ts @@ -1,5 +1,5 @@ import { noteController } from '../../../src/controllers'; -import { enquiryService, noteService, submissionService, userService } from '../../../src/services'; +import { enquiryService, housingProjectService, noteService, userService } from '../../../src/services'; // Mock config library - @see {@link https://stackoverflow.com/a/64819698} jest.mock('config'); @@ -110,7 +110,7 @@ describe('listBringForward', () => { // Mock service calls const listSpy = jest.spyOn(noteService, 'listBringForward'); - const searchSubmissionsSpy = jest.spyOn(submissionService, 'searchSubmissions'); + const searchHousingProjectsSpy = jest.spyOn(housingProjectService, 'searchHousingProjects'); const searchEnquiries = jest.spyOn(enquiryService, 'searchEnquiries'); const searchUsersSpy = jest.spyOn(userService, 'searchUsers'); @@ -157,7 +157,7 @@ describe('listBringForward', () => { /* eslint-disable @typescript-eslint/no-explicit-any */ listSpy.mockResolvedValue(noteList); - searchSubmissionsSpy.mockResolvedValue(submissionList as any); + searchHousingProjectsSpy.mockResolvedValue(submissionList as any); searchEnquiries.mockResolvedValue(enquiriesList as any); searchUsersSpy.mockResolvedValue(userList as any); @@ -166,8 +166,8 @@ describe('listBringForward', () => { expect(listSpy).toHaveBeenCalledTimes(1); expect(listSpy).toHaveBeenCalledWith(undefined); - expect(searchSubmissionsSpy).toHaveBeenCalledTimes(1); - expect(searchSubmissionsSpy).toHaveBeenCalledWith({ activityId: ['123'] }); + expect(searchHousingProjectsSpy).toHaveBeenCalledTimes(1); + expect(searchHousingProjectsSpy).toHaveBeenCalledWith({ activityId: ['123'] }); expect(searchUsersSpy).toHaveBeenCalledTimes(1); expect(searchUsersSpy).toHaveBeenCalledWith({ userId: ['11abbea6-2f3a-4ff3-8e55-b1e5290046f6'] }); expect(res.status).toHaveBeenCalledWith(200); @@ -198,7 +198,7 @@ describe('listBringForward', () => { expect(listSpy).toHaveBeenCalledTimes(1); expect(listSpy).toHaveBeenCalledWith(undefined); - expect(searchSubmissionsSpy).toHaveBeenCalledTimes(0); + expect(searchHousingProjectsSpy).toHaveBeenCalledTimes(0); expect(searchUsersSpy).toHaveBeenCalledTimes(0); expect(res.status).toHaveBeenCalledTimes(0); expect(next).toHaveBeenCalledTimes(1); diff --git a/app/tests/unit/controllers/reporting.spec.ts b/app/tests/unit/controllers/reporting.spec.ts index 9523529eb..87e998333 100644 --- a/app/tests/unit/controllers/reporting.spec.ts +++ b/app/tests/unit/controllers/reporting.spec.ts @@ -29,11 +29,11 @@ afterEach(() => { const CURRENT_CONTEXT = { authType: AuthType.BEARER, tokenPayload: undefined, userId: 'abc-123' }; -describe('getSubmissionPermitData', () => { +describe('getHousingProjectPermitData', () => { const next = jest.fn(); // Mock service call - const getSubmissionPermitDataSpy = jest.spyOn(reportingService, 'getSubmissionPermitData'); + const getHousingProjectPermitDataSpy = jest.spyOn(reportingService, 'getHousingProjectPermitData'); it('should return 200 if all good', async () => { const req = { @@ -78,12 +78,12 @@ describe('getSubmissionPermitData', () => { } ]; - getSubmissionPermitDataSpy.mockResolvedValue(mockData); + getHousingProjectPermitDataSpy.mockResolvedValue(mockData); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await reportingController.getSubmissionPermitData(req as any, res as any, next); + await reportingController.getHousingProjectPermitData(req as any, res as any, next); - expect(getSubmissionPermitDataSpy).toHaveBeenCalledTimes(1); + expect(getHousingProjectPermitDataSpy).toHaveBeenCalledTimes(1); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith(mockData); }); @@ -94,14 +94,14 @@ describe('getSubmissionPermitData', () => { currentContext: CURRENT_CONTEXT }; - getSubmissionPermitDataSpy.mockImplementationOnce(() => { + getHousingProjectPermitDataSpy.mockImplementationOnce(() => { throw new Error('Service error'); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await reportingController.getSubmissionPermitData(req as any, res as any, next); + await reportingController.getHousingProjectPermitData(req as any, res as any, next); - expect(getSubmissionPermitDataSpy).toHaveBeenCalledTimes(1); + expect(getHousingProjectPermitDataSpy).toHaveBeenCalledTimes(1); expect(res.status).not.toHaveBeenCalled(); expect(res.json).not.toHaveBeenCalled(); expect(next).toHaveBeenCalledTimes(1); diff --git a/app/tests/unit/controllers/submission.spec.ts b/app/tests/unit/controllers/submission.spec.ts index 914a48c4f..d54ee7b9d 100644 --- a/app/tests/unit/controllers/submission.spec.ts +++ b/app/tests/unit/controllers/submission.spec.ts @@ -1,16 +1,17 @@ -import submissionController from '../../../src/controllers/submission'; +import housingProjectController from '../../../src/controllers/housingProject'; import { activityService, contactService, - enquiryService, - permitService, draftService, - submissionService + enquiryService, + housingProjectService, + permitService } from '../../../src/services'; -import type { Permit, Submission, Draft } from '../../../src/types'; import { ApplicationStatus, IntakeStatus, PermitNeeded, PermitStatus } from '../../../src/utils/enums/housing'; import { AuthType, Initiative } from '../../../src/utils/enums/application'; +import type { Draft, HousingProject, Permit } from '../../../src/types'; + // Mock config library - @see {@link https://stackoverflow.com/a/64819698} jest.mock('config'); @@ -41,8 +42,8 @@ const uuidv4Pattern = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{ const CURRENT_CONTEXT = { authType: AuthType.BEARER, tokenPayload: undefined, userId: 'abc-123' }; -const SUBMISSION_1 = { - submissionId: '5183f223-526a-44cf-8b6a-80f90c4e802b', +const HOUSING_PROJECT_1 = { + housingProjectId: '5183f223-526a-44cf-8b6a-80f90c4e802b', activityId: '5183f223', assignedUserId: null, submittedAt: new Date().toISOString(), @@ -95,29 +96,32 @@ const SUBMISSION_1 = { user: null }; -describe('createSubmission', () => { +describe('createHousingProject', () => { // Mock service calls const createPermitSpy = jest.spyOn(permitService, 'createPermit'); - const createSubmissionSpy = jest.spyOn(submissionService, 'createSubmission'); + const createHousingProjectSpy = jest.spyOn(housingProjectService, 'createHousingProject'); const createActivitySpy = jest.spyOn(activityService, 'createActivity'); it('creates submission with unique activity ID', async () => { const req = { - body: { ...SUBMISSION_1, activityId: undefined, submissionId: undefined }, + body: { ...HOUSING_PROJECT_1, activityId: undefined, housingProjectId: undefined }, currentContext: CURRENT_CONTEXT }; const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000', submissionId: '11111111' } as Submission); + createHousingProjectSpy.mockResolvedValue({ + activityId: '00000000', + housingProjectId: '11111111' + } as HousingProject); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.createSubmission(req as any, res as any, next); + await housingProjectController.createHousingProject(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); expect(res.status).toHaveBeenCalledWith(201); - expect(res.json).toHaveBeenCalledWith({ activityId: '00000000', submissionId: '11111111' }); + expect(res.json).toHaveBeenCalledWith({ activityId: '00000000', housingProjectId: '11111111' }); }); it('populates data from body if it exists', async () => { @@ -144,20 +148,20 @@ describe('createSubmission', () => { const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createHousingProjectSpy.mockResolvedValue({ activityId: '00000000' } as HousingProject); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.createSubmission(req as any, res as any, next); + await housingProjectController.createHousingProject(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledWith( + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledWith( expect.objectContaining({ projectApplicantType: 'Individual', projectName: 'TheProject', projectLocation: 'Some place', hasAppliedProvincialPermits: true, - submissionId: expect.any(String), + housingProjectId: expect.any(String), activityId: '00000000', submittedAt: expect.stringMatching(isoPattern), intakeStatus: IntakeStatus.SUBMITTED, @@ -197,14 +201,14 @@ describe('createSubmission', () => { const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createHousingProjectSpy.mockResolvedValue({ activityId: '00000000' } as HousingProject); createPermitSpy.mockResolvedValue({} as Permit); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.createSubmission(req as any, res as any, next); + await housingProjectController.createHousingProject(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); expect(createPermitSpy).toHaveBeenCalledTimes(3); expect(createPermitSpy).toHaveBeenNthCalledWith( @@ -242,7 +246,7 @@ describe('getStatistics', () => { const next = jest.fn(); // Mock service calls - const statisticsSpy = jest.spyOn(submissionService, 'getStatistics'); + const statisticsSpy = jest.spyOn(housingProjectService, 'getStatistics'); it('should return 200 if all good', async () => { const req = { @@ -267,7 +271,7 @@ describe('getStatistics', () => { statisticsSpy.mockResolvedValue(statistics); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getStatistics(req as any, res as any, next); + await housingProjectController.getStatistics(req as any, res as any, next); expect(statisticsSpy).toHaveBeenCalledTimes(1); expect(statisticsSpy).toHaveBeenCalledWith(req.query); @@ -291,7 +295,7 @@ describe('getStatistics', () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getStatistics(req as any, res as any, next); + await housingProjectController.getStatistics(req as any, res as any, next); expect(statisticsSpy).toHaveBeenCalledTimes(1); expect(statisticsSpy).toHaveBeenCalledWith(req.query); @@ -300,57 +304,57 @@ describe('getStatistics', () => { }); }); -describe('getSubmission', () => { +describe('getHousingProject', () => { const next = jest.fn(); // Mock service calls - const submissionSpy = jest.spyOn(submissionService, 'getSubmission'); + const housingProjectSpy = jest.spyOn(housingProjectService, 'getHousingProject'); const getRelatedEnquiriesSpy = jest.spyOn(enquiryService, 'getRelatedEnquiries'); it('should return 200 if all good', async () => { const req = { - params: { submissionId: 'SOMEID' }, + params: { housingProjectId: 'SOMEID' }, currentContext: CURRENT_CONTEXT }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - submissionSpy.mockResolvedValue(SUBMISSION_1 as any); + housingProjectSpy.mockResolvedValue(HOUSING_PROJECT_1 as any); getRelatedEnquiriesSpy.mockResolvedValue([]); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getSubmission(req as any, res as any, next); + await housingProjectController.getHousingProject(req as any, res as any, next); - expect(submissionSpy).toHaveBeenCalledTimes(1); - expect(submissionSpy).toHaveBeenCalledWith(req.params.submissionId); + expect(housingProjectSpy).toHaveBeenCalledTimes(1); + expect(housingProjectSpy).toHaveBeenCalledWith(req.params.housingProjectId); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith(SUBMISSION_1); + expect(res.json).toHaveBeenCalledWith(HOUSING_PROJECT_1); }); it('calls next if the submission service fails to get submission', async () => { const req = { - params: { submissionId: 'SOMEID' }, + params: { housingProjectId: 'SOMEID' }, currentContext: CURRENT_CONTEXT }; - submissionSpy.mockImplementationOnce(() => { + housingProjectSpy.mockImplementationOnce(() => { throw new Error(); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getSubmission(req as any, res as any, next); + await housingProjectController.getHousingProject(req as any, res as any, next); - expect(submissionSpy).toHaveBeenCalledTimes(1); - expect(submissionSpy).toHaveBeenCalledWith(req.params.submissionId); + expect(housingProjectSpy).toHaveBeenCalledTimes(1); + expect(housingProjectSpy).toHaveBeenCalledWith(req.params.housingProjectId); expect(res.status).toHaveBeenCalledTimes(0); expect(next).toHaveBeenCalledTimes(1); }); }); -describe('getSubmissions', () => { +describe('getHousingProjects', () => { const next = jest.fn(); // Mock service calls - const submissionsSpy = jest.spyOn(submissionService, 'getSubmissions'); + const housingProjectsSpy = jest.spyOn(housingProjectService, 'getHousingProjects'); it('should return 200 if all good', async () => { const req = { @@ -359,15 +363,15 @@ describe('getSubmissions', () => { }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - submissionsSpy.mockResolvedValue([SUBMISSION_1 as any]); + housingProjectsSpy.mockResolvedValue([HOUSING_PROJECT_1 as any]); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getSubmissions(req as any, res as any, next); + await housingProjectController.getHousingProjects(req as any, res as any, next); - expect(submissionsSpy).toHaveBeenCalledTimes(1); - expect(submissionsSpy).toHaveBeenCalledWith(); + expect(housingProjectsSpy).toHaveBeenCalledTimes(1); + expect(housingProjectsSpy).toHaveBeenCalledWith(); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith([SUBMISSION_1]); + expect(res.json).toHaveBeenCalledWith([HOUSING_PROJECT_1]); }); it('calls checkAndStoreNewSubmissions', async () => { @@ -376,12 +380,12 @@ describe('getSubmissions', () => { }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - submissionsSpy.mockResolvedValue([SUBMISSION_1 as any]); + housingProjectsSpy.mockResolvedValue([HOUSING_PROJECT_1 as any]); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getSubmissions(req as any, res as any, next); + await housingProjectController.getHousingProjects(req as any, res as any, next); - expect(submissionsSpy).toHaveBeenCalledTimes(1); + expect(housingProjectsSpy).toHaveBeenCalledTimes(1); }); it('calls next if the submission service fails to get submissions', async () => { @@ -389,15 +393,15 @@ describe('getSubmissions', () => { currentContext: CURRENT_CONTEXT }; - submissionsSpy.mockImplementationOnce(() => { + housingProjectsSpy.mockImplementationOnce(() => { throw new Error(); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.getSubmissions(req as any, res as any, next); + await housingProjectController.getHousingProjects(req as any, res as any, next); - expect(submissionsSpy).toHaveBeenCalledTimes(1); - expect(submissionsSpy).toHaveBeenCalledWith(); + expect(housingProjectsSpy).toHaveBeenCalledTimes(1); + expect(housingProjectsSpy).toHaveBeenCalledWith(); expect(res.status).toHaveBeenCalledTimes(0); expect(next).toHaveBeenCalledTimes(1); }); @@ -406,7 +410,7 @@ describe('getSubmissions', () => { describe('submitDraft', () => { // Mock service calls const createPermitSpy = jest.spyOn(permitService, 'createPermit'); - const createSubmissionSpy = jest.spyOn(submissionService, 'createSubmission'); + const createHousingProjectSpy = jest.spyOn(housingProjectService, 'createHousingProject'); const createActivitySpy = jest.spyOn(activityService, 'createActivity'); const upsertContacts = jest.spyOn(contactService, 'upsertContacts'); @@ -432,22 +436,22 @@ describe('submitDraft', () => { const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createHousingProjectSpy.mockResolvedValue({ activityId: '00000000' } as HousingProject); upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.submitDraft(req as any, res as any, next); + await housingProjectController.submitDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); expect(upsertContacts).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledWith( + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledWith( expect.objectContaining({ projectApplicantType: 'Individual', projectName: 'TheProject', projectLocation: 'Some place', hasAppliedProvincialPermits: true, - submissionId: expect.stringMatching(uuidv4Pattern), + housingProjectId: expect.stringMatching(uuidv4Pattern), activityId: '00000000', submittedAt: expect.stringMatching(isoPattern), intakeStatus: IntakeStatus.SUBMITTED, @@ -464,16 +468,16 @@ describe('submitDraft', () => { const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createHousingProjectSpy.mockResolvedValue({ activityId: '00000000' } as HousingProject); upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.submitDraft(req as any, res as any, next); + await housingProjectController.submitDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); expect(upsertContacts).toHaveBeenCalledTimes(0); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledWith( + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledWith( expect.objectContaining({ intakeStatus: IntakeStatus.SUBMITTED }) @@ -511,16 +515,16 @@ describe('submitDraft', () => { const next = jest.fn(); createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createHousingProjectSpy.mockResolvedValue({ activityId: '00000000' } as HousingProject); createPermitSpy.mockResolvedValue({} as Permit); upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.submitDraft(req as any, res as any, next); + await housingProjectController.submitDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); expect(upsertContacts).toHaveBeenCalledTimes(0); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); + expect(createHousingProjectSpy).toHaveBeenCalledTimes(1); expect(createPermitSpy).toHaveBeenCalledTimes(3); expect(createPermitSpy).toHaveBeenNthCalledWith( 1, @@ -585,7 +589,7 @@ describe('updateDraft', () => { createDraftSpy.mockResolvedValue({ draftId: '11111111', activityId: '00000000' } as Draft); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateDraft(req as any, res as any, next); + await housingProjectController.updateDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(1); expect(createDraftSpy).toHaveBeenCalledTimes(1); @@ -627,7 +631,7 @@ describe('updateDraft', () => { updateDraftSpy.mockResolvedValue({ draftId: '11111111', activityId: '00000000' } as Draft); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateDraft(req as any, res as any, next); + await housingProjectController.updateDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(0); expect(updateDraftSpy).toHaveBeenCalledTimes(1); @@ -641,25 +645,25 @@ describe('updateDraft', () => { }); }); -describe('updateSubmission', () => { +describe('updateHousingProject', () => { const next = jest.fn(); // Mock service calls - const updateSpy = jest.spyOn(submissionService, 'updateSubmission'); + const updateSpy = jest.spyOn(housingProjectService, 'updateHousingProject'); it('should return 200 if all good', async () => { const req = { - body: SUBMISSION_1, + body: HOUSING_PROJECT_1, currentContext: CURRENT_CONTEXT }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - const updated: any = SUBMISSION_1; + const updated: any = HOUSING_PROJECT_1; updateSpy.mockResolvedValue(updated); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateSubmission(req as any, res as any, next); + await housingProjectController.updateHousingProject(req as any, res as any, next); expect(updateSpy).toHaveBeenCalledTimes(1); expect(updateSpy).toHaveBeenCalledWith({ @@ -671,9 +675,9 @@ describe('updateSubmission', () => { expect(res.json).toHaveBeenCalledWith(updated); }); - it('calls next if the submission service fails to update', async () => { + it('calls next if the housingProject service fails to update', async () => { const req = { - body: SUBMISSION_1, + body: HOUSING_PROJECT_1, currentContext: CURRENT_CONTEXT }; @@ -682,7 +686,7 @@ describe('updateSubmission', () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateSubmission(req as any, res as any, next); + await housingProjectController.updateHousingProject(req as any, res as any, next); expect(updateSpy).toHaveBeenCalledTimes(1); @@ -697,73 +701,73 @@ describe('updateSubmission', () => { }); describe('assignPriority', () => { - it('assigns priority 1 when submission matches priority 1 criteria - 50 to 500 units', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 criteria - 50 to 500 units', () => { + const housingProject: Partial = { singleFamilyUnits: '50-500', hasRentalUnits: 'No', financiallySupportedBC: 'No', financiallySupportedIndigenous: 'No' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 1 when submission matches priority 1 criteria - over 500 units', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 criteria - over 500 units', () => { + const housingProject: Partial = { singleFamilyUnits: '>500', hasRentalUnits: 'No', financiallySupportedBC: 'No', financiallySupportedIndigenous: 'No' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 1 when submission matches priority 1 criteria - Has Rental Units', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 criteria - Has Rental Units', () => { + const housingProject: Partial = { singleFamilyUnits: '1-9', hasRentalUnits: 'Yes', financiallySupportedBC: 'No', financiallySupportedIndigenous: 'No' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 1 when submission matches priority 1 criteria - Social Housing', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 criteria - Social Housing', () => { + const housingProject: Partial = { singleFamilyUnits: '1-9', hasRentalUnits: 'No', financiallySupportedBC: 'Yes', financiallySupportedIndigenous: 'No' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 1 when submission matches priority 1 criteria - Indigenous Led', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 criteria - Indigenous Led', () => { + const housingProject: Partial = { singleFamilyUnits: '1-9', hasRentalUnits: 'No', financiallySupportedBC: 'No', financiallySupportedIndigenous: 'Yes' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 1 when submission matches priority 1 and priority 2 criteria', () => { - const submission: Partial = { + it('assigns priority 1 when housing project matches priority 1 and priority 2 criteria', () => { + const housingProject: Partial = { singleFamilyUnits: '10-49', hasRentalUnits: 'Yes', financiallySupportedBC: 'No', @@ -772,43 +776,43 @@ describe('assignPriority', () => { otherUnits: '' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(1); + expect(housingProject.queuePriority).toBe(1); }); - it('assigns priority 2 when submission matches priority 2 criteria - 10-49 single family units', () => { - const submission: Partial = { + it('assigns priority 2 when housing project matches priority 2 criteria - 10-49 single family units', () => { + const housingProject: Partial = { singleFamilyUnits: '10-49' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(2); + expect(housingProject.queuePriority).toBe(2); }); it('assigns priority 2 if only multiFamilyUnits is provided', () => { - const submission: Partial = { + const housingProject: Partial = { multiFamilyUnits: '1-9' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(2); + expect(housingProject.queuePriority).toBe(2); }); it('assigns priority 2 if only otherUnits is provided', () => { - const submission: Partial = { + const housingProject: Partial = { otherUnits: '1-9' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(2); + expect(housingProject.queuePriority).toBe(2); }); - it('assigns priority 3 when submission matches neither priority 1 nor priority 2 criteria', () => { - const submission: Partial = { + it('assigns priority 3 when housing project matches neither priority 1 nor priority 2 criteria', () => { + const housingProject: Partial = { singleFamilyUnits: '1-9', hasRentalUnits: 'No', financiallySupportedBC: 'No', @@ -817,16 +821,16 @@ describe('assignPriority', () => { otherUnits: '' }; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(3); + expect(housingProject.queuePriority).toBe(3); }); it('assigns priority 3 if no criteria are met/given', () => { - const submission: Partial = {}; + const housingProject: Partial = {}; - submissionController.assignPriority(submission); + housingProjectController.assignPriority(housingProject); - expect(submission.queuePriority).toBe(3); + expect(housingProject.queuePriority).toBe(3); }); }); diff --git a/frontend/src/components/contact/ContactHistoryList.vue b/frontend/src/components/contact/ContactHistoryList.vue index 7a2a2b61f..587ab0040 100644 --- a/frontend/src/components/contact/ContactHistoryList.vue +++ b/frontend/src/components/contact/ContactHistoryList.vue @@ -10,12 +10,12 @@ import { formatDate } from '@/utils/formatters'; import { toNumber } from '@/utils/utils'; import type { Ref } from 'vue'; -import type { Enquiry, Pagination, Submission } from '@/types'; +import type { Enquiry, Pagination, HousingProject } from '@/types'; // Props const { assignedUsers, contactsHistory, loading } = defineProps<{ assignedUsers: Record; - contactsHistory: Array; + contactsHistory: Array; loading: boolean; }>(); @@ -32,16 +32,16 @@ const pagination: Ref = ref({ page: 0 }); const rowsPerPageOptions: Ref> = ref([10, 20, 50]); -const selection: Ref = ref(undefined); +const selection: Ref = ref(undefined); // Actions function getUsersName(userId: string) { return assignedUsers[userId]; } -function getRouteToObject(data: Enquiry | Submission) { +function getRouteToObject(data: Enquiry | HousingProject) { let toObject = {}; - if ('submissionId' in data) { + if ('housingProjectId' in data) { toObject = { name: RouteName.INT_HOUSING_PROJECT, params: { submissionId: data.submissionId } @@ -58,9 +58,9 @@ function getRouteToObject(data: Enquiry | Submission) { function normalizeContactHistory() { return contactsHistory.map((se) => ({ ...se, - state: 'submissionId' in se ? se.applicationStatus : se.enquiryStatus, + state: 'housingProjectId' in se ? se.applicationStatus : se.enquiryStatus, assignedUser: se.assignedUserId ? getUsersName(se.assignedUserId) : 'Unassigned', - historyType: 'submissionId' in se ? 'Submission' : 'Enquiry' + historyType: 'housingProjectId' in se ? 'Submission' : 'Enquiry' })); } diff --git a/frontend/src/components/file/AdvancedFileUpload.vue b/frontend/src/components/file/AdvancedFileUpload.vue index 5b46f640a..3c57195f2 100644 --- a/frontend/src/components/file/AdvancedFileUpload.vue +++ b/frontend/src/components/file/AdvancedFileUpload.vue @@ -5,7 +5,7 @@ import { computed, ref } from 'vue'; import { Button, FileUpload, ProgressBar, useToast } from '@/lib/primevue'; import DocumentCardLite from '@/components/file/DocumentCardLite.vue'; import { documentService } from '@/services'; -import { useConfigStore, useSubmissionStore } from '@/store'; +import { useConfigStore, useHousingProjectStore } from '@/store'; import type { FileUploadUploaderEvent } from 'primevue/fileupload'; import type { Ref } from 'vue'; @@ -27,7 +27,7 @@ const { // Store const { getConfig } = storeToRefs(useConfigStore()); -const submissionStore = useSubmissionStore(); +const housingProjectStore = useHousingProjectStore(); // State const fileInput: Ref = ref(null); @@ -70,7 +70,7 @@ const onUpload = async (files: Array) => { .then((response) => { if (response?.data) { response.data.filename = decodeURI(response.data.filename); - submissionStore.addDocument(response.data); + housingProjectStore.addDocument(response.data); toast.success('Document uploaded'); } return resolve(response); @@ -92,7 +92,7 @@ const onUpload = async (files: Array) => { // If accept is provided, only documents with extensions in accept are shown // If reject is provided, only documents with extensions not in reject are shown const filteredDocuments = computed(() => { - let documents = submissionStore.getDocuments; + let documents = housingProjectStore.getDocuments; return documents.filter( (document) => (!accept && !reject) || diff --git a/frontend/src/components/file/DeleteDocument.vue b/frontend/src/components/file/DeleteDocument.vue index becc8ff19..651937011 100644 --- a/frontend/src/components/file/DeleteDocument.vue +++ b/frontend/src/components/file/DeleteDocument.vue @@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n'; import { Button, useConfirm, useToast } from '@/lib/primevue'; import { documentService } from '@/services'; -import { useSubmissionStore } from '@/store'; +import { useHousingProjectStore } from '@/store'; import type { Document } from '@/types'; @@ -14,7 +14,7 @@ const { document, disabled = false } = defineProps<{ }>(); // Store -const submissionStore = useSubmissionStore(); +const housingProjectStore = useHousingProjectStore(); // Actions const { t } = useI18n(); @@ -34,7 +34,7 @@ const confirmDelete = (document: Document) => { documentService .deleteDocument(document.documentId) .then(() => { - submissionStore.removeDocument(document); + housingProjectStore.removeDocument(document); toast.success('Document deleted'); }) .catch((e: any) => toast.error('Failed to deleted document', e.message)); diff --git a/frontend/src/components/file/FileUpload.vue b/frontend/src/components/file/FileUpload.vue index 77401a4ec..7c1426312 100644 --- a/frontend/src/components/file/FileUpload.vue +++ b/frontend/src/components/file/FileUpload.vue @@ -4,7 +4,7 @@ import { ref } from 'vue'; import { Button, FileUpload, ProgressBar, useToast } from '@/lib/primevue'; import { documentService } from '@/services'; -import { useConfigStore, useSubmissionStore } from '@/store'; +import { useConfigStore, useHousingProjectStore } from '@/store'; import { getFilenameAndExtension } from '@/utils/utils'; import type { FileUploadUploaderEvent } from 'primevue/fileupload'; @@ -18,7 +18,7 @@ const { activityId, disabled = false } = defineProps<{ // Store const { getConfig } = storeToRefs(useConfigStore()); -const submissionStore = useSubmissionStore(); +const housingProjectStore = useHousingProjectStore(); // State const fileInput: Ref = ref(null); @@ -58,7 +58,7 @@ const onUpload = async (files: Array) => { if (response) { response.extension = getFilenameAndExtension(response.filename).extension; response.filename = decodeURI(response.filename); - submissionStore.addDocument(response); + housingProjectStore.addDocument(response); toast.success('Document uploaded'); } } catch (e: any) { diff --git a/frontend/src/components/housing/enquiry/EnquiryForm.vue b/frontend/src/components/housing/enquiry/EnquiryForm.vue index 3cb2f0fa8..6d22bca5a 100644 --- a/frontend/src/components/housing/enquiry/EnquiryForm.vue +++ b/frontend/src/components/housing/enquiry/EnquiryForm.vue @@ -20,7 +20,7 @@ import ATSUserLinkModal from '@/components/user/ATSUserLinkModal.vue'; import ATSUserCreateModal from '@/components/user/ATSUserCreateModal.vue'; import ATSUserDetailsModal from '@/components/user/ATSUserDetailsModal.vue'; import { Button, Message, useConfirm, useToast } from '@/lib/primevue'; -import { enquiryService, submissionService, userService } from '@/services'; +import { enquiryService, housingProjectService, userService } from '@/services'; import { useEnquiryStore } from '@/store'; import { IdentityProviderKind, Regex } from '@/utils/enums/application'; import { ApplicationStatus, EnquirySubmittedMethod, IntakeStatus } from '@/utils/enums/housing'; @@ -278,7 +278,7 @@ onBeforeMount(async () => { atsClientId: enquiry?.atsClientId, user: assigneeOptions.value[0] ?? null }; - projectActivityIds.value = filteredProjectActivityIds.value = (await submissionService.getActivityIds()).data; + projectActivityIds.value = filteredProjectActivityIds.value = (await housingProjectService.getActivityIds()).data; }); @@ -490,7 +490,7 @@ onBeforeMount(async () => { { { +
+

{{ t('homeView.welcome') }}

+

{{ t('homeView.pcns') }}

+

{{ t('homeView.chooseProject') }}

+
+ + +
@@ -55,7 +77,7 @@ const toHousing = (): void => { diff --git a/frontend/src/components/electrification/project/ProjectAssistance.vue b/frontend/src/components/electrification/project/ProjectAssistance.vue new file mode 100644 index 000000000..5304e276f --- /dev/null +++ b/frontend/src/components/electrification/project/ProjectAssistance.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/frontend/src/components/electrification/project/ProjectIntakeForm.vue b/frontend/src/components/electrification/project/ProjectIntakeForm.vue new file mode 100644 index 000000000..8fcadce26 --- /dev/null +++ b/frontend/src/components/electrification/project/ProjectIntakeForm.vue @@ -0,0 +1,586 @@ + + +