Skip to content

feat(widgets): added-agent-rona-popup - #389

Merged
adhmenon merged 8 commits into
webex:ccwidgetsfrom
adhmenon:agent-rona
Feb 21, 2025
Merged

feat(widgets): added-agent-rona-popup#389
adhmenon merged 8 commits into
webex:ccwidgetsfrom
adhmenon:agent-rona

Conversation

@adhmenon

Copy link
Copy Markdown
Contributor

COMPLETES SPARK-604350

ISSUE

  • In widgets, we were receiving the agent rona related event but we were not handling it in the sample app.

FIX

  • Code has been added to include a taskRejectionCallback handler inside of the widgets store so users can specify their own custom task rejection method
  • For our use case, we show a popup whihch allows users to set a state inside of our sample app (this is widgets sample app specific change)

Vidcasts

Manual Test Cases

  • Tested with webRTC in both web and react components
  • Tested with external type in both web and react
  • Tested by keeping both web and react sample pages open and used external type (we got popup in both as expected)
  • Tested that the user state is not getting messed up with this (cross verified with agent desktop)

Test Doc Link - https://confluence-eng-gpk2.cisco.com/conf/display/WSDK/CC+Widgets+Test+Plan

@coderabbitai

coderabbitai Bot commented Feb 18, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

The pull request introduces a new optional callback, onTaskRejected, to the task management system. This callback is added as a method in the IStoreWrapper interface and as a property in the StoreWrapper class. Additionally, a new event type, TASK_REJECT, is defined in the TASK_EVENTS enum to support task rejection events. The changes extend the existing event handling logic by invoking the onTaskRejected callback—providing a reason for task rejection—before proceeding with removing the task. Corresponding tests have been added to ensure that the callback is executed correctly. Furthermore, both React and web component sample applications have been updated with UI logic to display a popup and manage agent state changes when tasks are rejected.

Possibly related PRs

  • feat(user-state): load and change state, client timer #347: The changes in the main PR, which add the onTaskRejected method to the IStoreWrapper interface and the StoreWrapper class, are related to the modifications in the retrieved PR that involve user state management and the handling of task rejections, as both PRs enhance the functionality of their respective components by introducing new callback mechanisms.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment thread packages/contact-center/store/src/store.types.ts Outdated

class StoreWrapper implements IStoreWrapper {
store: IStore;
onTaskRejected?: (reason: string) => void;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept it as void return type and have added this in the wrapper as well.
I felt we should input it here as store will be used regardless of whether the user is using task list or incoming task. Further, we are handling the event here so I added this here.
Open to any suggestion however.

expect(storeWrapper['cc'].off).toHaveBeenCalledWith(CC_EVENTS.AGENT_MULTI_LOGIN, expect.any(Function));
});

it('should handle task rejection event and call onTaskRejected with the provided reason', () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test to check if the reason / rejected task etc. are coming properly.

setSelectedState('');
};

useEffect(() => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanted to add this code here as I wanted to set up the callback on the first mounting itself.

setSelectedWidgets((prev) => ({...prev, [name]: checked}));
};

const changeAgentState = (newState: string) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method to handle agent state change using cc from the store instance.
Please do correct me if there is an issue with this method (I didn't see anything else).

// If the selected state is "Available", we pass it as is; otherwise, we consider it "Idle".
const chosenState = newState === 'Available' ? 'Available' : 'Idle';
// In the idle codes, we need to search for the 'Idle' state with code name 'Meeting'.
const lookupCodeName = newState === 'Available' ? 'Available' : 'Meeting';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly the above 2 are needed as backend only recognises Available and Idle.
Also for idle codes, we sadly only have the above 2, hence we have to add this check (note that this is only for sample app, however). We are doing the same in the cc sdk sample app.

}

// Helper to show the task rejected popup.
function showTaskRejectedPopup(reason) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is so that we can get the popup similar to react sample app, open to suggestions here also.

@aws-amplify-us-east-2

Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-389.d1b38q61t1z947.amplifyapp.com

@adhmenon

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 18, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
widgets-samples/cc/samples-cc-wc-app/app.js (2)

146-174: Improve error handling and code style.

  1. The state lookup logic could be more robust
  2. The optional chaining suggestion from static analysis is valid

Apply these improvements:

-  const idleCode =
-    store.idleCodes &&
-    store.idleCodes.find(function(code) {
-      return code.name === lookupCodeName;
-    });
+  const idleCode = store.idleCodes?.find((code) => code.name === lookupCodeName);
🧰 Tools
🪛 Biome (1.9.4)

