-
Notifications
You must be signed in to change notification settings - Fork 2
Chore/merge upstream #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This PR fixes the issue with message creation when someone sends messages from the Instagram app instead of the Chatwoot dashboard.
Users who have not changed the order of the sidebar items were not able to change it as the object returned was created using Object.freeze To reproduce: - Create a new user account, open a conversation, try changing the order of the sidebar items.
This PR will handle the Instagram test events. We are using the last created Instagram channel as the test channel since we don't have any other channels for testing purposes at the time of Meta approval. https://github.com/user-attachments/assets/98302b7a-d72c-4950-9660-861a5e08d55f --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
- use stricter validation to restrict gmail signups
) # Pull Request Template ## Description This PR fixes the issue where a clock with animation is shown inside the message bubble for failed and deleted messages. The message status is now hidden for such messages. ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Before **Failed message bubble** <img width="223" alt="image" src="https://github.com/user-attachments/assets/bb4d7a34-4a1c-495a-9a3d-21d065bba020" /> **Deleted message bubble** <img width="223" alt="image" src="https://github.com/user-attachments/assets/ece8e2ff-c6d7-4fa7-b11c-04748bf9ea2d" /> ### After **Failed message bubble** <img width="223" alt="image" src="https://github.com/user-attachments/assets/6a6d81eb-52d9-48c3-bbc1-810b19770d61" /> **Deleted message bubble** <img width="223" alt="image" src="https://github.com/user-attachments/assets/828b553a-c88a-4a9e-9773-d75d76a9d0fd" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
- Added favicon link to portal layout when logo is present - Added tests to verify favicon behavior with and without logo
This pull request includes several changes to better show the report metrics fetching status on the dashboard. This also prevents showing stale data in case fetching summary fails due to timeout or other issue The most important changes include adding a new fetching status state to the store called `summaryFetchingStatus`, updating the `useReportMetrics` composable, and modifying the `ChartStats` component to handle different fetching statuses. #### Loading  #### Finished  #### Failed <img width="1512" alt="image" src="https://github.com/user-attachments/assets/d521a785-9299-4e59-94dc-561a7a84377e" /> --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
…#11304) Add ability to send files as attachments instead of links Fixes: chatwoot#1074 ## Changes - `emaily_reply` : We will attach the small attachments as attachments and large ones as links - `reply_with_summary`, `conversation_transcript`, `reply_with_out_summary` : We will change the attachment format to the following instead of the previous `View the attachment here` ``` Attachments: file_name file_name2 ``` --------- ref: https://github.com/chatwoot/chatwoot/pull/10318/files -> for fixing : chatwoot#9655 (comment) --------- Co-authored-by: Marco Marinho <marcomarinho12@gmail.com> Co-authored-by: Pranav <pranavrajs@gmail.com>
) Converting chatwoot#7802 (comment) to a PR Co-authored-by: Nickson Yap <hi@nickson.me>
# Pull Request Template ## Description This PR resolves a Chrome-specific rendering bug where emojis in the `<textarea>` of the reply box display a shadow or different color tone when placed at the start of the input without a leading space. **Solution:** Removed the border-radius from the textarea in the Reply box component, which resolves the rendering issue in Chrome **Cause:** This appears to be a Chrome rendering bug related to how border-radius is handled on form elements, especially with emojis. The exact cause is unclear, but there is some known issue [Chromium Issue 40333458](https://issues.chromium.org/issues/40333458) [Related Chromium Duplicates](https://issues.chromium.org/issues/40333458/dupes) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Screen recording **Before** https://github.com/user-attachments/assets/618de129-5631-4c7c-ab0b-7188b83c6bf1 **After** https://github.com/user-attachments/assets/bb3adcbe-e603-4792-a8fd-51501d284c78 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
# Pull Request Template ## Description This PR includes updating the PDF text color from Ruby to Slate for improved readability ## How Has This Been Tested? ### Screenshots **Before** | Dark Mode | Light Mode | | ------------- | ------------- | | <img width="258" alt="image" src="https://github.com/user-attachments/assets/6124c69d-8bf1-47f3-9a06-eeb21dbedbb2" /> | <img width="258" alt="image" src="https://github.com/user-attachments/assets/d0f2b90b-6796-45ae-a02a-05c07246d25c" /> | **After** | Dark Mode | Light Mode | | ------------- | ------------- | | <img width="258" alt="image" src="https://github.com/user-attachments/assets/c0285062-83b6-49d5-bdb0-2aebf977cf74" /> | <img width="258" alt="image" src="https://github.com/user-attachments/assets/6b153d45-ee8d-42ac-a863-0fa8b294a21b" /> | ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
#### Before   #### After  
We have added warnings for existing Instagram messenger inboxes via chatwoot#11303. There is an issue with finding the correct Instagram/messenger inbox. This PR will fixes that issue.
…hatwoot#11305) # Pull Request Template ## Description This PR changes to translation to properly handle different content types during translation. ### Changes 1. **Email translation with HTML support** - Properly detects and preserves HTML content from emails - Sets `mime_type` to 'text/html' when HTML content is present 2. **Email translation with plain text support** - Falls back to email text content when HTML is not available - Sets `mime_type` to 'text/plain' when HTML is not available and content type includes 'text/plain' 3. **Plain message with plain text support (Non email channels)** - Sets `mime_type` to 'text/plain' for non-email channels - Fixes an issue where Markdown formatting was being lost due to incorrect `mime_type` **Note**: Translation for very long emails is not currently supported. Fixes https://linear.app/chatwoot/issue/CW-4244/translate-button-doesnt-work-in-email-channels ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? **Loom video** https://www.loom.com/share/8f8428ed2cfe415ea5cb6c547c070f00?sid=eab9fa11-05f8-4838-9181-334bee1023c4 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
…chatwoot#11276) - Add agent bots management UI in settings with avatar upload - Enable agent bot configuration for all inbox types - Implement proper CRUD operations with webhook URL support - Fix agent bots menu item visibility in settings sidebar - Remove all CSML-related code and features - Add migration to convert existing CSML bots to webhook bots - Simplify agent bot model and services to focus on webhook bots - Improve UI to differentiate between system bots and account bots ## Video https://github.com/user-attachments/assets/3f4edbb7-b758-468c-8dd6-a9537b983f7d --------- Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
…woot#11315) When we send text with attachments on Instagram, Instagram treats text and attachments as separate messages. However, Chatwoot keeps them as a single message. Since Instagram sends echo events for each message, this can create duplicate messages in Chatwoot. To prevent this, we will send text and attachments as separate messages. --------- Co-authored-by: Pranav <pranav@chatwoot.com>
…woot#11320) - deprecated report_v4 feature flag - remove the gating logic so new reports is available by default - make search_with_gin an internal feature flag
…oot#11321) - Updates ACCOUNT_LEVEL_FEATURE_DEFAULTS installation config to enable chatwoot_v4 - Enables chatwoot_v4 for all existing accounts in batches of 100 - Clears GlobalConfig cache after update
- Move instagram channel from `internal` to general availability
# Pull Request Template ## Description Please include a summary of the change and issue(s) fixed. Also, mention relevant motivation, context, and any dependencies that this change requires. Fixes # (issue) ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
…ns (chatwoot#11329) We have added warnings for existing Instagram messenger inboxes via chatwoot#11303. However, an issue arose where warnings were incorrectly displaying for messenger conversations. This PR resolves that issue.
## Description Fixed a typo in the `CampaignConversationBuilder` class. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Verified that the typo fix does not affect functionality by running the existing test suite. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings - [x] New and existing unit tests pass locally with my changes Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
) # Pull Request Template ## Description This PR fixes the CC/BCC field reset issue on activity action. Fixes [CW-4256](https://linear.app/chatwoot/issue/CW-4256/emails-added-in-cc-and-bcc-disappears-when-you-click-on-assign-to-me), chatwoot#5234 ### Cause of the Issue Previously, the CC and BCC fields in the reply box were being reset whenever a conversation activity occurred (such as assignment, status change, etc.). This happened because watchers on the current chat messages array and current chat object would trigger `setCCAndToEmailsFromLastChat` even when the last message was an activity or system message, not a real email. ### Solution - The updated logic ensures that the CC and BCC fields are only set under the following conditions: **1**. Switching to a new conversation. **2**. In the same conversation, only if the last message is not an activity (i.e., only for actual emails). - It uses the `lastEmail` computed property, which filters out private and activity messages, ensuring only real email context changes update the fields. - This prevents user-entered `CC/BCC` values from being cleared after system events (e.g., assignments or status changes), while still updating the fields correctly when relevant messages are received. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Loom video **Before** https://www.loom.com/share/2ec50dd1c0ed4eaf9170465274bea41e?sid=cc8b88cb-fd39-473a-8df3-78a242c8407b **After** https://www.loom.com/share/17fd2d96d5d84a049dcbf20d401d2ada?sid=8949ad48-7769-49d2-92c5-267da8c60d6e ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
**Before**   **After**   --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
- Audited message characters across all channels. - Replaced `isAInstagramChannel` with `isAnInstagramChannel`
# Pull Request Template ## Description Please include a summary of the change and issue(s) fixed. Also, mention relevant motivation, context, and any dependencies that this change requires. Fixes # (issue) ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.17 to 5.4.18. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/releases">vite's releases</a>.</em></p> <blockquote> <h2>v5.4.18</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v5.4.18/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/blob/v5.4.18/packages/vite/CHANGELOG.md">vite's changelog</a>.</em></p> <blockquote> <h2><!-- raw HTML omitted -->5.4.18 (2025-04-10)<!-- raw HTML omitted --></h2> <ul> <li>fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/19830">#19830</a>, reject requests with <code>#</code> in request-target (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/19831">#19831</a>) (<a href="https://github.com/vitejs/vite/commit/823675baff2bd6809c74ba2d9acca0327923a54f">823675b</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/19830">#19830</a> <a href="https://redirect.github.com/vitejs/vite/issues/19831">#19831</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/vitejs/vite/commit/731b77d19d36f5682a5441b49cb2f6473389ad99"><code>731b77d</code></a> release: v5.4.18</li> <li><a href="https://github.com/vitejs/vite/commit/823675baff2bd6809c74ba2d9acca0327923a54f"><code>823675b</code></a> fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/19830">#19830</a>, reject requests with <code>#</code> in request-target (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/19831">#19831</a>)</li> <li>See full diff in <a href="https://github.com/vitejs/vite/commits/v5.4.18/packages/vite">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Pull Request Template ## Description This PR resolves the issue where the focus outline for the standard bubble type appears as a square in Safari, while it appears circular in Chrome. Fixes [CW-4252](https://linear.app/chatwoot/issue/CW-4252/v41-circle-around-campaign-pop-up-warped), chatwoot#11327 **Cause** In Chrome, the focus outline for the standard bubble type is circular, but in Safari, it appears square. This is due to a 20px margin on the SVG inside the bubble. **Solution** The `20px` margin was removed from the SVG, fixing the focus outline to appear circular in Safari browser. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Loom Video https://www.loom.com/share/cc4244f369f84b98afaef539b79abcfe?sid=e957df16-1a53-4349-8bdc-705b2ed82d45 ### Screenshots **Before** https://www.loom.com/share/e858417722c64df6801ea87e4b89779f?sid=81a0acec-c5f0-4daa-832c-1f23289e2352 <img width="100" alt="image" src="https://github.com/user-attachments/assets/19c6b62c-96da-485b-9fe8-223065415369" /> **After** https://www.loom.com/share/3946546382884a33a8fef89f81faf7c2?sid=feeaf18c-2b3d-4d4f-bcdf-70335b456dd1 <img width="100" alt="image" src="https://github.com/user-attachments/assets/0e83d9e3-3153-47ef-9f2d-0a2f7270935c" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
…e portal (chatwoot#11349) Portals can have custom domains. When inserting or previewing articles, we consider the frontend URL. This PR fixes article URL generation to properly include the portal's custom domain.
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.18.4 to 1.18.8. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's releases</a>.</em></p> <blockquote> <h2>v1.18.8 / 2025-04-21</h2> <h3>Security</h3> <ul> <li>[CRuby] Vendored libxml2 is updated to <a href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.13.8">v2.13.8</a> to address CVE-2025-32414 and CVE-2025-32415. See <a href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-5w6v-399v-w3cc">GHSA-5w6v-399v-w3cc</a> for more information.</li> </ul> <!-- raw HTML omitted --> <pre><code>36badd2eb281fca6214a5188e24a34399b15d89730639a068d12931e2adc210e nokogiri-1.18.8-aarch64-linux-gnu.gem 664e0f9a77a7122a66d6c03abba7641ca610769a4728db55ee1706a0838b78a2 nokogiri-1.18.8-aarch64-linux-musl.gem 483b5b9fb33653f6f05cbe00d09ea315f268f0e707cfc809aa39b62993008212 nokogiri-1.18.8-arm64-darwin.gem 17de01ca3adf9f8e187883ed73c672344d3dbb3c260f88ffa1008e8dc255a28e nokogiri-1.18.8-arm-linux-gnu.gem 6e6d7e71fc39572bd613a82d528cf54392c3de1ba5ce974f05c832b8187a040b nokogiri-1.18.8-arm-linux-musl.gem 8c7464875d9ca7f71080c24c0db7bcaa3940e8be3c6fc4bcebccf8b9a0016365 nokogiri-1.18.8.gem 41002596960ff854198a20aaeb34cff0d445406d5ad85ba7ca9c3fd0c8f03de0 nokogiri-1.18.8-java.gem 11ab0f76772c5f2d718fb253fca5b74c6ef7628b72bbf8deba6ab1ffc93344cf nokogiri-1.18.8-x64-mingw-ucrt.gem 024cdfe7d9ae3466bba6c06f348fb2a8395d9426b66a3c82f1961b907945cc0c nokogiri-1.18.8-x86_64-darwin.gem 4a747875db873d18a2985ee2c320a6070c4a414ad629da625fbc58d1a20e5ecc nokogiri-1.18.8-x86_64-linux-gnu.gem ddd735fba49475a395b9ea793bb6474e3a3125b89960339604d08a5397de1165 nokogiri-1.18.8-x86_64-linux-musl.gem </code></pre> <h2>v1.18.7 / 2025-03-31</h2> <h3>Dependencies</h3> <ul> <li>[CRuby] Vendored libxml2 is updated to <a href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.13.7">v2.13.7</a>, which is a bugfix release.</li> </ul> <!-- raw HTML omitted --> <pre><code>57a064ab5440814a69a0e040817bd8154adea68a30d2ff2b3aa515a6a06dbb5f nokogiri-1.18.7-aarch64-linux-gnu.gem 3e442dc5b69376e84288295fe37cbb890a21ad816a7e571e5e9967b3c1e30cd3 nokogiri-1.18.7-aarch64-linux-musl.gem 083abb2e9ed2646860f6b481a981485a658c6064caafaa81bf1cda1bada2e9d5 nokogiri-1.18.7-arm64-darwin.gem 337d9149deb5ae01022dff7c90f97bed81715fd586aacab0c5809ef933994c5e nokogiri-1.18.7-arm-linux-gnu.gem 97a26edcc975f780a0822aaf7f7d7427c561067c1c9ee56bd3542960f0c28a6e nokogiri-1.18.7-arm-linux-musl.gem 6b63ff5defe48f30d1d3b3122f65255ca91df2caf5378c6e0482ce73ff46fb31 nokogiri-1.18.7.gem 2cb83666f35619ec59d24d831bf492e49cfe27b112c222330ee929737f42f2eb nokogiri-1.18.7-java.gem 681148fbc918aa5d54933d8b48aeb9462ab708d23409797ed750af961107f72b nokogiri-1.18.7-x64-mingw-ucrt.gem 081d1aa517454ba3415304e2ea51fe411d6a3a809490d0c4aa42799cada417b7 nokogiri-1.18.7-x86_64-darwin.gem 3a0bf946eb2defde13d760f869b61bc8b0c18875afdd3cffa96543cfa3a18005 nokogiri-1.18.7-x86_64-linux-gnu.gem 9d83f8ec1fc37a305fa835d7ee61a4f37899e6ccc6dcb05be6645fa9797605af nokogiri-1.18.7-x86_64-linux-musl.gem </code></pre> <h2>v1.18.6 / 2025-03-24</h2> <h3>Fixed</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's changelog</a>.</em></p> <blockquote> <h2>v1.18.8 / 2025-04-21</h2> <h3>Security</h3> <ul> <li>[CRuby] Vendored libxml2 is updated to <a href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.13.8">v2.13.8</a> to address CVE-2025-32414 and CVE-2025-32415. See <a href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-5w6v-399v-w3cc">GHSA-5w6v-399v-w3cc</a> for more information.</li> </ul> <h2>v1.18.7 / 2025-03-31</h2> <h3>Dependencies</h3> <ul> <li>[CRuby] Vendored libxml2 is updated to <a href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.13.7">v2.13.7</a>, which is a bugfix release.</li> </ul> <h2>v1.18.6 / 2025-03-24</h2> <h3>Fixed</h3> <ul> <li>[JRuby] In HTML documents, <code>Node#attribute</code> now returns the correct attribute. This has been broken, and returning <code>nil</code>, since v1.17.0. (<a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3487">#3487</a>) <a href="https://github.com/flavorjones"><code>@flavorjones</code></a></li> </ul> <h2>v1.18.5 / 2025-03-19</h2> <h3>Fixed</h3> <ul> <li>[JRuby] Update JRuby's XML serialization so it outputs namespaces exactly like CRuby. (<a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3455">#3455</a>, <a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3456">#3456</a>) <a href="https://github.com/johnnyshields"><code>@johnnyshields</code></a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/sparklemotion/nokogiri/commit/9187f4af0dc3cc7ea439bd4957a2fbfab18f6665"><code>9187f4a</code></a> version bump to v1.18.8</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/1deea041e3f359ccef67abadf19f0d634bf473dd"><code>1deea04</code></a> dep: libxml2 to v2.13.8 (branch <code>v1.18.x</code>) (<a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3509">#3509</a>)</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/6457fe639359edda9f8817994bc4935abae3e81e"><code>6457fe6</code></a> dep: libxml2 to v2.13.8</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/13e8aa4ef52f39d1273d2435bd6bfa98982471ef"><code>13e8aa4</code></a> version bump to v1.18.7</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/605699d271ee5d7f58ab29c4550fad42b9cc3d69"><code>605699d</code></a> dep: bump libxml2 to 2.13.7 (v1.18.x backport) (<a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3495">#3495</a>)</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/804e59038c4318ebe7ba0dc08105cff2030df415"><code>804e590</code></a> dep: bump libxml2 to 2.13.7</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/52bf15b62d4e576292e21ac948030783333f9e0b"><code>52bf15b</code></a> dep(dev): drop Rubocop from JRuby deps</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/189769d72627da5f272c7ffcc14cce49d60b9b5a"><code>189769d</code></a> version bump to v1.18.6</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/de4982f0cc39995570f5d15d753577bede5804dc"><code>de4982f</code></a> fix(jruby): Node#attribute in HTML documents (v1.18.x) (<a href="https://redirect.github.com/sparklemotion/nokogiri/issues/3492">#3492</a>)</li> <li><a href="https://github.com/sparklemotion/nokogiri/commit/7d95b0f10cf33c505f11fd7d68d7de8943dda2cd"><code>7d95b0f</code></a> fix(jruby): Node#attribute in HTML documents</li> <li>Additional commits viewable in <a href="https://github.com/sparklemotion/nokogiri/compare/v1.18.4...v1.18.8">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sojan Jose <sojan@pepalo.com>
This upgrade includes a fix for the duplicate `CC` and `TO` emails bug - https://github.com/chatwoot/utils/releases/tag/v0.0.43 - chatwoot/utils#50 Fixes chatwoot#11210 Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
…11357) The pre-commit hook was failing when there were staged deleted files because: - It was using 'ls' to filter files, which fails when they don't exist in the filesystem - Deleted files are still in Git's staging area but not in the filesystem Changes made: 1. Added a check to filter files with 'test -f' before passing to Rubocop 2. Added the same check for staging Rubocop's changes 3. Added '|| true' to prevent errors when no files match the filters This ensures the pre-commit hook completes successfully even when files are staged for deletion.
# Pull Request Template ## Description This PR fixes an issue with slug validation in the Help Center portal settings. Previously, users were able to create or update slugs with invalid characters such as spaces, slashes, and special symbols, which cause help center to crash. With this update, slug creation and updates are now properly validated. Only slugs that match the allowed pattern will be accepted. No spaces, underscores, slashes, or special characters are allowed. Examples: **user**, **user-guide** --- Fixes https://linear.app/chatwoot/issue/CW-4273/add-validation-for-help-centre-slugs ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Loom video https://www.loom.com/share/a2ca5e2104984f28b29539293ffed33a?sid=e5064cb8-6220-4c43-99da-242c25d32027 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
Fixes chatwoot#9272 --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
WalkthroughThis update introduces significant refactoring and feature changes across both backend and frontend components. The main highlights include the complete removal of CSML bot support, corresponding migrations to convert existing CSML bots to webhook bots, and the introduction of a new centralized service for handling conversation messaging window logic. The agent bot management UI is overhauled to use modals instead of navigation-based flows, with enhanced avatar upload and deletion capabilities. Instagram channel support is expanded and refined, including duplicate inbox detection and improved configuration handling. Several new Vue components, composables, and helper functions are added, along with extensive updates to localization, validation, and test coverage. Deprecated methods and legacy feature flag logic are removed, and configuration files are updated to reflect new defaults and enabled features. Changes
Sequence Diagram(s)Conversation Reply Permission Flow (Old vs. New)Old Flow (in Conversation model): sequenceDiagram
participant User
participant Conversation
participant Channel
participant GlobalConfig
User->>Conversation: can_reply?
Conversation->>Channel: check channel type
alt Channel has messaging window
Conversation->>Conversation: last_message_in_messaging_window?
Conversation->>GlobalConfig: check config flags
Conversation-->>User: true/false
else
Conversation-->>User: true
end
New Flow (with MessageWindowService): sequenceDiagram
participant User
participant Conversation
participant MessageWindowService
participant Channel
participant GlobalConfig
User->>Conversation: can_reply?
Conversation->>MessageWindowService: can_reply?
MessageWindowService->>Channel: determine channel type & window
MessageWindowService->>GlobalConfig: check config flags (if needed)
MessageWindowService->>MessageWindowService: evaluate message window logic
MessageWindowService-->>Conversation: true/false
Conversation-->>User: true/false
Agent Bot Creation/Editing with AvatarsequenceDiagram
participant User
participant AgentBotModal
participant VuexStore
participant AgentBotsAPI
User->>AgentBotModal: Open modal, fill form, select avatar
AgentBotModal->>VuexStore: dispatch create/update action with FormData
VuexStore->>AgentBotsAPI: send POST/PATCH request (multipart/form-data)
AgentBotsAPI-->>VuexStore: response (success/error)
VuexStore-->>AgentBotModal: update state, show alert
AgentBotModal-->>User: show result, close modal/reset form
Instagram Duplicate Inbox DetectionsequenceDiagram
participant User
participant UIComponent
participant VuexStore
User->>UIComponent: Load inbox/settings
UIComponent->>VuexStore: getInstagramInboxByInstagramId / getFacebookInboxByInstagramId
VuexStore-->>UIComponent: return matching inbox (if any)
alt Duplicate found
UIComponent-->>User: Show duplicate inbox banner
else
UIComponent-->>User: No banner
end
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 RuboCop (1.73)spec/mailers/conversation_reply_mailer_spec.rbError: app/listeners/agent_bot_listener.rbError: app/builders/messages/instagram/base_message_builder.rbError:
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR merges upstream changes that include enhancements to configuration flags, UI updates, and improved validation and consistency in various components. Key changes include:
- Addition of an Instagram configuration check and removal of deprecated CSML editor flags.
- Updates to translation handling in message bubbles and UI components.
- Improvements to validation error messaging and permitted parameter handling.
Reviewed Changes
Copilot reviewed 129 out of 130 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
app/javascript/dashboard/components/widgets/ChannelItem.vue | Added Instagram configuration check for channel activation. |
app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue | Removed specific global config flag handling for the CSML editor. |
app/javascript/dashboard/components/layout/config/sidebarItems/settings.js | Removed legacy globalConfigFlag configuration. |
app/javascript/dashboard/components-next/sidebar/Sidebar.vue | Removed old report routes and references to deprecated feature flags. |
app/javascript/dashboard/components-next/message/chips/File.vue | Updated styling for PDF file type. |
app/javascript/dashboard/components-next/message/bubbles/Text/Index.vue | Refactored translation toggle rendering and removed redundant computed properties. |
app/javascript/dashboard/components-next/message/bubbles/Email/Index.vue | Updated translation support and ensured unique key handling for re-rendering. |
app/javascript/dashboard/components-next/message/TranslationToggle.vue | Introduced a new translation toggle component. |
app/javascript/dashboard/components-next/message/MessageMeta.vue | Updated Instagram channel variable naming for consistency. |
app/javascript/dashboard/components-next/dialog/Dialog.vue | Added conditional rendering for dialog footer buttons. |
app/javascript/dashboard/components-next/copilot/CopilotAssistantMessage.vue | Improved handling for empty message content. |
app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue | Enhanced slug validation with custom error messages. |
app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue | Adjusted slug validation and error handling similar to CreatePortalDialog.vue. |
app/javascript/dashboard/api/agentBots.js | Added new API helper methods for creating, updating, and deleting agent bot avatars. |
app/controllers/devise_overrides/omniauth_callbacks_controller.rb | Ensured emails are lowercased before validating business accounts. |
app/controllers/dashboard_controller.rb | Updated global configuration to remove outdated flags and include Instagram settings. |
app/controllers/api/v1/accounts/agent_bots_controller.rb | Modified permitted parameters to allow flexible bot configuration. |
app/builders/messages/instagram/base_message_builder.rb | Minor comment update clarifying potential echo event handling. |
app/builders/campaigns/campaign_conversation_builder.rb | Fixed a spelling error in a raised exception message. |
Files not reviewed (1)
- .husky/pre-commit: Language not supported
@@ -33,7 +36,9 @@ export default { | |||
} | |||
|
|||
if (key === 'instagram') { | |||
return this.enabledFeatures.channel_instagram; | |||
return ( | |||
this.enabledFeatures.channel_instagram && this.hasInstagramConfigured |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method 'hasInstagramConfigured' is referenced without being invoked. Please call it as 'this.hasInstagramConfigured()' to ensure the method executes and returns the expected value.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
🔭 Outside diff range comments (1)
app/javascript/dashboard/store/modules/reports.js (1)
133-150
: 🛠️ Refactor suggestionReturn the Promise so callers can await completion / handle failure
fetchAccountSummary
(and its bot counterpart) dispatch asynchronous network calls but do not return the resulting Promise.
Components that call this action and need to chain logic (e.g. showing toast notifications) currently have no reliable hook.- fetchAccountSummary({ commit }, reportObj) { + fetchAccountSummary({ commit }, reportObj) { commit(types.default.SET_ACCOUNT_SUMMARY_STATUS, STATUS.FETCHING); - Report.getSummary( + return Report.getSummary( /* … */ )Apply the same change to
fetchBotSummary
. This is a non-breaking enhancement—existing callers that ignore the result remain unaffected, while new callers gain control.
🧹 Nitpick comments (30)
app/javascript/dashboard/helper/portalHelper.js (2)
6-7
: Consider more robust protocol handling.The
formatCustomDomain
function forces HTTPS, which is good for security but may not work for all scenarios. Consider adding support for domains that might specifically require HTTP or detecting the existing protocol if present.-const formatCustomD C95D omain = customDomain => - customDomain.startsWith('https') ? customDomain : `https://${customDomain}`; +const formatCustomDomain = customDomain => { + if (customDomain.startsWith('http://') || customDomain.startsWith('https://')) { + return customDomain; + } + return `https://${customDomain}`; +};
45-54
: Consider removing unused parameters.The
buildPortalArticleURL
function acceptscategorySlug
andlocale
parameters but doesn't use them. Consider removing these parameters if they're no longer needed or document why they're retained.export const buildPortalArticleURL = ( portalSlug, - categorySlug, - locale, articleSlug, customDomain ) => { const portalURL = buildPortalURL(portalSlug, customDomain); return `${portalURL}/articles/${articleSlug}`; };app/javascript/shared/helpers/Validators.js (1)
103-109
: Well-implemented slug validator.The new
isValidSlug
function is properly documented and correctly validates that a string contains only alphanumeric characters and hyphens, which is appropriate for slug validation.One enhancement to consider for the future might be to add minimum/maximum length validation for slugs, as most systems have length restrictions for URL paths.
app/javascript/dashboard/store/constants.js (1)
1-5
: Good implementation of status constants.Using constants for status values helps prevent typos and provides better code maintainability compared to magic strings. The three states (
FAILED
,FETCHING
,FINISHED
) appropriately cover the lifecycle of asynchronous operations.One suggestion would be to use a more specific name for the
STATUS
object (likeFETCH_STATUS
orASYNC_STATUS
) since "STATUS" is very generic and could potentially conflict with other status enumerations in the future.app/javascript/shared/helpers/MessageTypeHelper.js (1)
5-23
: Well-documented platform message limits.The updated message length limits are well-structured with appropriate documentation links to official sources. The additions of Instagram, WhatsApp Cloud, Bandwidth SMS, Telegram, and LINE platforms align with the PR's expanded channel support.
For consistency, consider adding documentation links to the remaining platforms that don't have them (like TWILIO_SMS on line 12), as you've done for the newer entries.
app/javascript/dashboard/components/widgets/conversation/linear/Issue.vue (1)
59-59
: Approve translucent background and blur styling
The new root container classes (bg-n-alpha-3
,backdrop-blur-[100px]
,border-n-container
) align with the updated design system and maintain readability without altering logic.Consider testing the backdrop-blur performance on lower-end devices and browsers that may have limited CSS filter support.
app/builders/campaigns/campaign_conversation_builder.rb (1)
12-12
: Approve typo correction in error message
Fixing "alread" → "already" improves clarity in logs. No logic change.Optionally, consider using a custom exception class (e.g.,
ConversationAlreadyPresentError
) for more precise error handling upstream.app/javascript/dashboard/composables/useTranslations.js (1)
8-22
: Optimize defensive coding in translation handlingThe composable provides a good abstraction for handling translations, but has a few areas for improvement:
- Remove redundant null check in
hasTranslations
:const hasTranslations = computed(() => { if (!contentAttributes.value) return false; const { translations = {} } = contentAttributes.value; - return Object.keys(translations || {}).length > 0; + return Object.keys(translations).length > 0; });
- Add consistent destructuring in
translationContent
:const translationContent = computed(() => { if (!hasTranslations.value) return null; - const translations = contentAttributes.value.translations; + const { translations = {} } = contentAttributes.value; return translations[Object.keys(translations)[0]]; });
- Consider clarifying in the JSDoc or adding a check for the type:
/** * Composable to extract translation state/content from contentAttributes. - * @param {Ref|Reactive} contentAttributes - Ref or reactive object containing `translations` property + * @param {Ref} contentAttributes - Ref object containing `translations` property * @returns {Object} { hasTranslations, translationContent } */app/javascript/dashboard/components-next/message/TranslationToggle.vue (2)
4-6
: Add default value for theshowingOriginal
propConsider setting a default value for the boolean prop to make the component more robust.
defineProps({ - showingOriginal: Boolean, + showingOriginal: { + type: Boolean, + default: true, + }, });
11-24
: Simplify component HTML structureThe component uses nested
<span>
elements where the outer one doesn't have any purpose.<template> - <span> <span class="text-xs text-n-slate-11 cursor-pointer hover:underline select-none" @click="$emit('toggle')" > {{ showingOriginal ? $t('CONVERSATION.VIEW_TRANSLATED') : $t('CONVERSATION.VIEW_ORIGINAL') }} </span> - </span> </template>app/javascript/dashboard/composables/useReportMetrics.js (1)
4-9
: Update JSDoc to include the new parameter.The JSDoc comment is missing documentation for the newly added
summaryFetchingKey
parameter./** * A composable function for report metrics calculations and display. * * @param {string} [accountSummaryKey='getAccountSummary'] - The key for accessing account summary data. + * @param {string} [summaryFetchingKey='getAccountSummaryFetchingStatus'] - The key for accessing the fetching status. * @returns {Object} An object containing utility functions for report metrics. */
app/javascript/dashboard/api/agentBots.js (1)
1-1
: Use ES module import instead of global axiosConsider importing axios explicitly rather than relying on it being available globally.
-/* global axios */ +import axios from 'axios';db/migrate/20250416182131_flip_chatwoot_v4_default_feature_flag_installation_config.rb (2)
2-23
: Migration for enabling chatwoot_v4 feature lacks a down methodThe migration effectively enables the
chatwoot_v4
feature flag globally and for all existing accounts, with proper batch processing to handle large datasets. However, it's missing adown
method for reversibility.Consider adding a
down
method to allow rolling back this migration if needed:def down # Revert the default feature flag config config = InstallationConfig.find_by(name: 'ACCOUNT_LEVEL_FEATURE_DEFAULTS') if config && config.value.present? features = config.value.map do |f| if f['name'] == 'chatwoot_v4' f.merge('enabled' => false) else f end end config.value = features config.save! end # Disable chatwoot_v4 for all accounts Account.find_in_batches(batch_size: 100) do |accounts| accounts.each { |account| account.disable_features!('chatwoot_v4') } end GlobalConfig.clear_cache end
17-20
: Consider adding progress logging for batch operationsWhen performing operations on potentially large datasets, adding progress logging can be helpful for monitoring the migration.
# Enable chatwoot_v4 for all accounts in batches of 100 +puts "Enabling chatwoot_v4 for all accounts..." +account_count = 0 Account.find_in_batches(batch_size: 100) do |accounts| accounts.each { |account| account.enable_features!('chatwoot_v4') } + account_count += accounts.size + puts "Processed #{account_count} accounts so far..." end +puts "Completed enabling chatwoot_v4 for #{account_count} accounts."app/javascript/dashboard/store/modules/reports.js (1)
12-15
: Consider removing the now-unusedfetchingStatus
flagAfter introducing the more granular
accountSummaryFetchingStatus
andbotSummaryFetchingStatus
, the genericfetchingStatus
boolean appears to be obsolete. Retaining it can confuse future contributors and risks diverging UI behaviour if some components still rely on it.- fetchingStatus: false, - accountSummaryFetchingStatus: STATUS.FINISHED, - botSummaryFetchingStatus: STATUS.FINISHED, + /* Removed deprecated `fetchingStatus`. + Use the explicit `*_SummaryFetchingStatus` keys instead. */ + accountSummaryFetchingStatus: STATUS.FINISHED, + botSummaryFetchingStatus: STATUS.FINISHED,Search the codebase for remaining reads/writes to
state.fetchingStatus
; if none exist, the line (and any related mutation) can be safely deleted.app/services/instagram/test_event_service.rb (3)
21-27
:Channel::Instagram.last
is brittle – pick the correct test inbox explicitlyUsing the last-created channel may point to a random customer inbox on multi-tenant systems.
Options:
- Query by the Instagram App ID contained in the payload (
@messaging[:recipient][:id]
), mapping it to a known channel.- Allow an
INBOX_ID_FOR_IG_TEST_EVENTS
ENV var and fall back tolast
only in development.This prevents accidentally creating messages on the wrong account.
38-41
: Populate contact details with real test dataCreating a contact inbox with the fixed identifiers
'sender_username'
twice (name
andusername
) throws away the real values present in the payload and prevents deduplication if a second test arrives.- @contact_inbox ||= @inbox.channel.create_contact_inbox( - 'sender_username', 'sender_username' - ) + username = @messaging.dig(:sender, :username) || 'ig_test_user' + @contact_inbox ||= @inbox.channel.create_contact_inbox(username, username)Leverage any available
profile_name
orusername
field from the webhook for better realism.
50-58
: Nil-safe access for optional textInstagram test events may not always include a
:text
key (e.g. media-only tests).
Consider defaulting to a placeholder to avoidNoMethodError: undefined method nil for String
.- content: @messaging[:message][:text], + content: @messaging.dig(:message, :text) || '[IG test event]',app/javascript/dashboard/components/widgets/conversation/MessagesView.vue (1)
238-245
: Enhanced reply window message with fallback optionThe code now properly handles API inbox cases by providing a fallback template with the hours parameter when a custom message isn't available. This ensures users always see relevant information about reply time windows.
Consider adding a default value for agentReplyTimeWindow to prevent potential undefined values in the translation.
return ( agentReplyTimeWindowMessage || this.$t('CONVERSATION.API_HOURS_WINDOW', { - hours: agentReplyTimeWindow, + hours: agentReplyTimeWindow || '--', }) );app/javascript/dashboard/store/modules/agentBots.js (1)
54-67
: DRY – extract duplicatedFormData
construction
create
andupdate
repeat the same block. Refactoring into a helper cuts maintenance in half and keeps both paths consistent (e.g., forgetting to append a future field in one place).-// Create FormData for file upload -const formData = new FormData(); -formData.append('name', botData.name); -formData.append('description', botData.description); -formData.append('bot_type', botData.bot_type || 'webhook'); -formData.append('outgoing_url', botData.outgoing_url); - -// Add avatar file if available -if (botData.avatar) { - formData.append('avatar', botData.avatar); -} +const formData = buildAgentBotFormData(botData);Add once at module top (outside
actions
):function buildAgentBotFormData(data) { const fd = new FormData(); ['name', 'description', 'bot_type', 'outgoing_url'].forEach(key => fd.append(key, data[key] ?? '') ); if (data.avatar) fd.append('avatar', data.avatar); return fd; }Also applies to: 82-92
app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue (2)
104-113
: Semantic HTML – wrap table headers in a<tr>
<thead>
should contain a<tr>
element; otherwise the markup is invalid and screen-reader navigation may break.-<thead> - <th v-for="thHeader in tableHeaders" :key="thHeader" class="py-4 font-semibold text-left ltr:pr-4 rtl:pl-4 text-n-slate-11"> - {{ thHeader }} - </th> -</thead> +<thead> + <tr> + <th + v-for="thHeader in tableHeaders" + :key="thHeader" + class="py-4 font-semibold text-left ltr:pr-4 rtl:pl-4 text-n-slate-11" + > + {{ thHeader }} + </th> + <!-- Empty header for action buttons --> + <th class="sr-only" /> + </tr> +</thead>
145-164
: Avoid per-rowloading
object growth
loading
is an object keyed by bot id. When rows are deleted the entry is never removed; over time the object can grow indefinitely. Clean up after deletion:finally { loading.value[id] = false; delete loading.value[id]; // 🆕 selectedBot.value = {}; }app/services/conversations/message_window_service.rb (2)
32-36
: Potential off-by-one on niltime
last_message_in_messaging_window?(time)
is called only whentime.present?
, but inside the method there’s no guard fortime.nil?
.
Either make the guard internal or document the pre-condition.
61-63
: Possible N+1 when memoisinglast_incoming_message
@conversation.messages.incoming.last
hits the DB ifmessages
aren’t eager-loaded.
Consider eager loading in callers (includes(:messages)
) or adding a scopes cache to avoid N+1s when processing a batch of conversations.app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue (3)
45-65
: Missing validation for avatar file size / type
handleImageUpload
accepts any file; oversized or non-image files reach the API and may blow up server-side.
Add a lightweight client check (e.g. ≤2 MB, mime starts withimage/
) before assigning toformState.botAvatar
.
128-165
: Danger of delayed validation disabling the submit button
<NextButton :disabled="v$.$invalid" />
allows submission until after$touch()
is invoked (on blur or submit).
Users can press “Create” with empty fields and only then see errors; the modal closes if API call fails early.
Prefer disabling until first interaction:-:disabled="v$.$invalid" +:disabled="v$.$invalid && v$.$dirty"
233-248
: Accessibility: missingaria-label
on action buttonsAdd
aria-label
(ortitle
) to the Cancel and Submit buttons for screen-reader clarity.app/javascript/dashboard/i18n/locale/en/agentBots.json (2)
62-88
: Duplicate error strings – consolidate to avoid translation driftYou have
"NAME.REQUIRED"
andERRORS.NAME
with identical text. Maintaining both keys risks divergence across locales.Consider keeping only
ERRORS.*
or the flatREQUIRED
keys and refactor component look-ups accordingly.
90-91
: Missing period at end of sentenceMinor punctuation consistency: add a period after the description for polished copy.
app/javascript/dashboard/components-next/message/bubbles/Email/Index.vue (1)
84-90
: Smart approach to forcing component re-rendering.Using a dynamic key suffix based on translation state ensures Vue properly re-renders the
Letter
component when toggling between translated and original content.Consider adding a unit test to verify this dynamic key behavior works correctly across different translation states.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
Gemfile.lock
is excluded by!**/*.lock
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
spec/assets/large_file.pdf
is excluded by!**/*.pdf
📒 Files selected for processing (107)
.husky/pre-commit
(1 hunks)app/builders/campaigns/campaign_conversation_builder.rb
(1 hunks)app/builders/messages/instagram/base_message_builder.rb
(1 hunks)app/controllers/api/v1/accounts/agent_bots_controller.rb
(1 hunks)app/controllers/dashboard_controller.rb
(2 hunks)app/controllers/devise_overrides/omniauth_callbacks_controller.rb
(1 hunks)app/javascript/dashboard/api/agentBots.js
(1 hunks)app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue
(3 hunks)app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue
(5 hunks)app/javascript/dashboard/components-next/copilot/CopilotAssistantMessage.vue
(2 hunks)app/javascript/dashboard/components-next/dialog/Dialog.vue
(1 hunks)app/javascript/dashboard/components-next/message/MessageMeta.vue
(3 hunks)app/javascript/dashboard/components-next/message/TranslationToggle.vue
(1 hunks)app/javascript/dashboard/components-next/message/bubbles/Email/Index.vue
(5 hunks)app/javascript/dashboard/components-next/message/bubbles/Text/Index.vue
(3 hunks)app/javascript/dashboard/components-next/message/chips/File.vue
(1 hunks)app/javascript/dashboard/components-next/sidebar/Sidebar.vue
(1 hunks)app/javascript/dashboard/components/layout/config/sidebarItems/settings.js
(0 hunks)app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
(0 hunks)app/javascript/dashboard/components/widgets/ChannelItem.vue
(2 hunks)app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue
(1 hunks)app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
(5 hunks)app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
(9 hunks)app/javascript/dashboard/components/widgets/conversation/linear/Issue.vue
(3 hunks)app/javascript/dashboard/components/widgets/conversation/linear/index.vue
(2 hunks)app/javascript/dashboard/composables/spec/useTranslations.spec.js
(1 hunks)app/javascript/dashboard/composables/useInbox.js
(2 hunks)app/javascript/dashboard/composables/useReportMetrics.js
(2 hunks)app/javascript/dashboard/composables/useTranslations.js
(1 hunks)app/javascript/dashboard/composables/useUISettings.js
(1 hunks)app/javascript/dashboard/helper/portalHelper.js
(1 hunks)app/javascript/dashboard/helper/specs/portalHelper.spec.js
(1 hunks)app/javascript/dashboard/i18n/locale/en/agentBots.json
(2 hunks)app/javascript/dashboard/i18n/locale/en/conversation.json
(1 hunks)app/javascript/dashboard/i18n/locale/en/helpCenter.json
(1 hunks)app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
(1 hunks)app/javascript/dashboard/i18n/locale/en/integrations.json
(1 hunks)app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue
(4 hunks)app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsArticlesEditPage.vue
(2 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotRow.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotType.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLBotEditor.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLMonacoEditor.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue
(0 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/ChannelList.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
(3 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
(6 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/channels/instagram/DuplicateInboxBanner.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/reports/BotReports.vue
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue
(2 hunks)app/javascript/dashboard/routes/dashboard/settings/reports/components/ChartElements/ChartStats.vue
(3 hunks)app/javascript/dashboard/store/constants.js
(1 hunks)app/javascript/dashboard/store/modules/agentBots.js
(5 hunks)app/javascript/dashboard/store/modules/inboxes.js
(1 hunks)app/javascript/dashboard/store/modules/reports.js
(7 hunks)app/javascript/dashboard/store/modules/specs/agentBots/agentBots.spec.js
(3 hunks)app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js
(1 hunks)app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js
(1 hunks)app/javascript/dashboard/store/modules/specs/inboxes/fixtures.js
(2 hunks)app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js
(3 hunks)app/javascript/dashboard/store/modules/specs/reports/actions.spec.js
(5 hunks)app/javascript/dashboard/store/mutation-types.js
(2 hunks)app/javascript/sdk/IFrameHelper.js
(2 hunks)app/javascript/sdk/bubbleHelpers.js
(2 hunks)app/javascript/sdk/sdk.js
(1 hunks)app/javascript/shared/constants/messages.js
(1 hunks)app/javascript/shared/helpers/MessageTypeHelper.js
(1 hunks)app/javascript/shared/helpers/Validators.js
(1 hunks)app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js
(2 hunks)app/javascript/shared/mixins/inboxMixin.js
(1 hunks)app/javascript/shared/store/globalConfig.js
(0 hunks)app/javascript/widget/constants/sdkEvents.js
(1 hunks)app/jobs/agent_bots/csml_job.rb
(0 hunks)app/jobs/webhooks/instagram_events_job.rb
(2 hunks)app/listeners/agent_bot_listener.rb
(1 hunks)app/mailers/conversation_reply_mailer.rb
(1 hunks)app/mailers/conversation_reply_mailer_helper.rb
(2 hunks)app/models/agent_bot.rb
(2 hunks)app/models/channel/api.rb
(0 hunks)app/models/channel/facebook_page.rb
(0 hunks)app/models/channel/twilio_sms.rb
(0 hunks)app/models/channel/whatsapp.rb
(0 hunks)app/models/concerns/channelable.rb
(0 hunks)app/models/conversation.rb
(1 hunks)app/services/agent_bots/validate_bot_service.rb
(0 hunks)app/services/conversations/message_window_service.rb
(1 hunks)app/services/instagram/test_event_service.rb
(1 hunks)app/views/api/v1/models/_agent_bot.json.jbuilder
(1 hunks)app/views/api/v1/models/_inbox.json.jbuilder
(1 hunks)app/views/layouts/portal.html.erb
(1 hunks)app/views/layouts/vueapp.html.erb
(1 hunks)app/views/mailers/conversation_reply_mailer/conversation_transcript.html.erb
(1 hunks)app/views/mailers/conversation_reply_mailer/email_reply.html.erb
(1 hunks)app/views/mailers/conversation_reply_mailer/reply_with_summary.html.erb
(1 hunks)app/views/mailers/conversation_reply_mailer/reply_without_summary.html.erb
(1 hunks)app/views/widget_tests/index.html.erb
(1 hunks)config/app.yml
(1 hunks)config/features.yml
(3 hunks)db/migrate/20250410061725_convert_csml_bots_to_w 426B ebhook_bots.rb
(1 hunks)db/migrate/20250416182131_flip_chatwoot_v4_default_feature_flag_installation_config.rb
(1 hunks)db/schema.rb
(1 hunks)enterprise/LICENSE
(1 hunks)
⛔ Files not processed due to max files limit (20)
- lib/csml_engine.rb
- lib/integrations/bot_processor_service.rb
- lib/integrations/csml/processor_service.rb
- lib/integrations/google_translate/processor_service.rb
- package.json
- spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb
- spec/controllers/devise/omniauth_callbacks_controller_spec.rb
- spec/controllers/public/api/v1/portals_controller_spec.rb
- spec/factories/instagram/instagram_message_create_event.rb
- spec/jobs/agent_bots/csml_job_spec.rb
- spec/lib/csml_engine_spec.rb
- spec/lib/integrations/csml/processor_service_spec.rb
- spec/listeners/agent_bot_listener_spec.rb
- spec/mailers/conversation_reply_mailer_spec.rb
- spec/models/agent_bot_spec.rb
- spec/models/channel/twilio_sms_spec.rb
- spec/models/conversation_spec.rb
- spec/services/agent_bots/validate_bot_service_spec.rb
- spec/services/conversations/message_window_service_spec.rb
- spec/services/instagram/test_event_service_spec.rb
💤 Files with no reviewable changes (17)
- app/models/channel/facebook_page.rb
- app/javascript/dashboard/components/layout/config/sidebarItems/settings.js
- app/javascript/shared/store/globalConfig.js
- app/models/channel/whatsapp.rb
- app/models/channel/api.rb
- app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
- app/services/agent_bots/validate_bot_service.rb
- app/jobs/agent_bots/csml_job.rb
- app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotType.vue
- app/models/concerns/channelable.rb
- app/models/channel/twilio_sms.rb
- app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js
- app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue
- app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotRow.vue
- app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLMonacoEditor.vue
- app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLBotEditor.vue
- app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue
🧰 Additional context used
🧬 Code Graph Analysis (13)
app/javascript/sdk/IFrameHelper.js (1)
app/javascript/widget/constants/sdkEvents.js (2)
CHATWOOT_POSTBACK
(4-4)CHATWOOT_POSTBACK
(4-4)
app/javascript/dashboard/store/modules/inboxes.js (1)
app/javascript/dashboard/helper/inbox.js (2)
INBOX_TYPES
(1-13)INBOX_TYPES
(1-13)
app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js (1)
app/javascript/shared/helpers/Validators.js (2)
isValidSlug
(109-109)isValidSlug
(109-109)
app/models/conversation.rb (1)
app/services/conversations/message_window_service.rb (1)
can_reply?
(9-13)
app/services/conversations/message_window_service.rb (1)
app/models/conversation.rb (2)
can_reply?
(117-119)last_incoming_message
(129-131)
app/listeners/agent_bot_listener.rb (1)
app/models/agent_bot.rb (1)
webhook_data
(45-51)
app/javascript/sdk/bubbleHelpers.js (2)
app/javascript/sdk/IFrameHelper.js (2)
IFrameHelper
(53-337)IFrameHelper
(53-337)app/javascript/widget/constants/sdkEvents.js (4)
CHATWOOT_OPENED
(6-6)CHATWOOT_OPENED
(6-6)CHATWOOT_CLOSED
(7-7)CHATWOOT_CLOSED
(7-7)
app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js (1)
app/javascript/dashboard/store/modules/inboxes.js (4)
getters
(49-139)getters
(49-139)state
(36-47)state
(36-47)
app/javascript/dashboard/composables/spec/useTranslations.spec.js (1)
app/javascript/dashboard/composables/useTranslations.js (3)
useTranslations
(8-22)hasTranslations
(9-13)translationContent
(15-19)
app/jobs/webhooks/instagram_events_job.rb (1)
app/services/instagram/test_event_service.rb (1)
perform
(6-12)
app/javascript/dashboard/store/modules/reports.js (1)
app/javascript/dashboard/store/constants.js (2)
STATUS
(1-5)STATUS
(1-5)
app/services/instagram/test_event_service.rb (1)
app/jobs/webhooks/instagram_events_job.rb (2)
perform
(8-15)messages
(112-114)
app/javascript/dashboard/helper/specs/portalHelper.spec.js (1)
app/javascript/dashboard/helper/portalHelper.js (3)
window
(15-15)buildPortalArticleURL
(45-54)buildPortalArticleURL
(45-54)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: test
- GitHub Check: test
🔇 Additional comments (157)
enterprise/LICENSE (1)
2-2
: Validate the copyright year extension
Updating the year range to “2017–2025” correctly reflects ongoing development through the current year. Ensure that this aligns with your release schedule and that downstream tooling or legal processes accommodate the new end year.app/controllers/devise_overrides/omniauth_callbacks_controller.rb (1)
56-59
: Good improvement for case-insensitive email validation.Converting the email to lowercase before checking for '@gmail.com' ensures consistent validation regardless of email case formatting. This prevents users from bypassing the business account restriction by using uppercase characters (e.g., 'user@Gmail.com').
app/javascript/dashboard/i18n/locale/en/integrations.json (1)
329-329
: Addition of error message for empty Copilot responses.This new localization string provides a user-friendly error message when the Copilot AI assistant fails to generate a response, improving the user experience by clearly communicating the error state.
app/javascript/dashboard/components-next/copilot/CopilotAssistantMessage.vue (3)
36-36
: Well-implemented empty content check.This computed property correctly checks for empty or undefined message content, providing a clean way to handle this state in the template.
58-65
: Good error handling for empty responses.The conditional rendering shows a helpful error message when content is empty and properly displays the formatted message otherwise. This aligns perfectly with the localization string added in integrations.json.
68-68
: Appropriate UI control hiding.Hiding the "Use" button when there's no content prevents users from attempting to use empty responses, which enhances the overall user experience.
app/javascript/sdk/sdk.js (1)
54-54
: Good addition for containing content within the bubble boundariesAdding
overflow: hidden
ensures that any content within the chat bubble stays contained within its borders, preventing visual spillover and maintaining a clean UI appearance.app/views/widget_tests/index.html.erb (1)
69-75
: Good addition of event listeners for widget state changesThese new event listeners for
chatwoot:opened
andchatwoot:closed
help with testing and debugging the widget's open/close state transitions. They align with the newly introduced SDK events and follow the same pattern as existing event listeners.app/javascript/sdk/IFrameHelper.js (2)
25-29
: Good practice using imported constantsExpanding the import to include
CHATWOOT_POSTBACK
alongside other SDK event constants follows good practices for maintainability and consistency.
212-213
: Improved consistency using constants for event namesReplacing the hardcoded event string with the imported
CHATWOOT_POSTBACK
constant reduces the risk of typos and ensures consistency throughout the codebase.app/javascript/widget/constants/sdkEvents.js (1)
4-7
: Good addition of SDK event constantsAdding these constants for postback, open, and close events standardizes event naming across the SDK and makes future maintenance easier. These are properly utilized in the bubble helpers and event handlers elsewhere in the codebase.
app/javascript/sdk/bubbleHelpers.js (3)
4-8
: Good addition of imports for new event handlingAdding imports for the SDK event constants and the dispatch helper function properly supports the new event dispatching functionality.
73-82
: Well-designed helper function for bubble toggle side effectsThe new
handleBubbleToggle
function effectively extracts side effects from the main click handler, improving code organization. The function properly dispatches appropriate window events and improves accessibility by setting focus back to the chat bubble when closing.
84-97
: Good refactoring of the bubble click handlerThe refactored
onBubbleClick
function now has:
- An early return to prevent unnecessary processing when the toggle state matches current state
- Clearer state management with explicit new state computation
- Better organization by delegating side effects to the helper function
This improves code readability, maintainability, and performance.
app/views/layouts/portal.html.erb (1)
42-45
: Well-implemented favicon integration for portal brandingThe addition of a favicon using the portal's logo when available is a good enhancement for portal branding. The implementation correctly checks for logo presence and uses Rails'
url_for
helper to generate the appropriate URL.app/javascript/dashboard/helper/specs/portalHelper.spec.js (1)
29-69
: Good test coverage for custom domain handlingThe added tests thoroughly cover the new custom domain functionality in portal article URLs:
- Using a custom domain for URL generation
- Handling domains with explicit HTTPS prefix
- Proper fallback to hostURL when helpCenterURL is not available
These tests will ensure the URL generation remains consistent as the codebase evolves.
app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsArticlesEditPage.vue (3)
23-25
: Good addition of portal data retrievalAdding the portal getter and computed property is a clean way to access portal-specific data needed for URL generation.
30-41
: Well-implemented article URL generation with custom domain supportThe renaming from
portalLink
toarticleLink
improves clarity, and the implementation correctly retrieves and passes the portal's custom domain to the URL builder function.
100-100
: Updated preview to use the correct article linkThis change ensures the preview opens with the correct URL including custom domain if configured.
app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue (2)
37-45
: Good implementation of custom domain support.The addition of Vuex getters to retrieve the portal and its custom domain is well implemented. The use of optional chaining (
?.
) in accessing the custom domain is a good defensive programming practice to prevent errors when the portal data might not be fully loaded.
79-81
: Well-integrated custom domai F438 n support.The article URL generation now correctly passes the portal's custom domain to the
buildPortalArticleURL
helper, ensuring article URLs are generated with the appropriate domain when available.app/mailers/conversation_reply_mailer.rb (2)
2-4
: Well-documented addition of attachment handlingThe comments clearly explain the purpose of separating large and small attachments in email replies, which helps maintain clear code understanding for other developers.
4-4
: Good implementation of large attachments exposureAdding
attr_reader :large_attachments
properly exposes this collection to the mailer views, supporting the coordinated changes across templates to improve attachment rendering.app/views/mailers/conversation_reply_mailer/reply_without_summary.html.erb (2)
6-7
: Improved attachment presence check and clarityThe change from a simple truthiness check to
.present?
is more explicit and the addition of the "Attachments:" label improves readability for email recipients.
9-9
: Enhanced attachment display with proper filename and target attributeThe updated implementation correctly:
- Shows the actual filename instead of generic "attachment" text
- Uses the standard
target="_blank"
attribute (fixing the non-standard_target="blank"
)This improves usability and accessibility for email recipients.
app/views/mailers/conversation_reply_mailer/conversation_transcript.html.erb (2)
35-36
: Improved attachment presence check and clarityThe change from a simple truthiness check to
.present?
is more explicit and the addition of the "Attachments:" label improves readability for email recipients.
38-38
: Enhanced attachment display with proper filename and target attributeThe updated implementation correctly:
- Shows the actual filename instead of generic "attachment" text
- Uses the standard
target="_blank"
attribute (fixing the non-standard_target="blank"
)This improves usability and accessibility for email recipients.
app/views/mailers/conversation_reply_mailer/reply_with_summary.html.erb (1)
23-26
: Improved attachment presentation with better structureThe changes implement a more readable and user-friendly attachment display:
- Clear "Attachments:" label
- Each attachment on a separate line when multiple attachments exist
- Filename shown instead of generic "attachment" text
- Standard
target="_blank"
attribute used correctlyThis significantly improves the user experience when viewing email replies with attachments.
app/views/mailers/conversation_reply_mailer/email_reply.html.erb (1)
4-8
: Improved attachment rendering with better accessibility and usability.The changes correctly implement a more user-friendly way to display attachments in email replies by:
- Adding a clear "Attachments:" label
- Using actual filenames instead of generic text
- Fixing the link attribute to correctly use
target="_blank"
(previously used incorrect_target="blank"
)This aligns with the coordinated changes in the mailer helper that now separates small and large attachments.
app/mailers/conversation_reply_mailer_helper.rb (2)
20-26
: Clear and helpful documentation for email type detection.The added comments clearly explain the logic for differentiating between email types, making the code more maintainable.
88-90
: Good addition of SMTP timeout settings.Adding timeout settings for SMTP connections is a great practice to prevent connection hangs that could potentially block the application. The 15-second value is reasonable for most email sending operations.
config/app.yml (1)
2-2
: Version number updated appropriately.The version has been incremented from 4.0.4 to 4.1.0, which correctly follows semantic versioning principles for the feature additions described in the PR summary (Instagram channel support and agent bot functionality).
app/javascript/dashboard/components/widgets/conversation/linear/Issue.vue (5)
74-74
: Verify Tailwind line-clamp support
Theline-clamp-3
utility requires the Tailwind Line Clamp plugin or typography extension. Ensure it's enabled in the build config to prevent missing styles in production.
81-81
: Approve consistent divider color
Switching the vertical divider tobg-n-slate-4
matches the new slate theme. This is purely a styling change with no logic impact.
88-88
: Approve text color update for state name
Updating the state name’s text color totext-n-slate-12
aligns with the new slate palette. Functionality remains unchanged.
92-92
: Approve divider before priority label
The addedbg-n-slate-4
divider before the priority block is consistent with the slate theme update.
114-114
: Approve timestamp color adjustment
Applyingtext-n-slate-11
to the created-at timestamp matches the updated text hierarchy. No functional changes introduced.db/schema.rb (1)
13-13
: Skip review for auto-generated schema change
The schema version bump is the only change; the file is auto-generated.app/views/layouts/vueapp.html.erb (1)
38-38
: Approve addition of Instagram App ID config
AddinginstagramAppId
towindow.chatwootConfig
aligns with the new Instagram channel support. No control flow or security concerns introduced.app/javascript/dashboard/components-next/dialog/Dialog.vue (1)
128-131
: Good improvement: Empty footer container now conditionally renderedThe dialog footer container is now only rendered when at least one button is visible. This prevents displaying an empty footer container when both buttons are hidden, creating a cleaner UI.
app/javascript/dashboard/routes/dashboard/settings/inbox/ChannelList.vue (1)
38-38
: Instagram channel re-added to active channelsRe-enabling Instagram as an active channel option aligns with the broader Instagram support enhancements across the application.
app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue (1)
205-205
: Grammatical fix for Instagram channel namingThe variable has been renamed from
isAInstagramChannel
toisAnInstagramChannel
for grammatical correctness, ensuring naming consistency across components.app/javascript/dashboard/routes/dashboard/settings/reports/BotReports.vue (1)
101-101
: Enhanced loading state tracking for bot summary reportsAdding the
summary-fetching-key
prop enables the component to properly display loading states when fetching bot summary data, improving user experience by providing visual feedback during data loading.app/builders/messages/instagram/base_message_builder.rb (1)
97-97
: Added comment clarifies echo event handling behavior.The additional comment properly documents the possibility of receiving echo events for the same message, which provides context for why duplicate message checks are important. Based on the AI summary, it appears an unconditional early return for outgoing echo messages was removed (though not visible in the diff), allowing echo events to be processed through normal checks.
app/views/api/v1/models/_inbox.json.jbuilder (1)
59-59
: Instagram ID added to inbox JSON payload.Including the
instagram_id
in the JSON response is a good enhancement that enables frontend components to identify and manage Instagram inboxes. This field will be especially useful for detecting duplicate Instagram inboxes by matching Instagram IDs.app/javascript/shared/mixins/inboxMixin.js (1)
124-126
: Grammatical improvement in method naming.Renaming from
isAInstagramChannel
toisAnInstagramChannel
is a good grammatical correction while maintaining the same functionality. This change appears to be part of a consistent renaming effort across the codebase.app/javascript/shared/constants/messages.js (1)
47-47
: XML file types added to supported upload formats.Adding
application/xml
andtext/xml
MIME types toALLOWED_FILE_TYPES
enhances the application's file handling capabilities by explicitly supporting XML document uploads. This is a good extension of the supported file types.app/javascript/dashboard/composables/useUISettings.js (1)
39-39
: Good improvement to prevent frozen array mutation issuesCreating a shallow copy of the frozen default array ensures that consumers of this function always receive a mutable array, preventing potential errors if the array needs to be modified. This aligns with the existing pattern on line 42 where you're already creating a copy of the user's custom order.
app/views/api/v1/models/_agent_bot.json.jbuilder (2)
4-6
: Improved API response structure for agent botsThe addition of the
thumbnail
field and conditionally includingoutgoing_url
only for non-system bots streamlines the API response. This supports the new avatar functionality while optimizing the payload.
10-10
: Good addition of explicit system_bot flagAdding the
system_bot
field to the API response makes it clearer for frontend code to distinguish system bots without having to infer it from other properties.app/javascript/dashboard/routes/dashboard/settings/inbox/channels/instagram/DuplicateInboxBanner.vue (1)
1-21
: Well-structured reusable banner componentThis component follows Vue best practices with proper prop definitions and clean template structure. The amber color and info icon appropriately indicate an informational notice, while the flexible content prop allows for different messages wherever this component is used in the Instagram channel workflow.
app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js (1)
54-65
: Good test coverage for the new avatar update mutationThe test follows the established pattern in this file and properly verifies that the agent bot's thumbnail property is updated when the mutation is called. This ensures the new avatar functionality works correctly in the Vuex store.
app/javascript/dashboard/composables/useInbox.js (2)
124-124
: Improved naming convention for better grammarThe renaming from
isAInstagramChannel
toisAnInstagramChannel
is a good change as it follows proper English grammar rules - using "an" before words that start with a vowel sound.
144-144
: LGTM: Updated export to match the renamed propertyThe export has been correctly updated to match the renamed computed property.
app/controllers/dashboard_controller.rb (2)
39-39
: Removed CSML_EDITOR_HOST configuration keyThis change aligns with the broader removal of CSML agent bot support throughout the application as mentioned in the PR summary.
68-68
: Added Instagram app ID configurationGood addition of the Instagram app ID configuration to support enhanced Instagram channel features. This change properly integrates with the GlobalConfigService pattern used for other similar configurations.
app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue (2)
22-25
: Added new prop for tracking summary fetching statusAdded a well-defined prop with proper typing and default value to track the fetching status of report summaries, improving the component's flexibility.
155-159
: Passed the new prop to ChartStats componentThe new summaryFetchingKey prop is correctly passed to the ChartStats child component, enabling consistent loading state tracking.
app/javascript/dashboard/store/modules/specs/inboxes/fixtures.js (2)
12-12
: Added Instagram ID to Facebook inbox fixtureAdded an instagram_id property to the Facebook Page channel, establishing a relationship between Facebook and Instagram channels. This supports the duplicate Instagram inbox detection functionality.
66-73
: Added Instagram channel test fixtureAdded a new Instagram channel fixture with the same instagram_id as the Facebook Page channel, which is essential for testing duplicate inbox detection functionality.
db/migrate/20250410061725_convert_csml_bots_to_webhook_bots.rb (1)
1-13
: Migration looks good for converting CSML bots to webhook botsThis migration correctly identifies all CSML bots (bot_type: 1) and converts them to webhook bots (bot_type: 0) while clearing their configuration. The down method appropriately raises an irreversible migration exception since CSML support is being removed.
app/javascript/dashboard/components/widgets/ChannelItem.vue (2)
20-22
: Properly handles Instagram configuration checkThe
hasInstagramConfigured
computed property correctly checks for the Instagram App ID in the global configuration, similar to the existing Facebook check.
38-41
: Instagram channel activation logic looks goodThe updated Instagram channel activation logic now properly checks both the feature flag and the Instagram App ID configuration, consistent with how the Facebook channel is handled.
app/javascript/dashboard/i18n/locale/en/helpCenter.json (1)
699-700
: Good addition of specific format error message for slugsAdding a specific format error message for slugs will improve the user experience by providing clearer guidance on valid slug formats.
app/models/agent_bot.rb (2)
28-28
: CSML bot type removed as expectedThe
bot_type
enum now only includes the webhook type (0), successfully removing CSML support as intended.
53-55
: Good addition of system_bot? helper methodThe
system_bot?
method provides a convenient way to identify system-level bots (those without an associated account). This will be useful for conditional logic in views and controllers.app/javascript/dashboard/components/widgets/conversation/linear/index.vue (3)
103-106
: Improved layout handling with pseudo-element spacer.The addition of the pseudo-element with transparent background creates better hover behavior for the component group, improving the user experience when interacting with Linear issues.
113-113
: Enhanced hover state styling.Adding the
group-hover:bg-n-alpha-2
class improves visual feedback by changing the button's background color when hovering over the group, creating a more cohesive interaction pattern.
130-131
: Adjusted positioning of the Issue component.The top offset adjustment from 40px to 36px provides better alignment with the button element, creating a more polished appearance when the issue details appear on hover.
app/controllers/api/v1/accounts/agent_bots_controller.rb (1)
40-40
:✅ Verification successful
Broadened permitted parameters for bot_config.
This change aligns with the removal of CSML agent bots, allowing any keys within the
bot_config
hash instead of restricting to:csml_content
. This provides more flexibility for webhook bot configurations.
🏁 Script executed:
#!/bin/bash # Verify that bot_config is now used for webhook bots instead of csml_content # Check for any remaining CSML references in the codebase echo "Checking for remaining CSML references in Ruby files:" rg -i "csml" --type ruby # Check for current usages of bot_config echo "Checking for current usage of bot_config:" rg "bot_config" --type rubyLength of output: 1167
Approve broadened bot_config parameters
No remaining CSML usages in the controller—only in the one‐time migration. Allowing any keys in
bot_config
correctly replaces the former:csml_content
restriction.• Confirmed CSML appears only in
db/migrate/20250410061725_convert_csml_bots_to_webhook_bots.rb
.
•app/controllers/api/v1/accounts/agent_bots_controller.rb
now permits allbot_config
keys as intended.app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue (3)
10-11
: Added necessary imports for enhanced slug validation.The additional imports properly set up the validation infrastructure for improved slug validation.
64-73
: Improved slug validation with helpful error messages.The slug validation now includes both a required check and format validation using
isValidSlug
, each with clear, localized error messages. This provides better user feedback and prevents invalid slug formats.
83-85
:✅ Verification successful
Enhanced error message handling for slug errors.
Updated the
slugError
computed property to return the first validation error message from the errors array, providing a more precise and user-friendly error display.
🏁 Script executed:
#!/bin/bash # Verify that isValidSlug validator exists and check its implementation # Look for the isValidSlug validator implementation echo "Looking for isValidSlug validator implementation:" rg "isValidSlug" --type js # Check if the validator is tested echo "Checking for validator tests:" rg "isValidSlug" --type js | grep -i specLength of output: 4626
Approved: Enhanced slug error message handling verified.
The
slugError
computed property now correctly returns the first validation error message:const slugError = computed(() => { return v$.value.slug.$errors[0]?.$message || ''; });The
isValidSlug
validator is implemented inapp/javascript/shared/helpers/Validators.js
and fully covered byValidatorsHelper.spec.js
, so no further action is needed.app/javascript/dashboard/store/mutation-types.js (2)
192-193
: Added new mutation types for report summary status tracking.These mutation types (
SET_BOT_SUMMARY_STATUS
andSET_ACCOUNT_SUMMARY_STATUS
) enable more granular tracking of fetching statuses for bot and account summaries in the reports module, which will improve UI feedback during data loading.
304-304
:✅ Verification successful
Added mutation type for agent bot avatar updates.
The
UPDATE_AGENT_BOT_AVATAR
mutation type supports the new functionality for updating agent bot avatars, enabling reactive UI updates when avatar changes occur.
🏁 Script executed:
#!/bin/bash # Verify the implementation of the new mutation types # Check where the new mutations are used echo "Checking implementation of SET_BOT_SUMMARY_STATUS and SET_ACCOUNT_SUMMARY_STATUS:" rg "SET_BOT_SUMMARY_STATUS|SET_ACCOUNT_SUMMARY_STATUS" --type js echo "Checking implementation of UPDATE_AGENT_BOT_AVATAR:" rg "UPDATE_AGENT_BOT_AVATAR" --type js # Look for related agent bot avatar functionality echo "Looking for agent bot avatar handling functions:" rg "agent[^\w]*bot[^\w]*avatar" -i --type jsLength of output: 3902
✅ Confirmed:
UPDATE_AGENT_BOT_AVATAR
Mutation Wired Correctly
- Mutation type declared in
app/javascript/dashboard/store/mutation-types.js
.- Used in
app/javascript/dashboard/store/modules/agentBots.js
:
- Action commits
types.UPDATE_AGENT_BOT_AVATAR
- Handler
[types.UPDATE_AGENT_BOT_AVATAR]($state, { id, thumbnail })
updates state.- Covered by unit tests in
app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js
.All usages and tests are in place—no further changes required.
app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue (3)
35-44
: Enhanced validation for the slug fieldThe validation for the slug field has been improved by adding the
isValidSlug
validator alongside therequired
validator, both with localized error messages. This ensures slugs follow the correct format (letters, numbers, and hyphens only).
53-55
: Improved error message handlingThe
slugError
computed property now returns the first validation error message directly from the$errors
array, allowing for more specific error messages to be displayed to users.
154-155
: Added validation touch triggersThe slug input field now has both
@input
and@blur
event handlers to trigger validation, providing more immediate feedback to users as they type and when they leave the field.app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js (1)
158-177
: Comprehensive test coverage for isValidSlug validatorThe tests for the
isValidSlug
validator are well-structured and thorough, covering both positive cases (valid slugs with letters, numbers, and hyphens) and negative cases (slugs with invalid characters, spaces, etc.). This ensures the validation logic works correctly across the application.app/javascript/dashboard/composables/spec/useTranslations.spec.js (1)
1-39
: Well-structured tests for the useTranslations composableThese tests thoroughly verify the behavior of the
useTranslations
composable, covering all key scenarios:
- When contentAttributes is null
- When translations are missing
- When translations is an empty object
- When translations exist
The tests ensure both
hasTranslations
andtranslationContent
computed properties return the expected values in each case.app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js (3)
29-29
: Updated count for dialogFlowEnabledInboxesThe expected count for dialogFlowEnabledInboxes has been updated from 6 to 7, reflecting the addition of a new inbox (likely the Instagram inbox).
46-46
: Added instagram_id property to Facebook inbox testThe test now includes the
instagram_id
property in the expected result for Facebook inboxes, which is necessary for the new Instagram-related functionality.
69-95
: Added tests for Instagram-related gettersThese new tests verify the behavior of two important getters:
getFacebookInboxByInstagramId
: Finds a Facebook inbox by Instagram IDgetInstagramInboxByInstagramId
: Finds an Instagram inbox by Instagram IDThese tests are essential for ensuring the application correctly handles Instagram channel integration and duplicate inbox detection.
app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue (1)
93-96
: LGTM! Banner implementation for duplicate Instagram inboxesThe conditional rendering of the DuplicateInboxBanner component with appropriate localized message looks good.
.husky/pre-commit (2)
7-8
: LGTM! Robust handling of staged filesThe updated pre-commit hook now correctly checks for file existence before running RuboCop, preventing errors from deleted files.
Minor optimization suggestion: Consider filtering for Ruby files first, then checking existence to reduce the number of file checks:
# lint only staged ruby files that still exist (not deleted) git diff --name-only --cached | grep '\.rb$' | xargs -I {} sh -c 'test -f "{}" && bundle exec rubocop --force-exclusion -a "{}"' || true
11-11
: LGTM! Consistent file existence checkGood improvement to check file existence before staging RuboCop changes.
app/javascript/dashboard/composables/useReportMetrics.js (2)
15-15
: LGTM!The implementation correctly uses
useMapGetter
to get the fetching status.
56-61
: LGTM!The
fetchingStatus
is correctly exported in the returned object.app/javascript/dashboard/store/modules/inboxes.js (1)
125-138
: LGTM! Good addition for Instagram inbox management.These getters are well-implemented and will help in detecting duplicate Instagram inboxes. The implementation correctly matches inbox records by Instagram ID and specific channel types.
#!/bin/bash # Looking for places where these getters are used in the codebase rg "getFacebookInboxByInstagramId|getInstagramInboxByInstagramId" --type jsconfig/features.yml (4)
38-41
: LGTM! Enabling agent bots feature.The agent bots feature is now enabled by default, which aligns with the broader changes removing CSML bot support and adopting webhook-based agent bots.
150-154
: LGTM! Enabling and deprecating Report V4 feature.The report_v4 feature is now enabled and marked as deprecated, indicating it's now the default reporting system and the feature flag is no longer needed for toggling.
162-166
: LGTM! Adding internal flag to search with GIN feature.Setting the search_with_gin feature as chatwoot_internal correctly indicates this feature is internal to Chatwoot and shouldn't be shown in self-hosted installations.
166-169
: LGTM! Enabling Instagram Channel feature.The Instagram Channel feature is now enabled by default, which aligns with the improvements to Instagram inbox handling and duplicate inbox detection throughout the application.
app/javascript/dashboard/components-next/message/MessageMeta.vue (4)
22-22
: Good fix for naming consistencyThe import from
useInbox
was correctly updated fromisAInstagramChannel
toisAnInstagramChannel
for better grammatical consistency, aligning with other channel variables likeisAnEmailChannel
.
25-32
: Well-structured destructuring with contentAttributes additionGood addition of
contentAttributes
to the destructured properties fromuseMessageContext()
. This enables checking for deleted messages in the status indicator logic.
38-44
: Improved status indicator logicGood enhancement to the
showStatusIndicator
computed property to hide status indicators for failed and deleted messages. This provides a cleaner UX by not showing confusing status indicators for messages in these states.
63-63
: Consistent naming for Instagram channel checksCorrectly updated references from
isAInstagramChannel
toisAnInstagramChannel
in the computed properties for grammatical consistency across the codebase.Also applies to: 103-103
app/javascript/dashboard/api/agentBots.js (3)
9-13
: Good implementation of multipart form data uploadThe implementation correctly uses
multipart/form-data
content type for the create method, which is necessary for avatar file uploads.
15-19
: Good implementation of agent bot update with avatar supportThe update method correctly uses
multipart/form-data
to support avatar updates along with other agent bot data.
21-23
: Clear and focused avatar deletion methodGood addition of a dedicated method for deleting an agent bot's avatar, following RESTful principles with the appropriate endpoint.
app/javascript/dashboard/components-next/message/bubbles/Text/Index.vue (4)
6-6
: Good component and composable importsClean addition of the
TranslationToggle
component anduseTranslations
composable, which helps with code organization and reusability.Also applies to: 9-9
14-15
: Improved translation handling with composableGood refactoring to use the
useTranslations
composable, which extracts the translation-related logic into a reusable piece, improving maintainability.
25-25
: Clean reference to translation contentGood update to use the reactive
translationContent.value
from the composable instead of directly accessing content attributes.
51-56
: Good component extraction for translation toggleExcellent refactoring to use the dedicated
TranslationToggle
component instead of inline toggle implementation. This improves code reusability and maintainability by centralizing the toggle UI and event handling.app/javascript/dashboard/components-next/sidebar/Sidebar.vue (1)
106-106
: Simplified reportRoutes by removing feature flag logic.The code has been simplified to always return the new report routes, removing previous conditional logic based on feature flags. This aligns with the broader changes where the
report_v4
feature flag has been globally enabled and marked as deprecated.app/jobs/webhooks/instagram_events_job.rb (4)
27-30
: Enhanced entry processing to detect Instagram webhook test events.Good addition - this change properly identifies test events early in the processing flow and handles them separately, returning after processing to avoid executing regular message handling logic on test payloads.
54-56
: Clean implementation of test event detection.The method provides a clear way to identify test events by checking for the presence of the
changes
key, which is specific to Instagram test webhook payloads as documented in the comments.
58-62
: Well-structured test event processing.The implementation correctly extracts messaging data from test events and passes it to the dedicated
Instagram::TestEventService
, ensuring proper isolation between test and production events.
64-66
: Proper extraction of messaging data from test events.The method correctly navigates the nested structure to extract the messaging data from test events, with appropriate null safety using
&.dig
and a presence check.app/listeners/agent_bot_listener.rb (1)
62-66
: Simplified to support only webhook bots.The method has been streamlined to process only webhook bot events, removing previous CSML bot support. This aligns with the broader removal of CSML bot functionality throughout the application. The parameter renaming from
event
to_event
properly follows Ruby conventions for unused parameters.app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js (3)
3-13
: Updated agent bot record structure.The fixture has been updated to include new properties and rename
type
tobot_type
. The bot type has been changed from 'csml' to 'webhook' to align with the removal of CSML bot support.
16-26
: Updated second agent bot record consistently.The changes to the second record match those of the first, maintaining consistency in the test data and reflecting the updated agent bot model structure.
29-35
: Added new fixture for bot creation/update with avatar support.This new fixture provides test data specifically for agent bot creation and update scenarios, including avatar file upload support. Good addition that will make testing these operations more straightforward.
app/javascript/dashboard/store/modules/reports.js (1)
80-85
: Minor: keep getter names symmetricalYou added
getAccountSummaryFetchingStatus
andgetBotSummaryFetchingStatus
.
For consistency with existing getters (e.g.getAccountSummary
vsgetBotSummary
) consider renaming the second togetBotSummaryFetchingStatus
, which you already did—good catch! No action required.app/models/conversation.rb (1)
117-119
: Delegation looks good – verify autoloading & memoisationReplacing inline logic with
Conversations::MessageWindowService
greatly improves separation of concerns. Ensure:
- The service is placed in
app/services/conversations/message_window_service.rb
(matching Rails autoload paths).- The service performs no heavy DB work per call; if it does, consider memoising the result in the instance to avoid N+1 queries when
can_reply?
is called repeatedly in views/serialisers.Otherwise, 👍.
app/javascript/dashboard/store/modules/specs/agentBots/agentBots.spec.js (4)
4-4
: Update to import structure for better testingThe addition of
agentBotData
import alongsideagentBotRecords
reflects the updated API structure and provides a clearer separation between the test data used for form submission and the expected response data.
33-34
: Test structure improved for FormData handlingThe test now properly validates that:
- The action is invoked with the correct data structure
- The HTTP request includes FormData payload for file upload support
- Only one API call is made
This better represents the actual implementation that will handle avatar uploads through multipart form data.
Also applies to: 41-43
59-65
: Enhanced test for update action with granular data structureThe update action test now uses a more explicit structure with separate
id
anddata
fields, making the payload shape clearer. The additional FormData verification ensures proper handling of file uploads in update operations.Also applies to: 73-75
48-48
: Error test case updated for consistencyThe error test case now uses an empty object that matches the expected signature of the action, ensuring consistent error handling behavior is tested.
app/javascript/dashboard/i18n/locale/en/inboxMgmt.json (1)
54-56
: Added Instagram migration notification messagesThese messages provide clear information to users about:
- The Instagram account migration between inboxes
- The limitations on the old inbox after migration
This enhances user experience by giving explicit guidance when Instagram accounts are migrated to the new channel structure.
app/javascript/dashboard/i18n/locale/en/conversation.json (2)
35-35
: Added parameterized message for API reply time windowThis translation string with the
{hours}
parameter allows for dynamic display of time windows based on inbox configuration, providing clearer context to users about when they can reply to conversations.
40-40
: Added Instagram migration banner messageThis message clearly communicates to users that the conversation they're viewing is from a migrated Instagram account and that they should use the new inbox for messaging, preventing confusion about where new messages will appear.
app/javascript/dashboard/components/widgets/conversation/MessagesView.vue (3)
39-39
: Added import for INBOX_TYPES constantThis import provides access to standardized inbox type constants, improving code maintainability and readability by avoiding hardcoded string values.
252-253
: Grammar correction for Instagram channel checkChanged
isAInstagramChannel
toisAnInstagramChannel
for proper English grammar. This is a small but important consistency improvement.Also applies to: 267-268
523-528
: Added banner for duplicate Instagram inbox notificationThis banner properly implements the UI for showing users when they're viewing a conversation in an old Instagram inbox that has been migrated. The conditional display based on the
hasDuplicateInstagramInbox
computed property ensures it only shows when relevant.app/javascript/dashboard/store/modules/agentBots.js (1)
194-198
: Ensure state mutation stays reactiveFor Vue 2 users the direct assignment to a nested property can break reactivity; in Vue 3 it’s safe but if the store is still on Vue 2 (composition API plug-in) use
Vue.set
(orstate.records = [...state.records]
) to guarantee updates propagate.Please verify which runtime is in use and adapt accordingly.
app/services/conversations/message_window_service.rb (1)
9-14
: Boundary check excludes the exact‐limit minute – confirm this is intentional
Time.current < last_incoming_message.created_at + time
denies a reply exactly at the boundary (e.g. 24 h to the second). Most platforms allow replies within the window, including the last second; others include the boundary.
If the business rule is “≤ window”, change to<=
.app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue (1)
104-121
:useAlert
used like a function but imported as composable
useAlert
usually returns{ showAlert }
. Directly callinguseAlert(msg)
assumes it’s a curried helper.
If the composable definition isconst { showAlert } = useAlert();
, adapt accordingly:- useAlert(t('AGENT_BOTS.AVATAR.SUCCESS_DELETE')); + const { showAlert } = useAlert(); + showAlert(t('AGENT_BOTS.AVATAR.SUCCESS_DELETE'));Repeat for the error branch and submit handler.
app/javascript/dashboard/i18n/locale/en/agentBots.json (1)
5-6
: Tone of description may not fit enterprise docs“Most fabulous members of your team” is informal and could be mis-aligned with brand voice.
Confirm with product/design before shipping.app/javascript/dashboard/components-next/message/bubbles/Email/Index.vue (12)
12-12
: Good component import for translation functionality.This import adds the translation toggle UI component, which is consistent with other message bubble components.
16-16
: Good use of composable pattern for translations.The
useTranslations
composable centralizes translation logic, improving code organization and reusability.
24-24
: Good state management for translation toggling.The
renderOriginal
ref provides the state needed to toggle between original and translated content.
34-35
: Effective composable implementation.Using the composable pattern here properly separates concerns and makes the translation functionality reusable across components.
37-47
: Well-structured computed property for original email content.This computed property correctly handles the text content from email attributes.
49-56
: Clear conditional logic for content selection.The
messageContent
computed property properly handles the logic for showing either translated or original content based on user preference.
58-65
: Consistent pattern for handling translated content.The
textToShow
andfullHTML
computed properties follow the same pattern for determining which content to display, maintaining consistency in the codebase.Also applies to: 67-74
76-82
: Clean refactoring for HTML quote extraction.The computed property for unquoted HTML correctly maintains the functionality while integrating with the translation toggle feature.
92-94
: Simple and effective toggle handler.The
handleSeeOriginal
function is concise and focused on a single responsibility.
122-122
: Improved gradient styling for better visibility.The updated gradient CSS (with percentages) provides better control over the fade effect.
140-140
: Good implementation of dynamic keys.The dynamic keys with translation suffix ensure proper re-rendering of the Letter component when toggling between translated and original content.
Also applies to: 152-152
184-189
: Well-structured component integration.The TranslationToggle component is conditionally rendered based on translation availability and properly receives and emits relevant props/events.
app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue (10)
244-246
: Grammar fix and proper channel type handling.Changed "isAInstagramChannel" to "isAnInstagramChannel" for correct grammar and added proper message length limits for Instagram.
247-252
: Improved message length detection for WhatsApp variants.Added specific message length handling for different WhatsApp channel types.
259-264
: Enhanced message length limit handling for additional channel types.Added message length limits for Twilio SMS and WhatsApp Cloud, providing better coverage of all supported channel types.
277-277
: Added Instagram to file upload supported chann 10000 els.Properly includes Instagram in the list of channels that support file uploads.
403-410
: Improved conversation switching logic.The updated watcher now only resets email fields when switching to a completely different conversation ID, preventing data loss when performing actions like self-assignment within the same conversation.
429-435
: Improved email field handling with dedicated watcher.The new lastEmail watcher replaces the previous deep watcher on currentChat.messages, providing more precise updates for CC/BCC fields based on relevant email changes rather than any message change.
708-709
: Added handling for Instagram messages with detailed comments.The comments clearly explain the technical reason for handling Instagram messages differently (echo events causing duplicates), helping future developers understand the implementation.
958-1003
: Refactored message handling for different channel types.The refactored method now:
- Handles Instagram messages by sending attachments without captions
- Sends a separate text message for Instagram if there's text content
- For WhatsApp, only applies caption to the first attachment
- For WhatsApp, only sends text if there are no attachments
This approach prevents duplicate messages while maintaining functionality across channel types.
982-989
: Clear conditional logic for text message sending.This conditional logic clearly separates when to send a standalone text message based on channel type:
- For Instagram: Always send text in a separate message if there's any text
- For other channels: Only send a text message if there are no attachments
1169-1169
: UI style update for plain text input.Added the
rounded-none
class for consistent styling of the input field.app/javascript/dashboard/routes/dashboard/settings/reports/components/ChartElements/ChartStats.vue (6)
3-5
: Good addition of necessary imports for handling loading states.The new imports for Spinner component, STATUS constants, and vue-i18n are appropriate for the enhanced functionality to handle different loading states in the component.
16-19
: Well-defined new prop with appropriate default value.Adding the
summaryFetchingKey
prop with a sensible default value allows for flexibility when reusing this component in different contexts, like with bot summary metrics that might use a different fetching status key.
22-25
: Good use of composables and destructuring.Properly setting up i18n and destructuring the enhanced useReportMetrics composable with the new fetchingStatus property. The code follows Vue 3 composition API best practices.
45-59
: Well-implemented loading and error states.The conditional rendering based on fetchingStatus properly handles all three possible states: loading (with spinner), error (with translated message), and success (with metric display). This improves the user experience by providing clear feedback about the data loading process.
60-63
: Appropriate conditional rendering of trend indicators.The trend indicator is now only shown when there's a trend value available and the fetching has completed successfully, which prevents displaying partial or inaccurate trend data during loading states.
52-52
:✅ Verification successful
Verify translation key exists.
Ensure that the translation key 'REPORT.SUMMARY_FETCHING_FAILED' is defined in your locale files to avoid displaying raw translation keys to users.
🏁 Script executed:
#!/bin/bash # Check if the translation key exists in the locale files echo "Checking for translation key in locale files..." grep -r "SUMMARY_FETCHING_FAILED" --include="*.json" app/javascript/dashboard/i18n/Length of output: 7507
Translation key ‘REPORT.SUMMARY_FETCHING_FAILED’ confirmed
The key is defined in everyapp/javascript/dashboard/i18n/locale/*/report.json
. No further action needed.
@@ -120,6 +120,7 @@ export default { | |||
|
|||
mounted() { | |||
this.$store.dispatch('agents/get'); | |||
this.$store.dispatch('portals/index'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Please verify the need for portal data in the conversation view
The added dispatch to fetch portal data looks correct, but it's unclear why portal data is needed in the conversation view component.
🏁 Script executed:
#!/bin/bash
# Check for usage of portal data in ConversationView or its child components
rg -A 3 -B 3 "portal" app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue app/javascript/dashboard/components/ChatList.vue app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue
Length of output: 983
Confirm necessity of portal data dispatch in ConversationView.vue
It appears the portals/index
action isn’t consumed in this component or its immediate children. Removing it will prevent an unnecessary API call unless you plan to use portal data here.
• File: app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue
Line 123: this.$store.dispatch('portals/index');
Action required:
- If portal data is needed, add explicit references (e.g.,
computed
properties or template bindings) so its usage is clear. - Otherwise, remove the dispatch to clean up unused data fetching.
export const buildPortalURL = (portalSlug, customDomain) => { | ||
const baseURL = getPortalBaseURL(customDomain); | ||
return `${baseURL}/hc/${portalSlug}`; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Implement portalSlug validation.
The JSDoc mentions that the function throws an error if portalSlug
is not provided or invalid, but no validation is implemented. Consider adding validation to match the documentation.
export const buildPortalURL = (portalSlug, customDomain) => {
+ if (!portalSlug || typeof portalSlug !== 'string') {
+ throw new Error('Portal slug must be a non-empty string');
+ }
const baseURL = getPortalBaseURL(customDomain);
return `${baseURL}/hc/${portalSlug}`;
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const buildPortalURL = (portalSlug, customDomain) => { | |
const baseURL = getPortalBaseURL(customDomain); | |
return `${baseURL}/hc/${portalSlug}`; | |
}; | |
export const buildPortalURL = (portalSlug, customDomain) => { | |
if (!portalSlug || typeof portalSlug !== 'string') { | |
throw new Error('Portal slug must be a non-empty string'); | |
} | |
const baseURL = getPortalBaseURL(customDomain); | |
return `${baseURL}/hc/${portalSlug}`; | |
}; |
const getDefaultBaseURL = () => { | ||
const { hostURL, helpCenterURL } = window.chatwootConfig || {}; | ||
const baseURL = helpCenterURL || hostURL || ''; | ||
|
||
if (!baseURL) { | ||
throw new Error('No valid base URL found in configuration'); | ||
} | ||
|
||
return baseURL; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for missing base URL.
The getDefaultBaseURL
function throws an error if no valid base URL is found, but this error isn't caught by the calling functions. Consider handling this error or providing a fallback URL to prevent potential crashes.
const getPortalBaseURL = customDomain =>
- customDomain ? formatCustomDomain(customDomain) : getDefaultBaseURL();
+ customDomain ? formatCustomDomain(customDomain) : getDefaultBaseURLSafely();
+/**
+ * Gets the default base URL from configuration with fallback
+ * @returns {string} The default base URL or a fallback URL
+ */
+const getDefaultBaseURLSafely = () => {
+ try {
+ return getDefaultBaseURL();
+ } catch (error) {
+ console.error('Failed to get default base URL:', error);
+ return 'https://app.chatwoot.com'; // Or another appropriate fallback
+ }
+};
Committable suggestion skipped: line range outside the PR's diff.
def process_attachments_as_files_for_email_reply | ||
# Attachment processing for direct email replies (when replying to a single message) | ||
# | ||
# How attachments are handled: | ||
# 1. Total file size (<20MB): Added directly to the email as proper attachments | ||
# 2. Total file size (>20MB): Added to @large_attachments to be displayed as links in the email | ||
|
||
@options[:attachments] = [] | ||
@large_attachments = [] | ||
current_total_size = 0 | ||
|
||
@message.attachments.each do |attachment| | ||
raw_data = attachment.file.download | ||
attachment_name = attachment.file.filename.to_s | ||
file_size = raw_data.bytesize | ||
|
||
# Attach files directly until we hit 20MB total | ||
# After reaching 20MB, send remaining files as links | ||
if current_total_size + file_size <= 20.megabytes | ||
mail.attachments[attachment_name] = raw_data | ||
@options[:attachments] << { name: attachment_name } | ||
current_total_size += file_size | ||
else | ||
@large_attachments << attachment | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix undefined variable and add error handling in attachment processing.
The attachment processing logic is well-structured, but there's a critical issue on line 49 where mail.attachments
is used, but mail
is not defined at this point. This will cause runtime errors.
Apply this diff to fix the issue:
- mail.attachments[attachment_name] = raw_data
+ @options[:attachments] << { name: attachment_name, content: raw_data }
Also consider adding error handling for file downloads:
- raw_data = attachment.file.download
+ begin
+ raw_data = attachment.file.download
+ rescue => e
+ Rails.logger.error("Failed to download attachment: #{e.message}")
+ next
+ end
Consider making the 20MB limit configurable rather than hardcoded:
+ MAX_ATTACHMENT_SIZE = 20.megabytes
+
@message.attachments.each do |attachment|
raw_data = attachment.file.download
attachment_name = attachment.file.filename.to_s
file_size = raw_data.bytesize
- # Attach files directly until we hit 20MB total
+ # Attach files directly until we hit MAX_ATTACHMENT_SIZE total
# After reaching 20MB, send remaining files as links
- if current_total_size + file_size <= 20.megabytes
+ if current_total_size + file_size <= MAX_ATTACHMENT_SIZE
@@ -39,7 +39,7 @@ const textColorClass = computed(() => { | |||
docx: 'dark:text-[#D6E1FF] text-[#1F2D5C]', // indigo-12 | |||
json: 'text-n-slate-12', | |||
odt: 'dark:text-[#D6E1FF] text-[#1F2D5C]', // indigo-12 | |||
pdf: 'text-n-ruby-12', | |||
pdf: 'text-n-slate-12', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Ensure dark mode consistency for PDF chips
The mapping for pdf
now only uses text-n-slate-12
and lacks a dark-mode variant. This may render illegible text in dark themes.
Please update the mapping to include both light and dark variants, for example:
- pdf: 'text-n-slate-12',
+ pdf: 'dark:text-[#EDEEF0] text-n-slate-12',
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pdf: 'text-n-slate-12', | |
pdf: 'dark:text-[#EDEEF0] text-n-slate-12', |
actions.fetchAccountSummary({ commit }, reportObj); | ||
await flushPromises(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always await the action promise to avoid swallowed rejections
actions.fetch*
returns a promise. If the promise rejects but we don’t await
it, the test may pass while the underlying logic is broken and an un-handled rejection warning will be emitted outside the test scope.
-actions.fetchAccountSummary({ commit }, reportObj);
-await flushPromises();
+await actions.fetchAccountSummary({ commit }, reportObj);
+await flushPromises();
Please apply the same pattern to all tests that dispatch fetchAccountSummary
, fetchBotSummary
, and the three download*Reports
helpers.
Also applies to: 62-64, 91-93, 110-112
instagramUnauthorized() { | ||
return this.isAInstagramChannel && this.inbox.reauthorization_required; | ||
return this.isAnInstagramChannel && this.inbox.reauthorization_required; | ||
}, | ||
// Check if a instagram inbox exists with the same instagram_id | ||
hasDuplicateInstagramInbox() { | ||
const instagramId = this.inbox.instagram_id; | ||
const instagramInbox = | ||
this.$store.getters['inboxes/getInstagramInboxByInstagramId']( | ||
instagramId | ||
); | ||
|
||
return this.inbox.channel_type === INBOX_TYPES.FB && instagramInbox; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Guard against missing inbox data in hasDuplicateInstagramInbox
During the first render this.inbox
can be {}
while the actual record is still being fetched, so direct property access is safe (undefined
) but the store getter will still be invoked with an invalid id. A tiny guard removes the unnecessary lookup and prevents avoidable Vue re-computations.
-hasDuplicateInstagramInbox() {
- const instagramId = this.inbox.instagram_id;
- const instagramInbox =
- this.$store.getters['inboxes/getInstagramInboxByInstagramId'](
- instagramId
- );
- return this.inbox.channel_type === INBOX_TYPES.FB && instagramInbox;
-},
+hasDuplicateInstagramInbox() {
+ const instagramId = this.inbox.instagram_id;
+ if (!instagramId) return false;
+ const instagramInbox =
+ this.$store.getters['inboxes/getInstagramInboxByInstagramId'](
+ instagramId
+ );
+ return this.inbox.channel_type === INBOX_TYPES.FB && !!instagramInbox;
+},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
instagramUnauthorized() { | |
return this.isAInstagramChannel && this.inbox.reauthorization_required; | |
return this.isAnInstagramChannel && this.inbox.reauthorization_required; | |
}, | |
// Check if a instagram inbox exists with the same instagram_id | |
hasDuplicateInstagramInbox() { | |
const instagramId = this.inbox.instagram_id; | |
const instagramInbox = | |
this.$store.getters['inboxes/getInstagramInboxByInstagramId']( | |
instagramId | |
); | |
return this.inbox.channel_type === INBOX_TYPES.FB && instagramInbox; | |
instagramUnauthorized() { | |
return this.isAnInstagramChannel && this.inbox.reauthorization_required; | |
}, | |
// Check if a instagram inbox exists with the same instagram_id | |
hasDuplicateInstagramInbox() { | |
const instagramId = this.inbox.instagram_id; | |
if (!instagramId) return false; | |
const instagramInbox = | |
this.$store.getters['inboxes/getInstagramInboxByInstagramId']( | |
instagramId | |
); | |
return this.inbox.channel_type === INBOX_TYPES.FB && !!instagramInbox; | |
}, |
def messaging_window | ||
case @conversation.inbox.channel_type | ||
when 'Channel::Api' | ||
api_messaging_window | ||
when 'Channel::FacebookPage' | ||
messenger_messaging_window | ||
when 'Channel::Instagram' | ||
instagram_messaging_window | ||
when 'Channel::Whatsapp' | ||
MESSAGING_WINDOW_24_HOURS | ||
when 'Channel::TwilioSms' | ||
twilio_messaging_window | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fall-through channels get unlimited replies – safe default?
messaging_window
returns nil
for any channel not enumerated in the case. can_reply?
then short-circuits to true
.
- Is that desirable for all future channels (e.g. Email, Telegram)?
- A forgotten mapping silently yields unrestricted replies – consider an explicit
else
that raises or logs.
- end
+ else
+ Rails.logger.warn("[MessageWindow] Unhandled channel: #{@conversation.inbox.channel_type}")
+ nil
+ end
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def messaging_window | |
case @conversation.inbox.channel_type | |
when 'Channel::Api' | |
api_messaging_window | |
when 'Channel::FacebookPage' | |
messenger_messaging_window | |
when 'Channel::Instagram' | |
instagram_messaging_window | |
when 'Channel::Whatsapp' | |
MESSAGING_WINDOW_24_HOURS | |
when 'Channel::TwilioSms' | |
twilio_messaging_window | |
end | |
end | |
def messaging_window | |
case @conversation.inbox.channel_type | |
when 'Channel::Api' | |
api_messaging_window | |
when 'Channel::FacebookPage' | |
messenger_messaging_window | |
when 'Channel::Instagram' | |
instagram_messaging_window | |
when 'Channel::Whatsapp' | |
MESSAGING_WINDOW_24_HOURS | |
when 'Channel::TwilioSms' | |
twilio_messaging_window | |
else | |
Rails.logger.warn("[MessageWindow] Unhandled channel: #{@conversation.inbox.channel_type}") | |
nil | |
end | |
end |
const initializeForm = () => { | ||
if (props.selectedBot && Object.keys(props.selectedBot).length) { | ||
const { name, description, outgoing_url, thumbnail, bot_config } = | ||
props.selectedBot; | ||
formState.botName = name || ''; | ||
formState.botDescription = description || ''; | ||
formState.botUrl = outgoing_url || bot_config?.webhook_url || ''; | ||
formState.botAvatarUrl = thumbnail || ''; | ||
} else { | ||
resetForm(); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
selectedBot
watcher uses deep: true
unnecessarily
You watch the entire object and immediately overwrite primitives; deep watching fires on every nested change (e.g. thumbnail update) and may reset the form mid-edit.
Drop deep: true
or watch props.selectedBot.id
.
-watch(() => props.selectedBot, initializeForm, { immediate: true, deep
10000
: true });
+watch(() => props.selectedBot?.id, initializeForm, { immediate: true });
const store = useStore(); | ||
const { t } = useI18n(); | ||
const dialogRef = ref(null); | ||
const uiFlags = useMapGetter('agentBots/getUIFlags'); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useMapGetter
returns a plain object, not a ref – adjust access
useMapGetter('agentBots/getUIFlags')
returns the value directly (not a ref
) in the existing composable.
uiFlags.value.isCreating
therefore breaks with “Cannot read property ‘isCreating’ of undefined”.
Use:
-const uiFlags = useMapGetter('agentBots/getUIFlags');
+const uiFlags = useMapGetter('agentBots/getUIFlags'); // returns reactive object
...
-? uiFlags.value.isCreating
-: uiFlags.value.isUpdating
+? uiFlags.isCreating
+: uiFlags.isUpdating
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
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 (3)
spec/controllers/public/api/v1/portals_controller_spec.rb (1)
33-58
: Well-structured tests for favicon rendering behavior.These new test cases properly verify the conditional favicon rendering based on portal logo presence, which aligns with the layout changes mentioned in the summary. The tests follow good RSpec practices with clear context descriptions and appropriate assertions.
Consider these minor improvements:
- The favicon link check could be more specific to avoid potential false positives from similar HTML elements
- The tests could verify the actual URL of the favicon points to the portal's logo
- File handling in the first test could use a block pattern for automatic resource cleanup
context 'when portal has a logo' do it 'includes the logo as favicon' do # Attach a test image to the portal - file = Rails.root.join('spec/assets/sample.png').open - portal.logo.attach(io: file, filename: 'sample.png', content_type: 'image/png') - file.close + Rails.root.join('spec/assets/sample.png').open do |file| + portal.logo.attach(io: file, filename: 'sample.png', content_type: 'image/png') + end get "/hc/#{portal.slug}/en" expect(response).to have_http_status(:success) - expect(response.body).to include('<link rel="icon" href=') + expect(response.body).to match(/<link rel="icon"[^>]*href="[^"]*#{portal.logo.filename}"/) end endspec/mailers/conversation_reply_mailer_spec.rb (2)
177-178
: Consider parameterizing the attachment size thresholdThe tests implicitly rely on file sizes to differentiate between "small" and "large" attachments. For better maintainability, consider parameterizing the size threshold used for classification.
-attachment.file.attach(io: Rails.root.join('spec/assets/large_file.pdf').open, filename: 'large_file.pdf', content_type: 'application/pdf') +# Define the threshold in a constant or let block for better readability +# let(:attachment_size_threshold) { ConversationReplyMailerHelper::LARGE_ATTACHMENT_THRESHOLD } +attachment.file.attach(io: Rails.root.join('spec/assets/large_file.pdf').open, filename: 'large_file.pdf', content_type: 'application/pdf') +# Optionally add a comment indicating why this file is considered "large"
187-190
: Consider using HTML parser instead of regex for link verificationUsing regex for HTML pattern matching can be fragile. Consider using a proper HTML parser like Nokogiri for more reliable verification of link elements.
-# Should render a link with large_file.pdf as the link text -expect(mail.body.encoded).to match(%r{<a [^>]*>large_file\.pdf</a>}) -# Small file should not be rendered as a link in the body -expect(mail.body.encoded).not_to match(%r{<a [^>]*>avatar\.png</a>}) +# Use Nokogiri for more reliable HTML parsing +html = Nokogiri::HTML(mail.body.encoded) +# Should render a link with large_file.pdf as the link text +expect(html.css('a').map(&:text)).to include('large_file.pdf') +# Small file should not be rendered as a link in the body +expect(html.css('a').map(&:text)).not_to include('avatar.png')
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Gemfile.lock
is excluded by!**/*.lock
📒 Files selected for processing (27)
app/builders/messages/instagram/base_message_builder.rb
(1 hunks)app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
(5 hunks)app/javascript/dashboard/composables/useInbox.js
(2 hunks)app/javascript/dashboard/composables/useUISettings.js
(1 hunks)app/javascript/dashboard/i18n/locale/en/conversation.json
(1 hunks)app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
(1 hunks)app/javascript/dashboard/i18n/locale/en/integrations.json
(1 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
(3 hunks)app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
(6 hunks)app/javascript/dashboard/store/modules/inboxes.js
(1 hunks)app/javascript/shared/mixins/inboxMixin.js
(1 hunks)app/listeners/agent_bot_listener.rb
(1 hunks)app/mailers/conversation_reply_mailer.rb
(1 hunks)app/models/channel/twilio_sms.rb
(0 hunks)app/models/channel/whatsapp.rb
(0 hunks)app/models/conversation.rb
(1 hunks)app/services/conversations/message_window_service.rb
(1 hunks)app/views/api/v1/models/_inbox.json.jbuilder
(1 hunks)config/features.yml
(3 hunks)db/schema.rb
(1 hunks)lib/integrations/bot_processor_service.rb
(0 hunks)package.json
(4 hunks)spec/controllers/public/api/v1/portals_controller_spec.rb
(1 hunks)spec/mailers/conversation_reply_mailer_spec.rb
(1 hunks)spec/models/channel/twilio_sms_spec.rb
(0 hunks)spec/models/conversation_spec.rb
(1 hunks)spec/services/conversations/message_window_service_spec.rb
(1 hunks)
💤 Files with no reviewable changes (4)
- app/models/channel/whatsapp.rb
- lib/integrations/bot_processor_service.rb
- app/models/channel/twilio_sms.rb
- spec/models/channel/twilio_sms_spec.rb
✅ Files skipped from review due to trivial changes (3)
- app/mailers/conversation_reply_mailer.rb
- package.json
- app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
🚧 Files skipped from review as they are similar to previous changes (16)
- app/builders/messages/instagram/base_message_builder.rb
- db/schema.rb
- app/views/api/v1/models/_inbox.json.jbuilder
- app/javascript/dashboard/composables/useInbox.js
- app/javascript/dashboard/composables/useUISettings.js
- app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
- app/javascript/dashboard/i18n/locale/en/integrations.json
- app/javascript/shared/mixins/inboxMixin.js
- config/features.yml
- app/javascript/dashboard/i18n/locale/en/conversation.json
- app/listeners/agent_bot_listener.rb
- app/javascript/dashboard/store/modules/inboxes.js
- app/models/conversation.rb
- app/services/conversations/message_window_service.rb
- app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
- app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
🧰 Additional context used
🧬 Code Graph Analysis (2)
spec/services/conversations/message_window_service_spec.rb (3)
app/services/conversations/message_window_service.rb (2)
hours
(1-66)can_reply?
(9-13)app/models/conversation.rb (1)
can_reply?
(117-119)spec/spec_helper.rb (1)
with_modified_env
(18-20)
spec/models/conversation_spec.rb (2)
app/services/conversations/message_window_service.rb (1)
can_reply?
(9-13)app/models/conversation.rb (1)
can_reply?
(117-119)
🔇 Additional comments (3)
spec/mailers/conversation_reply_mailer_spec.rb (1)
157-223
: Well-implemented test coverage for email attachments handlingThe implementation provides comprehensive test coverage for handling different types of email attachments:
- Small attachments are properly attached to emails
- Large attachments are rendered as links with appropriate labeling
- Mixed scenarios handle both types correctly
This matches the implementation changes mentioned in the summary where attachments are separated into small (attached) and large (linked) categories based on size thresholds.
spec/models/conversation_spec.rb (1)
811-831
: Well-structured delegation tests for can_reply?The addition of these tests properly validates that the
can_reply?
method delegates to theConversations::MessageWindowService
, following the single responsibility principle. The tests use mocking appropriately to isolate and verify this delegation behavior.spec/services/conversations/message_window_service_spec.rb (1)
1-645
: Comprehensive and well-organized test suite for the new serviceThis is an excellent test suite that thoroughly covers all messaging window scenarios across different channel types. The tests are structured logically by channel type, with appropriate test cases for each messaging window rule.
I particularly appreciate:
- Clear organization by channel type
- Testing both positive and negative cases
- Proper testing of feature flags with environment variables
- Coverage of edge cases (e.g., messages exactly at window boundaries)
- Testing sequences of messages (last incoming vs outgoing with previous incoming)
This refactoring centralizes what was previously scattered logic into a single service with comprehensive test coverage, which will make future maintenance much easier.
Pull Request Template
Description
Please include a summary of the change and issue(s) fixed. Also, mention relevant motivation, context, and any dependencies that this change requires.
Fixes # (issue)
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.
Checklist:
This change is
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Refactor
Tests
Chores
Documentation