Skip to content

Commit d8592b8

Browse files
committed
Merge branch 'master' into CHI-3545-incoming_transfer_breaks_chats
2 parents d4c506b + 46dc421 commit d8592b8

File tree

54 files changed

+2002
-728
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2002
-728
lines changed

lambdas/TwilioErrorReporter/index.ts

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,57 +19,61 @@ import { URLSearchParams } from 'url';
1919
import type { ALBEvent, ALBResult } from 'aws-lambda';
2020

2121
const productionAccountMap = {
22-
ZA_PRODUCTION: 'AC988e78b713be4a04246b39835de37ad4',
23-
ZM_PRODUCTION: 'ACf0b04d307d8f20074dc09cdb3b4f0a83',
24-
MW_PRODUCTION: 'AC926d9077405d7f5b4c7129d3087d7814',
25-
ET_PRODUCTION: 'AC16aa1fb9123860efd3bdde74de556e21',
22+
AS_PRODUCTION: 'AC4858840776b1f98a1367c9c6a401bd2c',
2623
BR_PRODUCTION: 'AC98b66f5541f81de7050c7254cf5c96c9',
24+
CA_PRODUCTION: 'AC9f951863c83dc61cf94bdc12a12270a5',
25+
CL_PRODUCTION: 'AC13e88b6577da29a0b35c0ea7939c3c22',
26+
CO_PRODUCTION: 'AC520ec62dcfa4ab4105c2f5850caf52b0',
27+
ET_PRODUCTION: 'AC16aa1fb9123860efd3bdde74de556e21',
28+
HU_PRODUCTION: 'AC378f491732fc29691b4126a2287cea9a',
2729
IN_PRODUCTION: 'AC64efffad72dccd812ca0c156355217ed',
2830
JM_PRODUCTION: 'AC9fd261078d40fcfa06f0e374921af7a5',
31+
MT_PRODUCTION: 'AC7854f6126459347434a8e659295ebb79',
32+
MW_PRODUCTION: 'AC926d9077405d7f5b4c7129d3087d7814',
33+
NZ_PRODUCTION: 'AC2825ea172ea83c2e422a9772a27beb29',
2934
PH_PRODUCTION: 'ACd9d59ab87f24d9086b25a7e3c67b2231',
30-
CO_PRODUCTION: 'AC520ec62dcfa4ab4105c2f5850caf52b0',
31-
HU_PRODUCTION: 'AC378f491732fc29691b4126a2287cea9a',
35+
SG_PRODUCTION: 'AC0751f021d17d50f0ee5af094acdee7c8',
3236
TH_PRODUCTION: 'AC77e853192a62159196decc2468243518',
33-
CA_PRODUCTION: 'AC9f951863c83dc61cf94bdc12a12270a5',
34-
ZW_PRODUCTION: 'ACd0db81cffec7eb658f6ee176a06dc902',
35-
CL_PRODUCTION: 'AC13e88b6577da29a0b35c0ea7939c3c22',
36-
AS_PRODUCTION: 'AC4858840776b1f98a1367c9c6a401bd2c',
37-
NZ_PRODUCTION: 'AC2825ea172ea83c2e422a9772a27beb29',
38-
USCR_PRODUCTION: 'AC34800d0cf642c95ce1f17785d239333c',
3937
TZ_PRODUCTION: 'AC24b2e69a74f051b368f4ec17f51ddbbc',
4038
UKMH_PRODUCTION: 'AC10edaafc5e4a4b6017d758b2f011142b',
41-
MT_PRODUCTION: 'AC7854f6126459347434a8e659295ebb79',
42-
SG_PRODUCTION: 'AC0751f021d17d50f0ee5af094acdee7c8',
39+
USCH_PRODUCTION: 'AC9eeaba1a6d959e0cc1befda3884a2bc1',
40+
USCR_PRODUCTION: 'AC34800d0cf642c95ce1f17785d239333c',
41+
ZA_PRODUCTION: 'AC988e78b713be4a04246b39835de37ad4',
42+
ZM_PRODUCTION: 'ACf0b04d307d8f20074dc09cdb3b4f0a83',
43+
ZW_PRODUCTION: 'ACd0db81cffec7eb658f6ee176a06dc902',
44+
// Alphabetize! Don't just add new helplines at the bottom!
4345
};
4446

4547
const productionAccounts = Object.values(productionAccountMap);
4648