[error] 150-153: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


177-201: Consider enhancing the popup implementation.

The current implementation has some potential improvements:

  1. No error handling for missing DOM elements
  2. Event listener cleanup is missing
  3. Direct DOM manipulation could be encapsulated better

Would you like me to propose a more robust implementation that addresses these concerns?

packages/contact-center/store/src/storeEventsWrapper.ts (1)

187-193: Reduce code duplication in task rejection handlers.

The task rejection handling logic is duplicated between the incoming task and hydrate handlers.

Consider extracting the common logic:

+  private handleTaskReject = (task: ITask, reason: string) => {
+    if (this.onTaskRejected) {
+      this.onTaskRejected(reason || 'No reason provided');
+    }
+    this.handleTaskRemove(task.data.interactionId);
+  };

-    task.on(TASK_EVENTS.TASK_REJECT, (reason: string) => {
-      if (this.onTaskRejected) {
-        this.onTaskRejected(reason || 'No reason provided');
-      }
-      this.handleTaskRemove(task.data.interactionId);
-    });
+    task.on(TASK_EVENTS.TASK_REJECT, (reason: string) => 
+      this.handleTaskReject(task, reason)
+    );

Also applies to: 224-230

widgets-samples/cc/samples-cc-react-app/src/App.tsx (2)

84-111: LGTM with a minor suggestion!

The function correctly handles agent state changes with proper error handling and state updates. Consider adding a type for the newState parameter to restrict it to valid values.

Apply this diff to add type safety:

-const changeAgentState = (newState: string) => {
+type AgentState = 'Available' | 'Busy' | 'On Break';
+const changeAgentState = (newState: AgentState) => {

245-268: Consider extracting popup styles and component.

The popup implementation works correctly but could benefit from the following improvements:

  1. Extract inline styles to a CSS file or styled component.
  2. Move the popup to a separate component for better maintainability.
  3. Add keyboard navigation and accessibility attributes.

Example of extracting the popup component:

+const TaskRejectionPopup = ({ reason, selectedState, onStateChange, onSubmit }) => (
+  <div className="task-rejection-popup">
+    <h2>Task Rejected</h2>
+    <p>Reason: {reason}</p>
+    <select 
+      value={selectedState} 
+      onChange={onStateChange}
+      aria-label="Select agent state"
+    >
+      <option value="">Select a state</option>
+      <option value="Available">Available</option>
+      <option value="Busy">Busy</option>
+      <option value="On Break">On Break</option>
+    </select>
+    <button onClick={onSubmit}>Submit</button>
+  </div>
+);
widgets-samples/cc/samples-cc-wc-app/index.html (2)

11-20: Consider enhancing popup styles for better UX.

The current styles provide basic positioning and appearance. Consider adding:

  1. A semi-transparent overlay to dim the background
  2. Transition animations for smooth appearance/disappearance
  3. Mobile-friendly styles with media queries

Example enhancement:

+.popup-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  z-index: 999;
+}
 #task-rejected-popup {
   position: fixed;
   top: 20%;
   left: 50%;
   transform: translate(-50%, 0);
   background: white;
   padding: 20px;
   border: 1px solid #ccc;
   z-index: 1000;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+  transition: opacity 0.2s ease-in-out;
 }
+@media (max-width: 768px) {
+  #task-rejected-popup {
+    width: 90%;
+    top: 10%;
+  }
+}

64-64: Consider moving styles to CSS.

Move the inline style to the CSS block for better maintainability and consistency.

Apply this diff:

-<div id="popup-container" style="display: none;"></div>
+<div id="popup-container"></div>

And add to the CSS block:

#popup-container {
  display: none;
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b13e84b and 8a6f5eb.

