Skip to content

Commit 2261bcf

Browse files
authored
ECHO-668 helper changes for anonymous transcripts mode (#436) (#437)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added conversation anonymization feature that replaces personal information with placeholders in transcripts. * Added visual indicators for anonymized conversations and verified artifacts. * **Changes** * Anonymized conversations now disable audio playback, audio download, and retranscription. * Updated UI messaging to inform users of anonymization restrictions and privacy protections. * **Bug Fixes** * Improved stability by protecting analytics tracking from failures. * **Documentation** * Added comprehensive translations across multiple languages for anonymization-related features. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent e1e53bc commit 2261bcf

33 files changed

Lines changed: 1364 additions & 1091 deletions

echo/directus/sync/collections/operations.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"resolve": null,
8080
"reject": null,
8181
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
82-
"_syncId": "615a54cd-a72e-41ad-9403-9577c80280d6"
82+
"_syncId": "84c38ea6-5d15-429f-8c24-9485d54ba7be"
8383
},
8484
{
8585
"name": "Email Send Operation Failed Dutch",
@@ -93,7 +93,7 @@
9393
"resolve": null,
9494
"reject": null,
9595
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
96-
"_syncId": "84c38ea6-5d15-429f-8c24-9485d54ba7be"
96+
"_syncId": "615a54cd-a72e-41ad-9403-9577c80280d6"
9797
},
9898
{
9999
"name": "failed",
@@ -107,7 +107,7 @@
107107
"resolve": null,
108108
"reject": null,
109109
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
110-
"_syncId": "8d8d787a-dbc4-44f9-9ab4-28e3f3d5f31c"
110+
"_syncId": "eb6f8253-647f-4fb1-9010-e93594ba065e"
111111
},
112112
{
113113
"name": "failed",
@@ -121,7 +121,7 @@
121121
"resolve": null,
122122
"reject": null,
123123
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
124-
"_syncId": "eb6f8253-647f-4fb1-9010-e93594ba065e"
124+
"_syncId": "8d8d787a-dbc4-44f9-9ab4-28e3f3d5f31c"
125125
},
126126
{
127127
"name": "Filter Emails",
@@ -132,10 +132,10 @@
132132
"options": {
133133
"code": "module.exports = async function(data) {\n\n const submissions = data.get_all_participants;\n \n // Filter submissions to only include those where email_opt_in is true\n const filteredSubmissions = submissions.filter(sub => sub.email_opt_in === true);\n\n // Create an array with email, project_id and an email_opt_out token for each submission\n const result = filteredSubmissions.map(sub => ({\n project_name: data.project_data[0].name || '',\n\t\tdefault_conversation_title: data.project_data[0].default_conversation_title || '',\n\t\tconversation_name: sub.conversation_id.participant_name || '',\n email: sub.email,\n project_id: sub.project_id || '',\n token: sub.email_opt_out_token,\n language: data.check_report_language[0].language || 'empty',\n ADMIN_BASE_URL: \"{{ $env.ADMIN_BASE_URL }}\" || \"http://localhost:5173\",\n PARTICIPANT_BASE_URL: \"{{ $env.PARTICIPANT_BASE_URL }}\" || \"http://localhost:5174\", \n }));\n \n return result;\n};"
134134
},
135-
"resolve": "b8144cee-59f6-40d9-a849-dd0c639e4e31",
135+
"resolve": "e101f00d-2fb8-4f40-9e0e-4d24da5bb1e9",
136136
"reject": null,
137137
"flow": "ec4e7ea5-72de-4365-b66f-d8f11b549495",
138-
"_syncId": "ca1ffbc5-cfce-4fb4-8f15-c128ea407d41"
138+
"_syncId": "efb3982e-5703-4c07-8982-a6e1b5218e4a"
139139
},
140140
{
141141
"name": "Filter Emails",
@@ -146,10 +146,10 @@
146146
"options": {
147147
"code": "module.exports = async function(data) {\n\n const submissions = data.get_all_participants;\n \n // Filter submissions to only include those where email_opt_in is true\n const filteredSubmissions = submissions.filter(sub => sub.email_opt_in === true);\n\n // Create an array with email, project_id and an email_opt_out token for each submission\n const result = filteredSubmissions.map(sub => ({\n project_name: data.project_data[0].name || '',\n\t\tdefault_conversation_title: data.project_data[0].default_conversation_title || '',\n\t\tconversation_name: sub.conversation_id.participant_name || '',\n email: sub.email,\n project_id: sub.project_id || '',\n token: sub.email_opt_out_token,\n language: data.check_report_language[0].language || 'empty',\n ADMIN_BASE_URL: \"{{ $env.ADMIN_BASE_URL }}\" || \"http://localhost:5173\",\n PARTICIPANT_BASE_URL: \"{{ $env.PARTICIPANT_BASE_URL }}\" || \"http://localhost:5174\", \n }));\n \n return result;\n};"
148148
},
149-
"resolve": "e101f00d-2fb8-4f40-9e0e-4d24da5bb1e9",
149+
"resolve": "b8144cee-59f6-40d9-a849-dd0c639e4e31",
150150
"reject": null,
151151
"flow": "ec4e7ea5-72de-4365-b66f-d8f11b549495",
152-
"_syncId": "efb3982e-5703-4c07-8982-a6e1b5218e4a"
152+
"_syncId": "ca1ffbc5-cfce-4fb4-8f15-c128ea407d41"
153153
},
154154
{
155155
"name": "log environment vars",
@@ -213,7 +213,7 @@
213213
"resolve": null,
214214
"reject": null,
215215
"flow": "ec4e7ea5-72de-4365-b66f-d8f11b549495",
216-
"_syncId": "84852456-3f3a-4906-be94-8b750159883b"
216+
"_syncId": "e8274ad4-5844-42cd-8a6b-d40d08cf83d3"
217217
},
218218
{
219219
"name": "Report Not Published",
@@ -227,7 +227,7 @@
227227
"resolve": null,
228228
"reject": null,
229229
"flow": "ec4e7ea5-72de-4365-b66f-d8f11b549495",
230-
"_syncId": "e8274ad4-5844-42cd-8a6b-d40d08cf83d3"
230+
"_syncId": "84852456-3f3a-4906-be94-8b750159883b"
231231
},
232232
{
233233
"name": "Send Email Dutch",
@@ -256,9 +256,9 @@
256256
]
257257
},
258258
"resolve": null,
259-
"reject": "84c38ea6-5d15-429f-8c24-9485d54ba7be",
259+
"reject": "615a54cd-a72e-41ad-9403-9577c80280d6",
260260
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
261-
"_syncId": "34fb6ee5-2813-484a-a1cc-f97de097509b"
261+
"_syncId": "ea78ec02-364d-4f18-80f8-ea5ac4c787ed"
262262
},
263263
{
264264
"name": "Send Email Dutch",
@@ -287,9 +287,9 @@
287287
]
288288
},
289289
"resolve": null,
290-
"reject": "615a54cd-a72e-41ad-9403-9577c80280d6",
290+
"reject": "84c38ea6-5d15-429f-8c24-9485d54ba7be",
291291
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
292-
"_syncId": "ea78ec02-364d-4f18-80f8-ea5ac4c787ed"
292+
"_syncId": "34fb6ee5-2813-484a-a1cc-f97de097509b"
293293
},
294294
{
295295
"name": "Send Email English",
@@ -318,9 +318,9 @@
318318
]
319319
},
320320
"resolve": null,
321-
"reject": "2b24450b-6a2e-4452-aba1-9814d17fef42",
321+
"reject": "920bd181-b2a2-4f0d-94dc-3b1a08c3f4ef",
322322
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
323-
"_syncId": "9390ed2f-7dc6-4a6a-83da-2d87d478261d"
323+
"_syncId": "3dbf2ea1-17f8-4bde-aa89-43278fe9a00f"
324324
},
325325
{
326326
"name": "Send Email English",
@@ -349,9 +349,9 @@
349349
]
350350
},
351351
"resolve": null,
352-
"reject": "920bd181-b2a2-4f0d-94dc-3b1a08c3f4ef",
352+
"reject": "2b24450b-6a2e-4452-aba1-9814d17fef42",
353353
"flow": "17703446-fef0-49e9-bdc4-385db1311137",
354-
"_syncId": "3dbf2ea1-17f8-4bde-aa89-43278fe9a00f"
354+
"_syncId": "9390ed2f-7dc6-4a6a-83da-2d87d478261d"
355355
},
356356
{
357357
"name": "Trigger Email Flow",

echo/directus/sync/collections/policies.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111
{
1212
"role": "_sync_default_admin_role",
1313
"sort": 1
14-
},
15-
{
16-
"role": null,
17-
"sort": null
1814
}
1915
],
2016
"_syncId": "_sync_default_admin_policy"