4749
const stagingAccountMap = {
4850
ASELO_BETA: 'AC6b99858a6faf7af1b572c83988b50eb1',
49-
ZA_STAGING: 'AC16dd71c6fd135ee250bd213ad1efa2e8',
50-
ZM_STAGING: 'ACc59300c7ca018e8652e4d6d86c2d50e6',
51-
MW_STAGING: 'AC874af45ec4a696d5d4dca07b0036e2bf',
52-
ET_STAGING: 'ACfd932bd76669f9cc2145e67e6c3e03ea',
51+
AS_STAGING: 'AC1ca120bc71593bbe9ca78e2232a31e0d',
5352
BR_STAGING: 'AC4a92fba4d8040b1d6dbe2bbceee87cbf',
53+
CA_STAGING: 'ACeb335f4685aa874fddf00cdd7c2946bd',
54+
CL_STAGING: 'AC6ca34b61e7bf2d7cf8b8ca24e7efe65f',
55+
CO_STAGING: 'AC76b8bd2798b01b067a1be7f17d36c894',
56+
ET_STAGING: 'ACfd932bd76669f9cc2145e67e6c3e03ea',
57+
EUMC_STAGING: 'AC6fbdd2a933a559283445c5e527c3cfe3',
58+
HU_STAGING: 'ACbdbee34ef7d099e71cf095d540ff3270',
5459
IN_STAGING: 'AC3ee345e8fe8213010ff81fbe54b1414e',
5560
JM_STAGING: 'ACbc27263c18e621f3deb57cf1998a4e04',
56-
CA_STAGING: 'ACeb335f4685aa874fddf00cdd7c2946bd',
61+
MT_STAGING: 'ACfb0ccf10880289d67f5c4e85ae26402b',
62+
MW_STAGING: 'AC874af45ec4a696d5d4dca07b0036e2bf',
63+
NZ_STAGING: 'AC3ee873a0431086e5b1166db5f5e29860',
5764
PH_STAGING: 'ACa10989d583df770649051aee1430fce9',
65+
PL_STAGING: 'ACb3da2ab24338c616db45ba3b4afce61a',
5866
RO_STAGING: 'ACbeffd85714fecd060d38aa4d84c3fc03',
59-
HU_STAGING: 'ACbdbee34ef7d099e71cf095d540ff3270',
60-
CO_STAGING: 'AC76b8bd2798b01b067a1be7f17d36c894',
67+
SG_STAGING: 'ACa00e3e32f7ba405cc0f5906906d88e97',
6168
TH_STAGING: 'AC58ec02594f3ca5225db71913b1cda612',
62-
CL_STAGING: 'AC6ca34b61e7bf2d7cf8b8ca24e7efe65f',
63-
ZW_STAGING: 'AC48d146ce2460184b8944cc7fdf8c5d25',
64-
PL_STAGING: 'ACb3da2ab24338c616db45ba3b4afce61a',
65-
MT_STAGING: 'ACfb0ccf10880289d67f5c4e85ae26402b',
66-
USCR_STAGING: 'AC3edc359b6a45de1a2f6078c7091c8fef',
67-
NZ_STAGING: 'AC3ee873a0431086e5b1166db5f5e29860',
68-
USCH_STAGING: 'AC147e360e21386797593c3893bf4def12',
6969
TZ_STAGING: 'AC94cb43c61dbc082094fb34cb147896eb',
7070
UKMH_STAGING: 'AC9eb11b6c714d785db648b6ea6a85c13f',
71-
SG_STAGING: 'ACa00e3e32f7ba405cc0f5906906d88e97',
72-
EUMC_STAGING: 'AC6fbdd2a933a559283445c5e527c3cfe3',
71+
USCH_STAGING: 'AC147e360e21386797593c3893bf4def12',
72+
USCR_STAGING: 'AC3edc359b6a45de1a2f6078c7091c8fef',
73+
ZA_STAGING: 'AC16dd71c6fd135ee250bd213ad1efa2e8',
74+
ZM_STAGING: 'ACc59300c7ca018e8652e4d6d86c2d50e6',
75+
ZW_STAGING: 'AC48d146ce2460184b8944cc7fdf8c5d25',
76+
// Alphabetize! Don't just add new helplines at the bottom!
7377
};
7478

7579
const stagingAccounts = Object.values(stagingAccountMap);

lambdas/account-scoped/src/router.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { AccountScopedRoute, FunctionRoute, HttpRequest } from './httpTypes';
1919
import { validateRequestMethod } from './validation/method';
2020
import { isAccountSID } from './twilioTypes';
2121
import { handleTaskRouterEvent } from './taskrouter';
22+
import { updateWorkersSkills } from './taskrouter/updateWorkersSkills';
2223
import { handleGetProfileFlagsForIdentifier } from './hrm/getProfileFlagsForIdentifier';
2324
import { handleToggleSwitchboardQueue } from './hrm/toggleSwitchboardQueue';
2425
import {
@@ -106,6 +107,10 @@ const ROUTES: Record<string, FunctionRoute> = {
106107
requestPipeline: [],
107108
handler: handleOperatingHours,
108109
},
110+
updateWorkersSkills: {
111+
requestPipeline: [validateFlexTokenRequest({ tokenMode: 'supervisor' })],
112+
handler: updateWorkersSkills,
113+
},
109114
};
110115