📒 Files selected for processing (7)
  • packages/contact-center/store/src/store.types.ts (1 hunks)
  • packages/contact-center/store/src/storeEventsWrapper.ts (3 hunks)
  • packages/contact-center/store/tests/storeEventsWrapper.ts (1 hunks)
  • packages/contact-center/task/src/task.types.ts (1 hunks)
  • widgets-samples/cc/samples-cc-react-app/src/App.tsx (4 hunks)
  • widgets-samples/cc/samples-cc-wc-app/app.js (3 hunks)
  • widgets-samples/cc/samples-cc-wc-app/index.html (2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
widgets-samples/cc/samples-cc-wc-app/app.js

[error] 150-153: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: setup_test_release
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (8)
packages/contact-center/store/src/store.types.ts (1)

55-55: LGTM! Well-designed callback interface.

The optional callback is well-typed and maintains backward compatibility.

packages/contact-center/task/src/task.types.ts (1)

123-123: LGTM! Consistent enum extension.

The new event type follows the established naming pattern and string format.

widgets-samples/cc/samples-cc-wc-app/app.js (1)

31-33: LGTM! Clean callback setup.

The callback registration is straightforward and follows the store's API.

packages/contact-center/store/src/storeEventsWrapper.ts (1)

8-8: LGTM! Well-typed callback property.

The optional callback property is properly typed and matches the interface.

widgets-samples/cc/samples-cc-react-app/src/App.tsx (3)

19-21: LGTM!

The state variables are well-named and appropriately scoped for managing the task rejection popup.


113-119: LGTM!

The function correctly handles the popup submission by updating the agent state and resetting the popup state.


121-130: LGTM!

The effect hook correctly sets up and cleans up the task rejection callback, following React's best practices for side effects.

packages/contact-center/store/tests/storeEventsWrapper.ts (1)

612-633: LGTM!

The test case thoroughly verifies the task rejection flow by:

  1. Setting up the necessary mocks and spies
  2. Verifying the registration of the rejection callback
  3. Testing the execution of the callback with the correct reason
  4. Ensuring proper task cleanup

@adhmenon

Copy link
Copy Markdown
Contributor Author

@coderabbitai resolved all your comments.

@coderabbitai

coderabbitai Bot commented Feb 18, 2025

Copy link
Copy Markdown

@adhmenon Thank you for resolving the comments. I'll review the changes again to ensure everything is in order.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
widgets-samples/cc/samples-cc-wc-app/app.js (1)

21-197: Consider extracting popup logic into a reusable component.

The current implementation mixes DOM manipulation, state management, and UI logic. Consider creating a reusable web component for the popup to:

  1. Encapsulate popup-related DOM manipulation and styling
  2. Make the implementation more consistent with the widget-based architecture
  3. Improve reusability across different scenarios

Would you like me to provide an example implementation of a reusable popup web component?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a6f5eb and 3631627.

📒 Files selected for processing (2)
  • packages/contact-center/store/src/storeEventsWrapper.ts (4 hunks)
  • widgets-samples/cc/samples-cc-wc-app/app.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/contact-center/store/src/storeEventsWrapper.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: setup_test_release
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (2)
widgets-samples/cc/samples-cc-wc-app/app.js (2)

21-21: LGTM!

The popup container reference is correctly placed with other DOM element references.


31-33: LGTM!

The task rejection callback is properly set up to handle RONA events by showing the popup.

Comment thread widgets-samples/cc/samples-cc-wc-app/app.js
Comment thread widgets-samples/cc/samples-cc-wc-app/app.js
Comment thread packages/contact-center/task/src/task.types.ts Outdated
Comment thread widgets-samples/cc/samples-cc-react-app/src/App.tsx Outdated
Comment thread widgets-samples/cc/samples-cc-react-app/src/App.tsx Outdated
Comment thread widgets-samples/cc/samples-cc-react-app/src/App.tsx Outdated
Comment thread widgets-samples/cc/samples-cc-wc-app/app.js Outdated
Comment thread widgets-samples/cc/samples-cc-wc-app/app.js Outdated
Comment thread widgets-samples/cc/samples-cc-wc-app/index.html Outdated
@Shreyas281299

Copy link
Copy Markdown
Contributor

Please do a testing with multiple tab with the same user

  • Have 2 tabs open with the same user logged in as EXT in both of them
  • See if the RONA is MODAL is working as expected, I feel when we get the modal on 1 tab and we set the state on that window, the UI in the second window will not be in sync, the RONA modal will be still set in the second modal.

@adhmenon

adhmenon commented Feb 19, 2025

Copy link
Copy Markdown
Contributor Author

Please do a testing with multiple tab with the same user

* Have 2 tabs open with the same user logged in as EXT in both of them

* See if the RONA is MODAL is working as expected, I feel when we get the modal on 1 tab and we set the state on that window, the UI in the second window will not be in sync, the RONA modal will be still set in the second modal.

Yes, I have tested this scenario and as mentioned in the test plan - the state syncs, however we do see modals in both windows. I think this behaviour is alright (considering it's not widget specific) and that in each tab we essentially are handling the modal (again a sample app issue).
Also, the end user would control the behaviour so I think from widgets side we are good.
Sample app... perhaps this is something we might have to leave... not sure what else can be done otherwise ... since the popup could be opened via web components sample and react sample also.

Comment thread packages/contact-center/task/tests/CallControl/index.tsx

class StoreWrapper implements IStoreWrapper {
store: IStore;
onTaskRejected?: (reason: string) => void;

@Shreyas281299 Shreyas281299 Feb 19, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still wondering if we need this or not? Can you try to remove this and see if something breaks? If it breaks something then we can add this and leave it.

@Shreyas281299 Shreyas281299 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good. Can you just try the above 1 comment

@Shreyas281299

Copy link
Copy Markdown
Contributor

Please do a testing with multiple tab with the same user

* Have 2 tabs open with the same user logged in as EXT in both of them

* See if the RONA is MODAL is working as expected, I feel when we get the modal on 1 tab and we set the state on that window, the UI in the second window will not be in sync, the RONA modal will be still set in the second modal.

Yes, I have tested this scenario and as mentioned in the test plan - the state syncs, however we do see modals in both windows. I think this behaviour is alright (considering it's not widget specific) and that in each tab we essentially are handling the modal (again a sample app issue). Also, the end user would control the behaviour so I think from widgets side we are good. Sample app... perhaps this is something we might have to leave... not sure what else can be done otherwise ... since the popup could be opened via web components sample and react sample also.

Just a question- when we have modal in both the sample pages and we click say change my state to available, do both the modals go away? or only in 1 tab the modal goes away.

@adhmenon

Copy link
Copy Markdown
Contributor Author

Please do a testing with multiple tab with the same user

* Have 2 tabs open with the same user logged in as EXT in both of them

* See if the RONA is MODAL is working as expected, I feel when we get the modal on 1 tab and we set the state on that window, the UI in the second window will not be in sync, the RONA modal will be still set in the second modal.

Yes, I have tested this scenario and as mentioned in the test plan - the state syncs, however we do see modals in both windows. I think this behaviour is alright (considering it's not widget specific) and that in each tab we essentially are handling the modal (again a sample app issue). Also, the end user would control the behaviour so I think from widgets side we are good. Sample app... perhaps this is something we might have to leave... not sure what else can be done otherwise ... since the popup could be opened via web components sample and react sample also.

Just a question- when we have modal in both the sample pages and we click say change my state to available, do both the modals go away? or only in 1 tab the modal goes away.

Only 1 goes away actually.
I could maybe put a check in both sample apps to see listen for state change event and if any modal is open close it... but if a user opens both web and react components it might not work. Let me check.

Comment thread packages/contact-center/store/src/storeEventsWrapper.ts
Comment thread packages/contact-center/store/src/storeEventsWrapper.ts Outdated
Comment thread widgets-samples/cc/samples-cc-react-app/src/App.tsx
@Shreyas281299

Copy link
Copy Markdown
Contributor

@adhmenon I saw that when Im on a call and I dont do anything the UI is stuck, The incoming task and task-list were still. Can you check that scenario please.

@adhmenon
adhmenon requested a review from Kesari3008 February 19, 2025 14:47
@adhmenon

adhmenon commented Feb 19, 2025

Copy link
Copy Markdown
Contributor Author

@adhmenon I saw that when Im on a call and I dont do anything the UI is stuck, The incoming task and task-list were still. Can you check that scenario please.

@Shreyas281299 , checked it in multiple scenarios... I'm not seeing this actually.
Things seem to be working normally on my system... tried on firefox and chrome also.

https://app.vidcast.io/share/538c1c99-512c-4fac-b9cf-9ba0d2b1bc83

@adhmenon
adhmenon merged commit 9c52c53 into webex:ccwidgets Feb 21, 2025
@adhmenon
adhmenon deleted the agent-rona branch February 21, 2025 04:26
@sreenara

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.28.0-ccwidgets.20 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Shreyas281299 pushed a commit to Shreyas281299/widgets that referenced this pull request Feb 21, 2025
# [1.28.0-ccwidgets.20](webex/widgets@v1.28.0-ccwidgets.19...v1.28.0-ccwidgets.20) (2025-02-21)

### Features

* **widgets:** added-agent-rona-popup ([webex#389](webex#389)) ([9c52c53](webex@9c52c53))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants