Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
58c8efa
feat: add Notifications schema (model, migrations, seed, tests)
thewatermethod Jun 1, 2026
50ce3f0
Simplify model
thewatermethod Jun 1, 2026
5f18679
Merge branch mb/TTAHUB-5539/actionable-notification-spec into mb/TTAH…
thewatermethod Jun 1, 2026
bd6ed6a
Update to add "triggeredAt" to model
thewatermethod Jun 1, 2026
f3440d0
Merge branch 'mb/TTAHUB-5539/actionable-notification-spec' into mb/TT…
thewatermethod Jun 1, 2026
e367d50
Add all enum types
thewatermethod Jun 2, 2026
6d9000f
Scopes, work in progress
thewatermethod Jun 2, 2026
a4d249b
Remove digest from notifications enum
thewatermethod Jun 2, 2026
a3884cd
Merge branch 'mb/TTAHUB/actionable-notification-model' into mb/TTAHUB…
thewatermethod Jun 2, 2026
c258671
Add notification scopes
thewatermethod Jun 2, 2026
b1a20ff
Register scopes
thewatermethod Jun 2, 2026
3b4ccbf
Fix bugs, add integration test
thewatermethod Jun 2, 2026
c3e9c0a
Add services and tests
thewatermethod Jun 3, 2026
51c56f6
Update model with other needed field
thewatermethod Jun 3, 2026
b53428a
Merge branch 'mb/TTAHUB/actionable-notification-model' into mb/TTAHUB…
thewatermethod Jun 3, 2026
233334a
Merge branch 'mb/TTAHUB-5385/add-notification-scopes' into mb/TTAHUB-…
thewatermethod Jun 3, 2026
bfc17c4
Accomodate missing column in services
thewatermethod Jun 3, 2026
0d71b6b
Potential fix for pull request finding
thewatermethod Jun 3, 2026
194a776
Potential fix for pull request finding
thewatermethod Jun 3, 2026
a68e39b
Add notification configuration tests
thewatermethod Jun 3, 2026
d9c6a39
fix: replace generic deleteNotification(scopes) with targeted delete …
thewatermethod Jun 5, 2026
d7b6c91
Merge branch 'mb/TTAHUB-5539/actionable-notification-spec' into mb/TT…
thewatermethod Jun 5, 2026
90e35d5
Merge branch mb/TTAHUB-5385/add-notification-scopes into mb/TTAHUB-53…
thewatermethod Jun 5, 2026
42b970c
Fix bad test
thewatermethod Jun 5, 2026
64bf0c5
[TTAHUB-5387] Add notification handlers (#3674)
thewatermethod Jun 8, 2026
0491e7e
Merge upstream
thewatermethod Jun 8, 2026
2bcf54a
Regenerate Logical Data Model
thewatermethod Jun 8, 2026
13147b4
Merge branch mb/TTAHUB-5539/actionable-notification-spec into mb/TTAH…
thewatermethod Jun 8, 2026
31033f4
Merge branch 'mb/TTAHUB-5539/actionable-notification-spec' into mb/TT…
thewatermethod Jun 8, 2026
58ca47f
Condense down notifications table
thewatermethod Jun 8, 2026
35d7ce2
Remove extraneous migration
thewatermethod Jun 8, 2026
a0be75d
Fix LDM bugs
thewatermethod Jun 8, 2026
adb8e4f
Remove extra columns from migration
thewatermethod Jun 8, 2026
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: 1 addition & 1 deletion docs/logical_data_model.encoded

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions docs/logical_data_model.puml
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,22 @@ class NextSteps{
completeDate : date
}

class Notifications{
* id : integer : <generated>
userId : integer : REFERENCES "Users".id
* createdAt : timestamp with time zone
* type : enum
* updatedAt : timestamp with time zone
archivedAt : date
displayId : varchar(255)
entityId : integer
label : text
link : text
text : text
triggeredAt : date
viewedAt : date
}

class ObjectiveCollaborators{
* id : integer : <generated>
* collaboratorTypeId : integer : REFERENCES "CollaboratorTypes".id
Expand Down Expand Up @@ -1346,6 +1362,7 @@ class Users{
}

enum enum_Users_flags {
actionable_notifications
monitoring-regional-dashboard
quality_assurance_dashboard
}
Expand Down Expand Up @@ -2520,6 +2537,20 @@ class ZALNextSteps{
session_sig : text
}

class ZALNotifications{
* id : bigint : <generated>
* data_id : bigint
* dml_as : bigint
* dml_by : bigint
* dml_timestamp : timestamp with time zone
* dml_txid : uuid
* dml_type : enum
descriptor_id : integer
new_row_data : jsonb
old_row_data : jsonb
session_sig : text
}

class ZALObjectiveCollaborators{
* id : bigint : <generated>
* data_id : bigint
Expand Down Expand Up @@ -3083,6 +3114,7 @@ Users "1" --[#black,dashed,thickness=2]--{ "n" GoalCollaborators : user, goalCo
Users "1" --[#black,dashed,thickness=2]--{ "n" GoalStatusChanges : user, goalStatusChanges
Users "1" --[#black,dashed,thickness=2]--{ "n" GroupCollaborators : user, groupCollaborators
Users "1" --[#black,dashed,thickness=2]--{ "n" NationalCenterUsers : user, nationalCenterUsers
Users "1" --[#black,dashed,thickness=2]--{ "n" Notifications : user, notifications
Users "1" --[#black,dashed,thickness=2]--{ "n" ObjectiveCollaborators : user, objectiveCollaborators
Users "1" --[#black,dashed,thickness=2]--{ "n" Permissions : user, permissions
Users "1" --[#black,dashed,thickness=2]--{ "n" SessionReportPilotTrainers : trainer, sessionTrainers
Expand Down
127 changes: 110 additions & 17 deletions specs/actionable-notifications/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Points: 5
- type: NOTIFICATION_TYPE[enum]
- link: computed link
- label: label for link
- displayId: displayed in the second column of the UI; it's a whole report ID like `R01-AR-1234`
- text: computed message (see notification configuration, next section)
- archivedAt: Date, nullable
- viewedAt: Date, nullable
Expand All @@ -48,31 +49,122 @@ Create simple seeded data, also for use during development

### Notification configuration

As example. These will be created as we build out notifications
The full enum lives in [`src/constants.js`](../../src/constants.js) under `NOTIFICATION_TYPES`. The table below maps each notification trigger (by CSV row ID) to its enum key. See [`notifications.csv`](./notifications.csv) for full copy, recipient, and channel detail.

```ts
**Convention:** one enum key per *trigger*. When the same event fans out to multiple recipients (e.g., approver, creator, collaborator), those variants share a single key — the recipient is resolved at send time via metadata passed to `createNotification`.

**Excluded rows:** rows with status `Out of scope` in the CSV are not represented in the enum. Rows with status `Paused` are included and noted below.

// also add to UserSettings enum
// @src/constants.js
**Worked example:**

```ts
// src/constants.js
const NOTIFICATION_TYPES = {
// for example
ACTIVITY_REPORT_CHANGES_REQUESTED: 'emailWhenChangeRequested',
ACTIVITY_REPORT_NEEDS_ACTION: 'changesRequested',
// ... etc
};

// Paired NOTIFICATION_CONFIGURATION entry (to be added per notification)
const NOTIFICATION_CONFIGURATION = {
// and so forth. We need custom functions for each type since each notification has bespoke text
[NOTIFICATION_TYPES.ACTIVITY_REPORT_CHANGES_REQUESTED]: {
textFn: ({ userName, recipientName }) => `${userName} has requested changes to your Activity Report for ${recipientName}.`,
// whether or not we display primary button style or outline button style ("view" vs "take action")
actionable: true,
linkFn: ({id}) => `/activity-report/${id}`,
linkText: () => 'View AR',
},
}

[NOTIFICATION_TYPES.ACTIVITY_REPORT_NEEDS_ACTION]: {
textFn: ({ userName, recipientName }) =>
`${userName} has requested changes to your Activity Report for ${recipientName}.`,
// whether or not we display primary button style or outline button style ("view" vs "take action")
actionable: true,
linkFn: ({ id }) => `/activity-reports/${id}`,
linkText: (metadata) => 'View AR',
displayId: (metadata) => `${metadata.displayId}`,
},
};
```

#### Notification inventory

##### Activity Report

| CSV row(s) | Enum key | Notes |
|---|---|---|
| AR-1a/b | `ACTIVITY_REPORT_COLLABORATOR_ADDED` | existing |
| AR-2a–d, AR-3a–d | `ACTIVITY_REPORT_SUBMITTED` | existing; covers both creator & collaborator submitting |
| AR-4a–d, AR-5a–d | `ACTIVITY_REPORT_RESUBMITTED` | new |
| AR-6a–f, AR-8a–f | `ACTIVITY_REPORT_NEEDS_ACTION` | existing; covers Approver 1 & 2 requesting changes |
| AR-7a–f, AR-9a–f | `ACTIVITY_REPORT_APPROVED` | existing; covers Approver 1 & 2 approvals |
| `ACTIVITY_REPORT_RECIPIENT_REPORT_APPROVED` | recipient notified on final approval | existing |
| AR-10a | `ACTIVITY_REPORT_SUBMITTED_DIGEST` | existing |
| AR-11 | `ACTIVITY_REPORT_NEEDS_ACTION_DIGEST` | existing |
| AR-12 | `ACTIVITY_REPORT_APPROVED_DIGEST` | existing |
| AR-13 | `ACTIVITY_REPORT_COLLABORATOR_DIGEST` | existing |
| `ACTIVITY_REPORT_RECIPIENT_REPORT_APPROVED_DIGEST` | recipient digest | existing |
| AR-14 | `ACTIVITY_REPORT_SUBMITTED_TO_COLLABORATOR_DIGEST` | new |
| AR-15 | `ACTIVITY_REPORT_COLLABORATOR_SUBMITTED_DIGEST` | new |
| AR-20 to AR-25 | _(out of scope — not in enum)_ | |

##### Collaborative Report

| CSV row(s) | Enum key | Notes |
|---|---|---|
| CR-1a/b | `COLLAB_REPORT_COLLABORATOR_ADDED` | new |
| CR-2a–d, CR-3a–d | `COLLAB_REPORT_SUBMITTED` | new |
| CR-4a–d, CR-5a–d | `COLLAB_REPORT_RESUBMITTED` | new |
| CR-6a–f, CR-8a–f | `COLLAB_REPORT_NEEDS_ACTION` | new |
| CR-7a–f, CR-9a–f | `COLLAB_REPORT_APPROVED` | new |
| CR-10a | `COLLAB_REPORT_SUBMITTED_DIGEST` | new |
| CR-11, CR-14 | `COLLAB_REPORT_NEEDS_ACTION_DIGEST` | new |
| CR-12, CR-15 | `COLLAB_REPORT_APPROVED_DIGEST` | new |
| CR-13 | `COLLAB_REPORT_COLLABORATOR_DIGEST` | new |
| CR-16 | `COLLAB_REPORT_SUBMITTED_TO_COLLABORATOR_DIGEST` | new |
| CR-17 | `COLLAB_REPORT_COLLABORATOR_SUBMITTED_DIGEST` | new |

##### Training Report

| CSV row(s) | Enum key | Notes |
|---|---|---|
| TR-1a/b | `TRAINING_REPORT_POC_ADDED` | new |
| TR-2a/b | `TRAINING_REPORT_COLLABORATOR_ADDED` | existing |
| TR-3a–e | `TRAINING_REPORT_SESSION_SUBMITTED` | new |
| TR-4a–f | `TRAINING_REPORT_SESSION_NEEDS_ACTION` | new |
| TR-5a–d, TR-6a–d | `TRAINING_REPORT_SESSION_RESUBMITTED` | new |
| _(existing)_ | `TRAINING_REPORT_SESSION_CREATED` | existing |
| _(existing)_ | `TRAINING_REPORT_EVENT_COMPLETED` | existing |
| _(existing)_ | `TRAINING_REPORT_TASK_DUE` | existing; cron umbrella |
| _(existing)_ | `TRAINING_REPORT_EVENT_IMPORTED` | existing |
| TR-5b, TR-7b _(Paused)_ | `TRAINING_REPORT_EVENT_INFO_MISSING` | new; Paused |
| TR-6a, TR-8a _(Paused)_ | `TRAINING_REPORT_EVENT_INFO_PAST_DUE` | new; Paused |
| TR-9a/b, TR-10a/b, TR-11a/b _(Paused)_ | `TRAINING_REPORT_SESSION_INFO_MISSING` | new; Paused |
| TR-10c, TR-12 _(Paused)_ | `TRAINING_REPORT_SESSION_INFO_PAST_DUE` | new; Paused |
| TR-13, TR-15 _(Paused)_ | `TRAINING_REPORT_NO_SESSIONS_CREATED` | new; Paused |
| TR-14, TR-16 _(Paused)_ | `TRAINING_REPORT_NO_SESSIONS_PAST_DUE` | new; Paused |
| TR-17 _(Paused)_ | `TRAINING_REPORT_EVENT_NOT_COMPLETED` | new; Paused |
| TR-18 _(Paused)_ | `TRAINING_REPORT_EVENT_NOT_COMPLETED_PAST_DUE` | new; Paused |
| TR-19 | `TRAINING_REPORT_POC_ADDED_DIGEST` | new |
| TR-20 | `TRAINING_REPORT_COLLABORATOR_ADDED_DIGEST` | new |
| TR-21 | `TRAINING_REPORT_SESSION_SUBMITTED_DIGEST` | new |
| TR-22 | `TRAINING_REPORT_SESSION_NEEDS_ACTION_DIGEST` | new |
| TR-23 | `TRAINING_REPORT_EVENT_INFO_MISSING_DIGEST` | new |
| TR-24, TR-25 | `TRAINING_REPORT_SESSION_INFO_MISSING_DIGEST` | new |
| TR-26 | `TRAINING_REPORT_NO_SESSIONS_CREATED_DIGEST` | new |
| TR-27 | `TRAINING_REPORT_EVENT_NOT_COMPLETED_DIGEST` | new |

##### Communication Log

| CSV row(s) | Enum key | Notes |
|---|---|---|
| CL-1a/b | `COMMUNICATION_LOG_TTA_STAFF_ADDED` | new |
| CL-2a/b | `COMMUNICATION_LOG_RECIPIENT_IN_GROUP` | new |
| CL-3 | `COMMUNICATION_LOG_TTA_STAFF_ADDED_DIGEST` | new |
| CL-4 | `COMMUNICATION_LOG_RECIPIENT_IN_GROUP_DIGEST` | new |

##### Monitoring / Group / System

| CSV row(s) | Enum key | Notes |
|---|---|---|
| Misc-1a/b (Draft) | `MONITORING_GOAL_ADDED` | new; Draft status |
| Misc-1a/b (monitoring data) | `MONITORING_DATA_RECEIVED` | new |
| Misc-2a/b | `GROUP_CO_OWNER_ADDED` | new |
| Misc-3a/b | `GROUP_SHARED` | new |
| Misc-4a/b | `SYSTEM_PLANNED_OUTAGE` | new |
| Misc-5a | `SYSTEM_UNPLANNED_OUTAGE` | new |

### Services

**Ticket #2: [Create Notifications service](https://jira.acf.gov/browse/TTAHUB-5384)**
Expand Down Expand Up @@ -186,10 +278,11 @@ To be expanded upon in conjunction with PM

- Refactor home page to use new tiled markup
- Create notifications page/table, less filter component
- Filter component, filters
- Create notifications preference page
- Move email opt-in
- Modify site header to add bell component/update avatar menu
- Modify admin interface for creating site alerts to also create notifications
- Add filter component to notifications page/table

Note: additional tickets will be needed to *register* the new notifications/emails.
Note: additional tickets will be needed to *register* the new notifications/emails on a per/notification basis
Loading