echo/directus/sync/snapshot/fields/conversation/conversation_artifacts.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"readonly": false,
1717
"required": false,
1818
"searchable": true,
19-
"sort": 26,
19+
"sort": 27,
2020
"special": [
2121
"o2m"
2222
],

echo/directus/sync/snapshot/fields/conversation/is_all_chunks_transcribed.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"readonly": false,
1717
"required": false,
1818
"searchable": true,
19-
"sort": 23,
19+
"sort": 24,
2020
"special": [
2121
"cast-boolean"
2222
],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"collection": "conversation",
3+
"field": "is_anonymized",
4+
"type": "boolean",
5+
"meta": {
6+
"collection": "conversation",
7+
"conditions": null,
8+
"display": null,
9+
"display_options": null,
10+
"field": "is_anonymized",
11+
"group": null,
12+
"hidden": false,
13+
"interface": "boolean",
14+
"note": null,
15+
"options": null,
16+
"readonly": false,
17+
"required": false,
18+
"searchable": true,
19+
"sort": 21,
20+
"special": [
21+
"cast-boolean"
22+
],
23+
"translations": null,
24+
"validation": null,
25+
"validation_message": null,
26+
"width": "full"
27+
},
28+
"schema": {
29+
"name": "is_anonymized",
30+
"table": "conversation",
31+
"data_type": "boolean",
32+
"default_value": false,
33+
"max_length": null,
34+
"numeric_precision": null,
35+
"numeric_scale": null,
36+
"is_nullable": true,
37+
"is_unique": false,
38+
"is_indexed": false,
39+
"is_primary_key": false,
40+
"is_generated": false,
41+
"generation_expression": null,
42+
"has_auto_increment": false,
43+
"foreign_key_table": null,
44+
"foreign_key_column": null
45+
}
46+
}