111116
export const lookupRoute = (event: HttpRequest): AccountScopedRoute | undefined => {
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* Copyright (C) 2021-2023 Technology Matters
3+
* This program is free software: you can redistribute it and/or modify
4+
* it under the terms of the GNU Affero General Public License as published
5+
* by the Free Software Foundation, either version 3 of the License, or
6+
* (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU Affero General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU Affero General Public License
14+
* along with this program. If not, see https://www.gnu.org/licenses/.
15+
*/
16+
17+
import { AccountScopedHandler } from '../httpTypes';
18+
import { newErr, newOk } from '../Result';
19+
import { newMissingParameterResult } from '../httpErrors';
20+
import {
21+
getTwilioClient,
22+
getTwilioWorkspaceSid,
23+
} from '../configuration/twilioConfiguration';
24+
25+
const validOperations = ['enable', 'disable'] as const;
26+
type ValidOperations = (typeof validOperations)[number];
27+
28+
const moveElementsBetweenArrays = ({
29+
from,
30+
to,
31+
elements,
32+
}: {
33+
from: Array<string>;
34+
to: Array<string>;
35+
elements: Array<string>;
36+
}) => {
37+
const updatedFrom = from.filter(e => !elements.includes(e));
38+
const updatedTo = Array.from(new Set([...to, ...elements]));
39+
40+
return { updatedFrom, updatedTo };
41+
};
42+
43+
const updateSkillsOperation = ({
44+
attributes,
45+
operation,
46+
skills,
47+
}: {
48+
attributes: any;
49+
skills: Array<string>;
50+
operation: ValidOperations;
51+
}) => {
52+
const enabledSkills = attributes?.routing?.skills || [];
53+
const disabledSkills = attributes?.disabled_skills?.skills || [];
54+
55+
if (operation === 'enable') {
56+
const { updatedFrom, updatedTo } = moveElementsBetweenArrays({
57+
from: disabledSkills,
58+
to: enabledSkills,
59+
elements: skills,
60+
});
61+
62+
return {
63+
...attributes,
64+
routing: {
65+
...attributes?.routing,
66+
skills: updatedTo,
67+
},
68+
disabled_skills: {
69+
...attributes.disabled_skills,
70+
skills: updatedFrom,
71+
},
72+
};
73+
}
74+
75+
if (operation === 'disable') {
76+
const { updatedFrom, updatedTo } = moveElementsBetweenArrays({
77+
from: enabledSkills,
78+
to: disabledSkills,
79+
elements: skills,
80+
});
81+
82+
return {
83+
...attributes,
84+
routing: {
85+
...attributes?.routing,
86+
skills: updatedFrom,
87+
},
88+
disabled_skills: {
89+
...attributes.disabled_skills,
90+
skills: updatedTo,
91+
},
92+
};
93+
}
94+
};
95+
96+
const updateWorkerSkills = async ({
97+
client,
98+
skills,
99+
operation,
100+
workerSid,
101+
workspaceSid,
102+
}: {
103+
workerSid: string;
104+
workspaceSid: string;
105+
skills: string[];
106+
operation: ValidOperations;
107+
client: Awaited<ReturnType<typeof getTwilioClient>>;
108+
}) => {
109+
try {
110+
const worker = await client.taskrouter.v1
111+
.workspaces(workspaceSid)
112+
.workers(workerSid)
113+
.fetch();
114+
115+
const workerAttributes = JSON.parse(worker.attributes);
116+
const updatedAttributes = updateSkillsOperation({
117+
attributes: workerAttributes,
118+
skills,
119+
operation,
120+
});
121+
122+
await worker.update({ attributes: JSON.stringify(updatedAttributes) });
123+
124+
return newOk(workerSid);
125+
} catch (err) {
126+
return newErr({
127+
error: err,
128+
message: 'Failed to update worker skills',
129+
extraProperties: { workerSid },
130+
});
131+
}
132+
};
133+
134+
export const updateWorkersSkills: AccountScopedHandler = async ({ body }, accountSid) => {
135+
const { workers, skills, operation } = body;
136+
137+
if (!workers || !Array.isArray(workers)) return newMissingParameterResult('workers');
138+
if (!skills || !Array.isArray(skills)) return newMissingParameterResult('skills');
139+
if (!operation || !validOperations.includes(operation))
140+
return newMissingParameterResult('operation');
141+
142+
const workspaceSid = await getTwilioWorkspaceSid(accountSid);
143+
144+
const client = await getTwilioClient(accountSid);
145+
146+
const result = await Promise.all(
147+
workers.map(workerSid =>
148+
updateWorkerSkills({ operation, skills, workerSid, workspaceSid, client }),
149+
),
150+
);
151+
152+
console.debug(`Skills ${skills} ${operation} for workers ${workers}`);
153+
154+
return newOk({
155+
message: `Skills ${skills} ${operation} for workers ${workers}`,
156+
result,
157+
});
158+
};

lambdas/packages/hrm-form-definitions/form-definitions/ca/v1/customStrings/Substitutions.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@
170170
"Switchboard-ModalTitleTurnOffSwitchboard": "Are you sure you want to turn off switchboarding?",
171171
"Switchboard-ButtonTurnOffSwitchboard": "Turn off switchboard",
172172
"Switchboard-ActivateSwitchboarding": "Activate switchboarding",
173-
"Switchboard-PleaseSelectQueue": "Please select a queue"
173+
"Switchboard-PleaseSelectQueue": "Please select a queue",
174+
175+
"QueuesStatsHeaderAvgWaitTime": "ASA"
174176

175177
},
176178
"fr": {
@@ -392,6 +394,8 @@
392394
"Switchboard-StatusInactive": "Commutation : désactivé",
393395
"Switchboard-ModalTitleTurnOffSwitchboard": "Êtes-vous sûr de vouloir désactiver la commutation?",
394396
"Switchboard-ButtonTurnOffSwitchboard": "Désactiver",
395-
"Switchboard-ActivateSwitchboarding": "Activer la commutation"
397+
"Switchboard-ActivateSwitchboarding": "Activer la commutation",
398+
399+
"QueuesStatsHeaderAvgWaitTime": "ASA"
396400
}
397401
}

lambdas/packages/hrm-form-definitions/form-definitions/ukmh/v1/CannedResponses.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,13 @@
3838
{
3939
"label": "Bye - Survey link",
4040
"text": "Just before you go, here at The Mix, we want to make sure we’re providing services that suit your needs and preferences. To get that right we’d love to ask you a few questions about your preferences when it comes to services and your experience with them. Could we contact you after this conversation via email to send you a link to our short survey?"
41+
},
42+
{
43+
"label": "Moving into signposting",
44+
"text": "hank you for everything you've shared today. Here at The Mix, as well as offering emotional support and a space to talk, something else we can offer is to look for some further resources or services that might be able to support you with some of the themes and topics we've explored today. Is that something you'd be interested in?"
45+
},
46+
{
47+
"label": "Pre-signposting questions",
48+
"text": "Just before I take a look on our resource data-base, may I ask you to kindly confirm your age, gender identity, as well as your nearest city or town? This is to help us locate services that are as relevant as possible for yourself.\n\nIf you do not wish me to look for local services, or you don't want to share your nearest city or town, sharing the name of the county you live in or the wider area (either England, Wales, Scotland, or Northern Ireland) would be helpful."
4149
}
4250
]

lambdas/packages/hrm-form-definitions/form-definitions/ukmh/v1/PrepopulateMappings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"anxietyScale": "ChildInformationTab.anxietyScale",
88
"moodhappinessBefore": "ChildInformationTab.moodhappinessBefore",
99
"bookingReference": "ChildInformationTab.bookingReference",
10-
"consentForResearchEvaluation": "ChildInformationTab.consentForResearchEvaluation",
10+
"consentForResearchEvaluation": "CaseInformationTab.consentForResearchEvaluation",
1111
"ip": "CaseInformationTab.ip"
1212
},
1313
"survey":{}

lambdas/packages/hrm-form-definitions/form-definitions/ukmh/v1/customStrings/Messages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"en": {
33
"WelcomeMsg": "You’re now connected to The PeerChat Service. Your PeerChat Supporter will be with you shortly.",
4-
"GoodbyeMsg": "The Peer Supporter has left the chat. Thank you for reaching out. Please contact us again if you need more help.",
4+
"GoodbyeMsg": "The PeerChat Supporter has left the chat. Thank you for reaching out. Please contact us again if you need more help.",
55
"EndChatMsg": "The Young person left the conversation.",
66
"UnsupportedMediaErrorMsg": "Sorry, the message you just sent is unsupported and could not be delivered."
77
}

