Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: enable financial Risk assessments to notifiy assignees #3544

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## [1.190.11](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.190.10...v1.190.11) (2024-09-12)

## [1.190.10](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.190.9...v1.190.10) (2024-09-10)

## [1.190.9](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.190.8...v1.190.9) (2024-09-06)
Expand Down
15 changes: 2 additions & 13 deletions app/backend/lib/emails/templates/assesmentSecondReviewChange.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import ASSESSMENT_TYPES from '../../../../data/assessmentTypes';
import {
EmailTemplate,
EmailTemplateProvider,
} from '../handleEmailNotification';

const formats = {
projectManagement: {
type: 'Project Management assessment',
slug: 'project-management',
},
permitting: { type: 'Permitting assessment', slug: 'permitting' },
technical: { type: 'Technical assessment', slug: 'technical' },
gis: { type: 'GIS assessment', slug: 'gis' },
financialRisk: { type: 'Financial Risk assessment', slug: 'financial-risk' },
screening: { type: 'Eligibility Screening', slug: 'screening' },
};

const assesmentSecondReviewChange: EmailTemplateProvider = (
applicationId: string,
url: string,
initiator: any,
params: any
): EmailTemplate => {
const { ccbcNumber, assessmentType } = params;
const { type, slug } = formats[assessmentType];
const { type, slug } = ASSESSMENT_TYPES[assessmentType];

return {
emailTo: [34, 71], // Temporary IDs to handle email recipients
Expand Down
8 changes: 4 additions & 4 deletions app/backend/lib/emails/templates/assessmentAssigneeChange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Context } from 'backend/lib/ches/sendEmailMerge';

import ASSESSMENT_TYPES from '../../../../data/assessmentTypes';
import {
EmailTemplate,
EmailTemplateProvider,
Expand Down Expand Up @@ -77,8 +77,8 @@ const assessmentAssigneeChange: EmailTemplateProvider = async (
([assignor, assignments]) => {
const alerts = (assignments as Array<any>).map((assignment) => {
return {
url: `${url}/analyst/application/${assignment.applicationId}/assessments/${assignment.assessmentType}`,
type: assignment.assessmentType,
url: `${url}/analyst/application/${assignment.applicationId}/assessments/${ASSESSMENT_TYPES[assignment.assessmentType].slug}`,
type: ASSESSMENT_TYPES[assignment.assessmentType].type,
ccbcNumber: assignment.ccbcNumber,
applicationId: assignment.applicationId,
};
Expand Down Expand Up @@ -121,7 +121,7 @@ const assessmentAssigneeChange: EmailTemplateProvider = async (
body: `{% for action in actions %}
{{ action.assignors }} has assigned you the following assessment(s):
<ul>{% for alert in action.alerts %}
<li><a href='{{ alert.url }}'>{{ alert.type | capitalize }}</a> for {{ alert.ccbcNumber }}</li>
<li><a href='{{ alert.url }}'>{{ alert.type }}</a> for {{ alert.ccbcNumber }}</li>
{% endfor %}</ul>
{% endfor %}`,
contexts,
Expand Down
123 changes: 77 additions & 46 deletions app/components/AnalystDashboard/AssessmentAssignmentTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { graphql, useFragment } from 'react-relay';
import styled from 'styled-components';
import cookie from 'js-cookie';
Expand Down Expand Up @@ -148,6 +148,18 @@ const findAssessment = (assessments, assessmentType) => {
};
};

const findNotification = (notifications, notificationType) => {
const data = notifications.find(
({ node }) => node?.notificationType === notificationType
);

return {
jsonData: data?.node?.jsonData,
notificationType,
createdAt: data?.node?.createdAt,
};
};

const StyledLink = styled.a`
color: ${(props) => props.theme.color.links};
text-decoration: none;
Expand Down Expand Up @@ -235,11 +247,7 @@ const AssessmentAssignmentTable: React.FC<Props> = ({ query }) => {
}
}
}
notificationsByApplicationId(
orderBy: CREATED_AT_DESC
first: 1
condition: { notificationType: "assignment_technical" }
) {
assessmentNotifications {
__id
edges {
node {
Expand Down Expand Up @@ -429,9 +437,8 @@ const AssessmentAssignmentTable: React.FC<Props> = ({ query }) => {
zones,
allAnalysts,
assessmentConnection: application.allAssessments.__id,
notifications: application.notificationsByApplicationId.edges,
notificationConnectionId:
application.notificationsByApplicationId.__id,
application.assessmentNotifications?.__id,
pmAssessment: findAssessment(
application.allAssessments.edges,
'projectManagement'
Expand All @@ -440,6 +447,10 @@ const AssessmentAssignmentTable: React.FC<Props> = ({ query }) => {
application.allAssessments.edges,
'technical'
),
techNotification: findNotification(
application.assessmentNotifications.edges,
'assignment_technical'
),
permittingAssessment: findAssessment(
application.allAssessments.edges,
'permitting'
Expand All @@ -456,6 +467,10 @@ const AssessmentAssignmentTable: React.FC<Props> = ({ query }) => {
application.allAssessments.edges,
'financialRisk'
),
financialRiskNotification: findNotification(
application.assessmentNotifications.edges,
'assignment_financialRisk'
),
organizationName,
};
}
Expand All @@ -465,46 +480,62 @@ const AssessmentAssignmentTable: React.FC<Props> = ({ query }) => {
[allApplications, allAnalysts]
);

const getUserEmailByAssignedTo = (assignedTo: string) => {
const analyst = allAnalysts.edges.find(
({ node }) => `${node.givenName} ${node.familyName}` === assignedTo
);
return analyst ? analyst.node.email : null;
};

const assignments = useMemo(
() =>
tableData
.filter((data: any) => {
const lastSentAt = data.notifications[0]?.node?.createdAt
? new Date(data.notifications[0]?.node?.createdAt)
: null;
return new Date(data.techAssessment.updatedAt) >= lastSentAt;
})
.filter(
(data: any) =>
data.techAssessment.jsonData.assignedTo &&
data.techAssessment.jsonData.assignedTo !==
data.notifications[0]?.node?.jsonData?.to
)
.map((data: any) => {
return {
ccbcNumber: data.ccbcNumber,
applicationId: data.applicationId,
notificationConnectionId: data.notificationConnectionId,
updatedBy: data.techAssessment.updatedBy,
updatedAt: data.techAssessment.updatedAt,
assignedTo: data.techAssessment.jsonData?.assignedTo,
assigneeEmail: getUserEmailByAssignedTo(
data.techAssessment.jsonData?.assignedTo
),
assessmentType: 'technical',
};
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[tableData]
const getUserEmailByAssignedTo = useCallback(
(assignedTo: string) => {
const analyst = allAnalysts.edges.find(
({ node }) => `${node.givenName} ${node.familyName}` === assignedTo
);
return analyst ? analyst.node.email : null;
},
[allAnalysts]
);

const assignments = useMemo(() => {
const createAssignment = (
application,
assessmentKey,
assessmentType = assessmentKey
) => {
const { updatedAt, jsonData, updatedBy } =
application[`${assessmentKey}Assessment`];
const notification = application[`${assessmentKey}Notification`];
const lastNotificationSentAt = notification?.createdAt
? new Date(notification.createdAt)
: null;

const assessmentChanged =
jsonData?.assignedTo &&
jsonData.assignedTo !== notification?.jsonData?.to;

if (new Date(updatedAt) >= lastNotificationSentAt && assessmentChanged) {
return {
ccbcNumber: application.ccbcNumber,
applicationId: application.applicationId,
notificationConnectionId: application.notificationConnectionId,
updatedBy,
updatedAt,
assignedTo: jsonData.assignedTo,
assigneeEmail: getUserEmailByAssignedTo(jsonData.assignedTo),
assessmentType,
};
}
return null;
};

return tableData.reduce((assignmentsList, application) => {
const techAssignment = createAssignment(application, 'tech', 'technical');
const financialAssignment = createAssignment(
application,
'financialRisk'
);

if (techAssignment) assignmentsList.push(techAssignment);
if (financialAssignment) assignmentsList.push(financialAssignment);

return assignmentsList;
}, []);
}, [getUserEmailByAssignedTo, tableData]);

const columns = useMemo<MRT_ColumnDef<Application>[]>(() => {
// Sonarcloud duplicate lines
const sharedAssessmentCell = {
Expand Down
13 changes: 13 additions & 0 deletions app/data/assessmentTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const ASSESSMENT_TYPES = {
projectManagement: {
type: 'Project Management assessment',
slug: 'project-management',
},
permitting: { type: 'Permitting assessment', slug: 'permitting' },
technical: { type: 'Technical assessment', slug: 'technical' },
gis: { type: 'GIS assessment', slug: 'gis' },
financialRisk: { type: 'Financial Risk assessment', slug: 'financial-risk' },
screening: { type: 'Eligibility Screening', slug: 'screening' },
};

export default ASSESSMENT_TYPES;
26 changes: 26 additions & 0 deletions app/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -29924,6 +29924,32 @@ type Application implements Node {
"""Computed column that takes the slug to return an assessment form"""
assessmentForm(_assessmentDataType: String!): AssessmentData

"""Computed column to get assessment notifications by assessment type"""
assessmentNotifications(
"""Only read the first `n` values of the set."""
first: Int

"""Only read the last `n` values of the set."""
last: Int

"""
Skip the first `n` values from our `after` cursor, an alternative to cursor
based pagination. May not be used with `last`.
"""
offset: Int

"""Read all values in the set before (above) this cursor."""
before: Cursor

"""Read all values in the set after (below) this cursor."""
after: Cursor

"""
A filter to be used in determining which values should be returned by the collection.
"""
filter: NotificationFilter
): NotificationsConnection!

"""Computed column to return conditional approval data"""
conditionalApproval: ConditionalApprovalData

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('assessmentAssigneeChange template', () => {
},
{
applicationId: 3,
assessmentType: 'technical',
assessmentType: 'financialRisk',
assignedTo: 'Tester 2',
assigneeEmail: '[email protected]',
ccbcNumber: 'CCBC-000003',
Expand Down Expand Up @@ -93,7 +93,7 @@ describe('assessmentAssigneeChange template', () => {
{
ccbcNumber: 'CCBC-000001',
applicationId: 1,
type: 'technical',
type: 'Technical assessment',
url: 'http://mock_host.ca/analyst/application/1/assessments/technical',
},
],
Expand All @@ -106,7 +106,7 @@ describe('assessmentAssigneeChange template', () => {
{
ccbcNumber: 'CCBC-000002',
applicationId: 2,
type: 'technical',
type: 'Technical assessment',
url: 'http://mock_host.ca/analyst/application/2/assessments/technical',
},
],
Expand All @@ -117,8 +117,8 @@ describe('assessmentAssigneeChange template', () => {
{
ccbcNumber: 'CCBC-000003',
applicationId: 3,
type: 'technical',
url: 'http://mock_host.ca/analyst/application/3/assessments/technical',
type: 'Financial Risk assessment',
url: 'http://mock_host.ca/analyst/application/3/assessments/financial-risk',
},
],
assignors: 'Assignor 3',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- Deploy ccbc:computed_columns/application_assessment_notifications to pg

BEGIN;

create or replace function ccbc_public.application_assessment_notifications(application ccbc_public.application) returns setof ccbc_public.notification as
$$
select distinct on (notification_type) *
from ccbc_public.notification
where application_id = application_id
ORDER BY notification_type, created_at DESC;
$$ language sql stable;

grant execute on function ccbc_public.application_assessment_notifications to ccbc_analyst;
grant execute on function ccbc_public.application_assessment_notifications to ccbc_admin;
grant execute on function ccbc_public.application_assessment_notifications to ccbc_auth_user;

comment on function ccbc_public.application_assessment_notifications is 'Computed column to get assessment notifications by assessment type';

COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

BEGIN;

drop function ccbc_public.application_assessment_notifications;

COMMIT;
2 changes: 2 additions & 0 deletions db/sqitch.plan
Original file line number Diff line number Diff line change
Expand Up @@ -668,3 +668,5 @@ tables/communities_source_data_001_service_account 2024-08-28T16:32:48Z Rafael S
@1.190.8 2024-09-04T23:08:50Z CCBC Service Account <[email protected]> # release v1.190.8
@1.190.9 2024-09-06T20:13:21Z CCBC Service Account <[email protected]> # release v1.190.9
@1.190.10 2024-09-10T23:10:53Z CCBC Service Account <[email protected]> # release v1.190.10
computed_columns/application_assessment_notifications 2024-09-06T21:58:13Z ,,, <ryohani89@NH504670> # Add notifications by assessment type field
@1.190.11 2024-09-12T18:13:42Z CCBC Service Account <[email protected]> # release v1.190.11
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "CONN-CCBC-portal",
"version": "1.190.10",
"version": "1.190.11",
"main": "index.js",
"repository": "https://github.com/bcgov/CONN-CCBC-portal.git",
"author": "Romer, Meherzad CITZ:EX <[email protected]>",
Expand Down
Loading