feat(cc): add campaign preview#684
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c3cda98967
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
This pull request is automatically being deployed by Amplify Hosting (learn more). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9a2feebe73
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 003596fba6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const globalVariablesRef = useRef(latestGlobalVariables); | ||
| if (latestGlobalVariables.length > 0) { | ||
| globalVariablesRef.current = latestGlobalVariables; | ||
| } |
There was a problem hiding this comment.
Reset campaign CAD when a new preview contact arrives
When Skip/Remove advances the same campaign task to another contact and the SDK update omits or empties callAssociatedData, this ref never clears because it only writes non-empty values. The component already detects new contacts via timeoutTimestamp below, but GlobalVariablesPanel will keep showing the previous contact's global variables on the next preview; reset/key the ref when the offer changes, and mirror the same fix in the popover path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97c2c9dd1a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }; | ||
|
|
||
| const CampaignTaskWithMetrics = withMetrics(CampaignTask, 'CampaignTask'); | ||
| export default CampaignTaskWithMetrics; |
There was a problem hiding this comment.
Should we wrap this with ErrorBoundary too? The other task components all have it and if something blows up in here it'd take down the whole TaskList.
There was a problem hiding this comment.
i added an ErrorBoundary to where CampaignTask is rendered
| @@ -648,6 +741,36 @@ class StoreWrapper implements IStoreWrapper { | |||
| this.refreshTaskList(); | |||
| }; | |||
|
|
|||
There was a problem hiding this comment.
nit: event is untyped here — should be ITask like the other handlers.
There was a problem hiding this comment.
should be resolved
| setIsAcceptClicked(true); | ||
| setHandleTimestamp(Date.now()); | ||
| disableAllButtons(); | ||
| logger?.info('CC-Widgets: CampaignTask: Auto-accept UI state set, awaiting backend', { |
There was a problem hiding this comment.
nit: This useState is way down here after a bunch of useEffects. Easy to miss — mind moving it up with the rest of the state declarations?
There was a problem hiding this comment.
there isn't a useState down here
97c2c9d to
e8a783a
Compare
| */ | ||
| export function isCampaignPreviewTask(task: ITask): boolean { | ||
| const outboundType = task.data.interaction.outboundType ?? ''; | ||
| const cpd = task.data.interaction.callProcessingDetails as unknown as Record<string, string | undefined>; |
There was a problem hiding this comment.
Is there a way to avoid using unknown? Do you think the correct type should come from the SDK?
There was a problem hiding this comment.
The type doesnt come from the SDK, although we can make an udpate to the SDK to export the type as a later item
| get acceptedCampaignIds() { | ||
| return this.store.acceptedCampaignIds; | ||
| } | ||
|
|
There was a problem hiding this comment.
Should we have a setter for this state as well??
There was a problem hiding this comment.
These get set in the addAcceptedCamapaign and removeAcceptedCampaign functions
| // Persist global variables across task updates — some store refreshes | ||
| // replace the task with a snapshot that omits callAssociatedData, | ||
| // which causes getAgentViewableGlobalVariables to return []. | ||
| // We intentionally keep the previous values when length === 0 because | ||
| // an empty array indicates missing data, not a legitimate clearing of | ||
| // variables. Variables are never cleared mid-call by the backend. | ||
| const globalVariablesRef = useRef(latestGlobalVariables); | ||
| if (latestGlobalVariables.length > 0) { | ||
| globalVariablesRef.current = latestGlobalVariables; | ||
| } | ||
| const globalVariables = globalVariablesRef.current; |
There was a problem hiding this comment.
If we need to persist variables across refrershed then ideally we should put the value in the store. But if its working then we dont need to make any changes
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1406aa2787
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97626ebe20
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| agentId, | ||
| }); | ||
|
|
||
| if (isUnacceptedCampaignPreview(currentTask, acceptedCampaignIds)) { |
There was a problem hiding this comment.
Guard null currentTask before campaign preview check
CallControlCADInternal now calls isUnacceptedCampaignPreview(currentTask, acceptedCampaignIds) before verifying that currentTask exists. store.currentTask is initialized to null and can be null between interactions, so this path throws (task.data access inside isCampaignPreviewTask) whenever the widget is mounted without an active task. In apps that render CallControlCAD persistently, this causes repeated error-boundary trips and hides the component until another render with a task.
Useful? React with 👍 / 👎.
| if ( | ||
| task && | ||
| this.isCampaignPreview(task) && | ||
| task.data.interaction.state === 'new' && | ||
| !this.store.acceptedCampaignIds.has(task.data.interactionId) | ||
| ) { | ||
| return; |
There was a problem hiding this comment.
Reset current task when blocking pending campaign previews
This early return prevents assignment of a pending campaign preview, but refreshTaskList() can call setCurrentTask() after removing a previous currentTask. In that flow, returning here leaves the old task object in store.currentTask, so call-control widgets can continue rendering stale state/actions for the ended interaction until a later event updates selection. The guard should avoid promoting the preview while still clearing the prior current task.
Useful? React with 👍 / 👎.
COMPLETES #https://jira-eng-sjc12.cisco.com/jira/browse/CAI-7660 and https://jira-eng-sjc12.cisco.com/jira/browse/CAI-7662
This pull request addresses
Adds the UI for the campaign preview functionality
by making the following changes
creates new components for campaign preview
creates global variable component to share between call control cad and campaign components
makes changes to call control cad to render campaign preview ui
adds campaign preview to task list only when flag is enabled
Figma: https://www.figma.com/design/JvnDPHRIXlvZEiSSCBwtjk/WxCC-6242---Preview-Campaign?node-id=6473-173114&m=dev
Vidcast: https://app.vidcast.io/share/72f03823-417d-4417-8a5a-35032943f3d3
Change Type
The following scenarios were tested
The testing is done with the amplify link
< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >
Can accept campaign previews
Can skip campaign previews
Can remove campaign previews
Can disable skip/remove buttons based on campaign settings
Campaign auto action fires when campaign times out
The GAI Coding Policy And Copyright Annotation Best Practices
Checklist before merging
Make sure to have followed the contributing guidelines before submitting.