lambdas/packages/hrm-form-definitions/form-definitions/ukmh/v1/customStrings/Substitutions.json

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
{
22
"en": {
3-
"Counsellor": "Peer Supporter",
4-
"CallSummary-Counselor": "Peer Supporter: ",
3+
"Counsellor": "PeerChat Supporter",
4+
"CallSummary-Counselor": "PeerChat Supporter: ",
55
"CallType-child": "Young Person calling about self",
66
"CallType-caller": "Someone calling about a Young Person",
77
"ConnectDialog-Child": "Copy Young Person information from this record to new contact?",
8-
"SearchForm-Counselor": "Peer Supporter",
9-
"SearchResults-CounselorName": "Peer Supporter Name:",
8+
"SearchForm-Counselor": "PeerChat Supporter",
9+
"SearchResults-CounselorName": "PeerChat Supporter Name:",
1010
"CaseHeader-CopyChildInfo": "Copy Young Person Information into Current Contact",
11-
"SharedStateLoadFormError": "The information stored in the form by previous Peer Supporter couldn't be retrieved. Starting current task with clear contact form.",
11+
"SharedStateLoadFormError": "The information stored in the form by previous PeerChat Supporter couldn't be retrieved. Starting current task with clear contact form.",
1212
"TabbedForms-AddChildInfoTab": "Young Person",
1313
"TabbedForms-ReportsChildLink": "Create link for Young Person",
14-
"TabbedForms-ReportsCounselorReport": "Report as Peer Supporter",
14+
"TabbedForms-ReportsCounselorReport": "Report as PeerChat Supporter",
1515
"Case-CaseDetailsChildName": "Young Person Name",
16-
"Case-CaseDetailsOwner": "Peer Supporter",
16+
"Case-CaseDetailsOwner": "PeerChat Supporter",
1717
"Case-EditCaseOverview": "Edit Case Information",
18-
"CSAMCLC-ActionHeaderAdded": "Added {{date}} at {{time}} &nbsp;&nbsp; Peer Supporter: {{counsellor}}",
19-
"Case-ActionHeaderCounselor": "Peer Supporter:",
18+
"CSAMCLC-ActionHeaderAdded": "Added {{date}} at {{time}} &nbsp;&nbsp; PeerChat Supporter: {{counsellor}}",
19+
"Case-ActionHeaderCounselor": "PeerChat Supporter:",
2020
"Case-PerpetratorRelationship": "Relationship to Young Person: ",
21-
"Case-Peer Supporter": "Peer Supporter",
21+
"Case-Peer Supporter": "PeerChat Supporter",
2222
"Case-ChildIsAtRisk": "Young Person is at risk",
2323
"CaseList-THChildName": "Young Person",
24-
"CaseList-THCounselor": "Peer Supporter",
25-
"CaseList-Filters-Counselor": "Peer Supporter",
26-
"CaseList-Filters-SearchForCounselor": "Search for Peer Supporter by name",
24+
"CaseList-THCounselor": "PeerChat Supporter",
25+
"CaseList-Filters-Counselor": "PeerChat Supporter",
26+
"CaseList-Filters-SearchForCounselor": "Search for PeerChat Supporter by name",
2727
"CallerForm-RelationshipToChild": "Relationship to Young Person",
2828
"CallerSection-RelationshipToChild": "Relationship to Young Person",
29-
"ContactDetails-GeneralDetails-Counselor": "Peer Supporter",
29+
"ContactDetails-GeneralDetails-Counselor": "PeerChat Supporter",
3030
"ContactDetails-GeneralDetails-ChildHearAboutUs": "How did the Young Person hear about us?",
3131
"ContactDetails-GeneralDetails-DiscussRights": "Did you discuss rights with the Young Person?",
3232
"ContactDetails-GeneralDetails-SolvedProblem": "Did the Young Person feel we solved their problem?",
@@ -69,9 +69,9 @@
6969
"CaseList-Filters-Categories": "Topics",
7070
"CaseList-Filters-SearchByCategory": "Search by topic",
7171
"Contact-EditCategories": "Edit Topics",
72-
"GeneralizedSearchForm-Counselor": "Peer Supporter",
73-
"CaseHeader-Counsellor": "Peer Supporter",
74-
"Case-DetailsHeaderCounselor": "Peer Supporter",
72+
"GeneralizedSearchForm-Counselor": "PeerChat Supporter",
73+
"CaseHeader-Counsellor": "PeerChat Supporter",
74+
"Case-DetailsHeaderCounselor": "PeerChat Supporter",
7575
"Categories-Title": "Choose appropriate topic(s) for this contact",
7676
"CaseList-THCategory": "Topic"
7777
}

0 commit comments

Comments
 (0)