echo/directus/sync/snapshot/fields/conversation/is_audio_processing_finished.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"readonly": false,
1717
"required": false,
1818
"searchable": true,
19-
"sort": 22,
19+
"sort": 23,
2020
"special": [
2121
"cast-boolean"
2222
],

echo/directus/sync/snapshot/fields/conversation/is_finished.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"readonly": false,
1717
"required": false,
1818
"searchable": true,
19-
"sort": 21,
19+
"sort": 22,
2020
"special": [
2121
"cast-boolean"
2222
],

echo/directus/sync/snapshot/fields/conversation/linked_conversations.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"readonly": false,
2222
"required": false,
2323
"searchable": true,
24-
"sort": 24,
24+
"sort": 25,
2525
"special": [
2626
"o2m"
2727
],

echo/directus/sync/snapshot/fields/conversation/linking_conversations.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"readonly": false,
2121
"required": false,
2222
"searchable": true,
23-
"sort": 25,
23+
"sort": 26,
2424
"special": [
2525
"o2m"
2626
],

echo/frontend/src/components/conversation/ConversationAccordion.tsx

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
useMediaQuery,
3535
useSessionStorage,
3636
} from "@mantine/hooks";
37+
import { ShieldCheckIcon } from "@phosphor-icons/react";
3738
import {
3839
IconArrowsExchange,
3940
IconArrowsUpDown,
@@ -556,11 +557,13 @@ const ConversationAccordionItem = ({
556557
<Text className="pl-[4px] text-sm font-normal">
557558
{conversation.participant_name || conversation.title}
558559
</Text>
560+
559561
{conversation.title && conversation.participant_name && (
560562
<Tooltip label={conversation.title}>
561563
<IconInfoCircle size={14} className="text-gray-400" />
562564
</Tooltip>
563565
)}
566+
564567
{hasVerifiedArtefacts && (
565568
<Tooltip label={t`Has verified artifacts`}>
566569
<ThemeIcon
@@ -574,7 +577,22 @@ const ConversationAccordionItem = ({
574577
</ThemeIcon>
575578
</Tooltip>
576579
)}
580+
581+
{conversation.is_anonymized && (
582+
<Tooltip label={t`Anonymized conversation`}>
583+
<ThemeIcon
584+
variant="subtle"
585+
color="primary"
586+
aria-label={t`anonymized conversation`}
587+
size={18}
588+
style={{ cursor: "default" }}
589+
>
590+
<ShieldCheckIcon />
591+
</ThemeIcon>
592+
</Tooltip>
593+
)}
577594
</Group>
595+
578596
<ConversationStatusIndicators
579597
conversation={conversation}
580598
showDuration={showDuration}
@@ -914,8 +932,11 @@ export const ConversationAccordion = ({
914932

915933
// Handle select all
916934
const handleSelectAllClick = () => {
917-
try { analytics.trackEvent(events.SELECT_ALL_CLICK); }
918-
catch (error) { console.warn("Analytics tracking failed:", error); }
935+
try {
936+
analytics.trackEvent(events.SELECT_ALL_CLICK);
937+
} catch (error) {
938+
console.warn("Analytics tracking failed:", error);
939+
}
919940
setSelectAllModalOpened(true);
920941
setSelectAllResult(null);
921942
};
@@ -927,8 +948,11 @@ export const ConversationAccordion = ({
927948
return;
928949
}
929950

930-
try { analytics.trackEvent(events.SELECT_ALL_CONFIRM); }
931-
catch (error) { console.warn("Analytics tracking failed:", error); }
951+
try {
952+
analytics.trackEvent(events.SELECT_ALL_CONFIRM);
953+
} catch (error) {
954+
console.warn("Analytics tracking failed:", error);
955+
}
932956

933957
setSelectAllLoading(true);
934958
try {
@@ -940,11 +964,17 @@ export const ConversationAccordion = ({
940964
verifiedOnly: showOnlyVerified || undefined,
941965
});
942966
setSelectAllResult(result);
943-
try { analytics.trackEvent(events.SELECT_ALL_SUCCESS); }
944-
catch (error) { console.warn("Analytics tracking failed:", error); }
967+
try {
968+
analytics.trackEvent(events.SELECT_ALL_SUCCESS);
969+
} catch (error) {
970+
console.warn("Analytics tracking failed:", error);
971+
}
945972
} catch (_error) {
946-
try { analytics.trackEvent(events.SELECT_ALL_ERROR); }
947-
catch (error) { console.warn("Analytics tracking failed:", error); }
973+
try {
974+
analytics.trackEvent(events.SELECT_ALL_ERROR);
975+
} catch (error) {
976+
console.warn("Analytics tracking failed:", error);
977+
}
948978
toast.error(t`Failed to add conversations to context`);
949979
setSelectAllModalOpened(false);
950980
} finally {

0 commit comments

Comments
 (0)