8000 Chore/merge upstream by gabrieljablonski · Pull Request #31 · fazer-ai/chatwoot · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

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

Merged
merged 43 commits into from
Apr 25, 2025
Merged

Chore/merge upstream #31

merged 43 commits into from
Apr 25, 2025

Conversation

gabrieljablonski
Copy link
@gabrieljablonski gabrieljablonski commented Apr 25, 2025

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

This change is Reviewable

Summary by CodeRabbit

  • New Features

    • Instagram channel is now generally available, with improved inbox management and migration banners for duplicate accounts.
    • Agent bot management is redesigned with a modern UI, supporting webhook bots, avatar upload, and improved form validation.
    • Translation toggle added for email and text messages, allowing users to switch between original and translated content.
    • Enhanced attachment handling in email replies: large files are shown as links, small files are sent as attachments.
  • Improvements

    • Unified reply window logic across channels, with clearer messaging on reply restrictions.
    • Sidebar and channel settings now always use the latest report routes and Instagram is shown as an available channel.
    • Portal article links now support custom domains.
    • Additional file types (XML) allowed for upload.
    • Enhanced error messages and validation for forms and slugs.
    • Improved message length limits for various channels.
  • Bug Fixes

    • Corrected several UI text typos and improved error messages.
    • Fixed logic for showing/hiding buttons and banners in dialogs and inboxes.
  • Refactor

    • Removed CSML bot support and related UI, models, and services.
    • Centralized reply window logic into a new service for maintainability.
    • Updated codebase for more consistent naming and validation patterns.
  • Tests

    • Added comprehensive tests for new reply window logic, agent bot avatar updates, translation features, and portal URL helpers.
    • Updated and expanded test coverage for inbox and report modules.
  • Chores

    • Upgraded version to 4.1.0 and updated dependencies.
    • Updated copyright and license years.
  • Documentation

    • Improved and expanded localization strings for new features and error messages.

muhsin-k and others added 30 commits April 10, 2025 12:58
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
![CleanShot 2025-04-09 at 13 49
35@2x](https://github.com/user-attachments/assets/575c9905-0bf7-4a6e-8709-026896dd95f8)

#### Finished
![CleanShot 2025-04-09 at 13 49
43@2x](https://github.com/user-attachments/assets/ef7f8cb3-ca34-4627-a954-ba23f156d2ff)

#### 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
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**
![CleanShot 2025-04-17 at 22 53
28@2x](https://github.com/user-attachments/assets/00841dd1-ca1f-47b3-9972-6c9100bd2291)

![CleanShot 2025-04-17 at 22 53
46@2x](https://github.com/user-attachments/assets/648a4770-3aea-4b84-9456-6941744a4d65)


**After**
![CleanShot 2025-04-17 at 22 50
40@2x](https://github.com/user-attachments/assets/d37250fd-5400-4548-82f4-ab0c3b417b3a)
![CleanShot 2025-04-17 at 22 50
47@2x](https://github.com/user-attachments/assets/ca611297-8dd0-4bd9-b6b6-02660c252aa9)

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
- Audited message characters across all channels.
- Replaced `isAInstagramChannel` with `isAnInstagramChannel`
muhsin-k and others added 12 commits April 21, 2025 15:29
# 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 />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=vite&package-manager=npm_and_yarn&previous-version=5.4.17&new-version=5.4.18)](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 />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.18.4&new-version=1.18.8)](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>
@Copilot Copilot AI review requested due to automatic review settings April 25, 2025 19:42
Copy link
coderabbitai bot commented Apr 25, 2025

Walkthrough

This 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

File(s) / Path(s) Change Summary
app/builders/campaigns/campaign_conversation_builder.rb Fixed typo in error message for existing conversation.
app/builders/messages/instagram/base_message_builder.rb Removed early return for outgoing echo messages; added explanatory comment.
app/controllers/api/v1/accounts/agent_bots_controller.rb Broadened strong parameter filtering for bot_config to allow any keys.
app/controllers/dashboard_controller.rb Removed CSML_EDITOR_HOST from global config; added INSTAGRAM_APP_ID to app config.
app/controllers/devise_overrides/omniauth_callbacks_controller.rb Made business account email check case-insensitive.
app/javascript/dashboard/api/agentBots.js Added create, update, and deleteAgentBotAvatar methods for agent bots API.
app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue,
app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue
Enhanced slug validation with specific error messages and custom validator; improved error feedback.
app/javascript/dashboard/components-next/copilot/CopilotAssistantMessage.vue Added placeholder and conditional rendering for empty message content.
app/javascript/dashboard/components-next/dialog/Dialog.vue Footer container now rendered only when at least one button is visible.
app/javascript/dashboard/components-next/message/MessageMeta.vue Improved status indicator logic; fixed Instagram channel naming.
app/javascript/dashboard/components-next/message/TranslationToggle.vue Introduced new component for toggling between original and translated messages.
app/javascript/dashboard/components-next/message/bubbles/Email/Index.vue Added translation toggle support for email content.
app/javascript/dashboard/components-next/message/bubbles/Text/Index.vue Refactored translation logic to use new composable and toggle component.
app/javascript/dashboard/components-next/message/chips/File.vue Changed PDF file chip color class.
app/javascript/dashboard/components-next/sidebar/Sidebar.vue Removed feature flag logic; always uses new report routes.
app/javascript/dashboard/components/layout/config/sidebarItems/settings.js Removed globalConfigFlag from agent bots menu item.
app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue Removed globalConfigFlag visibility logic; now relies on feature flags and enterprise status.
app/javascript/dashboard/components/widgets/ChannelItem.vue Added Instagram app ID check for channel activation.
app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue Renamed Instagram channel check for allowed file types.
app/javascript/dashboard/components/widgets/conversation/MessagesView.vue Added duplicate Instagram inbox detection and alert banner; improved reply window messaging.
app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue Enhanced support for Instagram/WhatsApp message limits and sending; improved watchers for email fields.
app/javascript/dashboard/components/widgets/conversation/linear/Issue.vue,
app/javascript/dashboard/components/widgets/conversation/linear/index.vue
Updated styling and layout for issue display.
app/javascript/dashboard/composables/spec/useTranslations.spec.js,
app/javascript/dashboard/composables/useTranslations.js
Added new composable and tests for translation content handling.
app/javascript/dashboard/composables/useInbox.js,
app/javascript/shared/mixins/inboxMixin.js
Renamed Instagram channel computed property for grammatical correctness.
app/javascript/dashboard/composables/useReportMetrics.js,
app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue,
app/javascript/dashboard/routes/dashboard/settings/reports/components/ChartElements/ChartStats.vue
Added summary fetching status support for report metrics and charts.
app/javascript/dashboard/composables/useUISettings.js Returns a mutable copy of default sidebar items order.
app/javascript/dashboard/helper/portalHelper.js,
app/javascript/dashboard/helper/specs/portalHelper.spec.js
Refactored portal URL helpers to support custom domains; added tests.
app/javascript/dashboard/i18n/locale/en/agentBots.json Restructured and expanded agent bots localization; removed CSML references.
app/javascript/dashboard/i18n/locale/en/conversation.json,
app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
Added new messages for Instagram inbox migration and reply windows.
app/javascript/dashboard/i18n/locale/en/helpCenter.json Added specific format error message for slug validation.
app/javascript/dashboard/i18n/locale/en/integrations.json Added Copilot error message for empty responses.
app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue Loads portals on mount.
app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue,
app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsArticlesEditPage.vue
Added support for portal custom domains in article URL generation.
app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue Refactored to use modals for create/edit/delete; new table layout; avatar support.
app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js Removed CSML bot routes and related components.
app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue New modal component for creating/editing agent bots with avatar upload and validation.
app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotRow.vue,
app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotType.vue,
app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLBotEditor.vue,
app/javascript/dashboard/routes/dashboard/settings/agentBots/components/CSMLMonacoEditor.vue,
app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue,
app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue
Deleted all CSML bot-related components.
app/javascript/dashboard/routes/dashboard/settings/inbox/ChannelList.vue Enabled Instagram as an active channel.
app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue,
app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue,
app/javascript/dashboard/routes/dashboard/settings/inbox/channels/instagram/DuplicateInboxBanner.vue
Added duplicate Instagram inbox detection and banner component.
app/javascript/dashboard/routes/dashboard/settings/reports/BotReports.vue Passed summary fetching key to report container.
app/javascript/dashboard/store/constants.js Added STATUS constants for fetching states.
app/javascript/dashboard/store/modules/agentBots.js,
app/javascript/dashboard/store/modules/specs/agentBots/agentBots.spec.js,
app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js,
app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js
Refactored agent bot actions to use FormData for avatar uploads; added avatar deletion; updated tests and fixtures.
app/javascript/dashboard/store/modules/inboxes.js,
app/javascript/dashboard/store/modules/specs/inboxes/fixtures.js,
app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js
Added getters for finding inboxes by Instagram ID; updated fixtures and tests.
app/javascript/dashboard/store/modules/reports.js,
app/javascript/dashboard/store/modules/specs/reports/actions.spec.js
Added explicit fetching status for summaries; updated tests for new logic.
app/javascript/dashboard/store/mutation-types.js Added mutation types for bot summary status and avatar updates.
app/javascript/shared/constants/messages.js Allowed XML file types for uploads.
app/javascript/shared/helpers/MessageTypeHelper.js Updated and expanded message length constants for various channels.
app/javascript/shared/helpers/Validators.js,
app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js
Added and tested isValidSlug validator function.
app/javascript/shared/store/globalConfig.js Removed CSML editor host from global config store.
app/javascript/sdk/IFrameHelper.js,
app/javascript/sdk/bubbleHelpers.js,
app/javascript/widget/constants/sdkEvents.js,
app/views/widget_tests/index.html.erb
Added new SDK events for postback, opened, and closed; refactored event handling; updated widget test listeners.
app/javascript/sdk/sdk.js Added overflow: hidden to chat bubble CSS.
app/views/api/v1/models/_agent_bot.json.jbuilder Updated JSON to include avatar thumbnail, system bot flag, and conditional outgoing URL.
app/views/api/v1/models/_inbox.json.jbuilder Added instagram_id to Instagram inbox JSON.
app/views/layouts/portal.html.erb Conditionally includes favicon if portal logo is present.
app/views/layouts/vueapp.html.erb Added Instagram app ID to global JS config.
app/views/mailers/conversation_reply_mailer/conversation_transcript.html.erb,
app/views/mailers/conversation_reply_mailer/email_reply.html.erb,
app/views/mailers/conversation_reply_mailer/reply_with_summary.html.erb,
app/views/mailers/conversation_reply_mailer/reply_without_summary.html.erb
Improved attachment display in emails; show filenames and links, corrected HTML attributes.
config/app.yml,
package.json
Bumped version to 4.1.0; updated dependencies.
config/features.yml Enabled agent bots and Instagram channel features; updated/deprecated flags.
db/migrate/20250410061725_convert_csml_bots_to_webhook_bots.rb Migration to convert CSML bots to webhook bots.
db/migrate/20250416182131_flip_chatwoot_v4_default_feature_flag_installation_config.rb Migration to enable chatwoot_v4 feature flag for all accounts.
db/schema.rb Updated schema version.
enterprise/LICENSE Updated copyright year.
lib/integrations/bot_processor_service.rb Removed obsolete comment.
app/jobs/agent_bots/csml_job.rb,
app/services/agent_bots/validate_bot_service.rb
Deleted CSML bot job and validation service.
app/jobs/webhooks/instagram_events_job.rb,
app/services/instagram/test_event_service.rb
Added support for Instagram webhook test events and new test event service.
app/listeners/agent_bot_listener.rb Removed CSML bot event processing; now only supports webhook bots.
app/mailers/conversation_reply_mailer.rb,
app/mailers/conversation_reply_mailer_helper.rb
Improved attachment handling for large files in email replies.
app/models/agent_bot.rb Removed CSML bot type and validation; added system_bot? method.
app/models/channel/api.rb,
app/models/channel/facebook_page.rb,
app/models/channel/twilio_sms.rb,
app/models/channel/whatsapp.rb,
app/models/concerns/channelable.rb
Removed messaging_window_enabled? methods.
app/models/conversation.rb,
app/services/conversations/message_window_service.rb
Refactored reply permission logic to new service; removed old helpers.
app/views/api/v1/models/_agent_bot.json.jbuilder Updated agent bot JSON fields.
spec/controllers/public/api/v1/portals_controller_spec.rb Added tests for favicon presence based on portal logo.
spec/mailers/conversation_reply_mailer_spec.rb Added tests for small and large attachment handling in email replies.
spec/models/channel/twilio_sms_spec.rb Removed tests for deleted messaging_window_enabled? method.
spec/models/conversation_spec.rb Replaced detailed can_reply? tests with delegation test to new service.
spec/services/conversations/message_window_service_spec.rb New test suite for message window service across all channel types.

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
Loading

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
Loading

Agent Bot Creation/Editing with Avatar

sequenceDiagram
    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
Loading

Instagram Duplicate Inbox Detection

sequenceDiagram
    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
Loading

Poem

Oh, what a leap for the bots and the chat,
CSML is gone—webhooks where it’s at!
Instagram’s channels are tidy and bright,
With banners for inboxes, warnings in sight.
Attachments in emails now shimmer with links,
While modals for bots are smoother than you think.
Hopping ahead, we’re version four-point-one—
This bunny’s excited for all that’s been done!
🐇✨

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.rb

Error: RSpec/FactoryBot/* has been extracted to the rubocop-factory_bot gem.
(obsolete configuration found in .rubocop.yml, please update it)

app/listeners/agent_bot_listener.rb

Error: RSpec/FactoryBot/* has been extracted to the rubocop-factory_bot gem.
(obsolete configuration found in .rubocop.yml, please update it)

app/builders/messages/instagram/base_message_builder.rb

Error: RSpec/FactoryBot/* has been extracted to the rubocop-factory_bot gem.
(obsolete configuration found in .rubocop.yml, please update it)

  • 8 others
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

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

Copy link
@Copilot Copilot AI left a 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
Copy link
Preview
Copilot AI Apr 25, 2025

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.

Copy link
@coderabbitai coderabbitai bot left a 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 suggestion

Return 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 accepts categorySlug and locale 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 (like FETCH_STATUS or ASYNC_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 handling

The composable provides a good abstraction for handling translations, but has a few areas for improvement:

  1. 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;
});
  1. 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]];
});
  1. 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 the showingOriginal prop

Consider 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 structure

The 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 axios

Consider 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 method

The 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 a down 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 operations

When 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-unused fetchingStatus flag

After introducing the more granular accountSummaryFetchingStatus and botSummaryFetchingStatus, the generic fetchingStatus 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 explicitly

Using the last-created channel may point to a random customer inbox on multi-tenant systems.

Options:

  1. Query by the Instagram App ID contained in the payload (@messaging[:recipient][:id]), mapping it to a known channel.
  2. Allow an INBOX_ID_FOR_IG_TEST_EVENTS ENV var and fall back to last only in development.

This prevents accidentally creating messages on the wrong account.


38-41: Populate contact details with real test data

Creating a contact inbox with the fixed identifiers 'sender_username' twice (name and username) 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 or username field from the webhook for better realism.


50-58: Nil-safe access for optional text

Instagram test events may not always include a :text key (e.g. media-only tests).
Consider defaulting to a placeholder to avoid NoMethodError: 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 option

The 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 duplicated FormData construction

create and update 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-row loading 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 nil time

last_message_in_messaging_window?(time) is called only when time.present?, but inside the method there’s no guard for time.nil?.
Either make the guard internal or document the pre-condition.


61-63: Possible N+1 when memoising last_incoming_message

@conversation.messages.incoming.last hits the DB if messages 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 with image/) before assigning to formState.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: missing aria-label on action buttons

Add aria-label (or title) 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 drift

You have "NAME.REQUIRED" and ERRORS.NAME with identical text. Maintaining both keys risks divergence across locales.

Consider keeping only ERRORS.* or the flat REQUIRED keys and refactor component look-ups accordingly.


90-91: Missing period at end of sentence

Minor 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc1418 and ef6949e.

⛔ 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 boundaries

Adding 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 changes

These new event listeners for chatwoot:opened and chatwoot: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 constants

Expanding 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 names

Replacing 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 constants

Adding 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 handling

Adding 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 effects

The 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 handler

The 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 branding

The 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 handling

The added tests thoroughly cover the new custom domain functionality in portal article URLs:

  1. Using a custom domain for URL generation
  2. Handling domains with explicit HTTPS prefix
  3. 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 retrieval

Adding 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 support

The renaming from portalLink to articleLink 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 link

This 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 handling

The 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 exposure

Adding 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 clarity

The 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 attribute

The updated implementation correctly:

  1. Shows the actual filename instead of generic "attachment" text
  2. 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 clarity

The 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 attribute

The updated implementation correctly:

  1. Shows the actual filename instead of generic "attachment" text
  2. 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 structure

The changes implement a more readable and user-friendly attachment display:

  1. Clear "Attachments:" label
  2. Each attachment on a separate line when multiple attachments exist
  3. Filename shown instead of generic "attachment" text
  4. Standard target="_blank" attribute used correctly

This 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:

  1. Adding a clear "Attachments:" label
  2. Using actual filenames instead of generic text
  3. 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
The line-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 to bg-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 to text-n-slate-12 aligns with the new slate palette. Functionality remains unchanged.


92-92: Approve divider before priority label
The added bg-n-slate-4 divider before the priority block is consistent with the slate theme update.


114-114: Approve timestamp color adjustment
Applying text-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
Adding instagramAppId to window.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 rendered

The 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 channels

Re-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 naming

The variable has been renamed from isAInstagramChannel to isAnInstagramChannel 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 reports

Adding 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 to isAnInstagramChannel 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 and text/xml MIME types to ALLOWED_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 issues

Creating 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 bots

The addition of the thumbnail field and conditionally including outgoing_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 flag

Adding 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 component

This 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 mutation

The 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 grammar

The renaming from isAInstagramChannel to isAnInstagramChannel 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 property

The export has been correctly updated to match the renamed computed property.

app/controllers/dashboard_controller.rb (2)

39-39: Removed CSML_EDITOR_HOST configuration key

This 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 configuration

Good 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 status

Added 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 component

The 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 fixture

Added 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 fixture

Added 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 bots

This 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 check

The 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 good

The 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 slugs

Adding 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 expected

The bot_type enum now only includes the webhook type (0), successfully removing CSML support as intended.


53-55: Good addition of system_bot? helper method

The 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 ruby

Length 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 all bot_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 spec

Length 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 in app/javascript/shared/helpers/Validators.js and fully covered by ValidatorsHelper.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 and SET_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 js

Length 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 field

The validation for the slug field has been improved by adding the isValidSlug validator alongside the required validator, both with localized error messages. This ensures slugs follow the correct format (letters, numbers, and hyphens only).


53-55: Improved error message handling

The 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 triggers

The 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 validator

The 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 composable

These 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 and translationContent 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 dialogFlowEnabledInboxes

The 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 test

The 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 getters

These new tests verify the behavior of two important getters:

  • getFacebookInboxByInstagramId: Finds a Facebook inbox by Instagram ID
  • getInstagramInboxByInstagramId: Finds an Instagram inbox by Instagram ID

These 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 inboxes

The conditional rendering of the DuplicateInboxBanner component with appropriate localized message looks good.

.husky/pre-commit (2)

7-8: LGTM! Robust handling of staged files

The 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 check

Good 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 js
config/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 consistency

The import from useInbox was correctly updated from isAInstagramChannel to isAnInstagramChannel for better grammatical consistency, aligning with other channel variables like isAnEmailChannel.


25-32: Well-structured destructuring with contentAttributes addition

Good addition of contentAttributes to the destructured properties from useMessageContext(). This enables checking for deleted messages in the status indicator logic.


38-44: Improved status indicator logic

Good 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 checks

Correctly updated references from isAInstagramChannel to isAnInstagramChannel 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 upload

The 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 support

The update method correctly uses multipart/form-data to support avatar updates along with other agent bot data.


21-23: Clear and focused avatar deletion method

Good 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 imports

Clean addition of the TranslationToggle component and useTranslations composable, which helps with code organization and reusability.

Also applies to: 9-9


14-15: Improved translation handling with composable

Good refactoring to use the useTranslations composable, which extracts the translation-related logic into a reusable piece, improving maintainability.


25-25: Clean reference to translation content

Good update to use the reactive translationContent.value from the composable instead of directly accessing content attributes.


51-56: Good component extraction for translation toggle

Excellent 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 to bot_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 symmetrical

You added getAccountSummaryFetchingStatus and getBotSummaryFetchingStatus.
For consistency with existing getters (e.g. getAccountSummary vs getBotSummary) consider renaming the second to getBotSummaryFetchingStatus, which you already did—good catch! No action required.

app/models/conversation.rb (1)

117-119: Delegation looks good – verify autoloading & memoisation

Replacing inline logic with Conversations::MessageWindowService greatly improves separation of concerns. Ensure:

  1. The service is placed in app/services/conversations/message_window_service.rb (matching Rails autoload paths).
  2. 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 testing

The addition of agentBotData import alongside agentBotRecords 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 handling

The test now properly validates that:

  1. The action is invoked with the correct data structure
  2. The HTTP request includes FormData payload for file upload support
  3. 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 structure

The update action test now uses a more explicit structure with separate id and data 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 consistency

The 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 messages

These messages provide clear information to users about:

  1. The Instagram account migration between inboxes
  2. 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 window

This 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 message

This 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 constant

This 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 check

Changed isAInstagramChannel to isAnInstagramChannel 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 notification

This 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 reactive

For 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 (or state.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 calling useAlert(msg) assumes it’s a curried helper.
If the composable definition is const { 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 and fullHTML 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:

  1. Handles Instagram messages by sending attachments without captions
  2. Sends a separate text message for Instagram if there's text content
  3. For WhatsApp, only applies caption to the first attachment
  4. 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 every app/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');
Copy link

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.

Comment on lines +40 to 43
export const buildPortalURL = (portalSlug, customDomain) => {
const baseURL = getPortalBaseURL(customDomain);
return `${baseURL}/hc/${portalSlug}`;
};
Copy link

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.

Suggested change
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}`;
};

Comment on lines +14 to +23
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;
};
Copy link

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.

Comment on lines +30 to +56
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
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

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',
Copy link

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.

Suggested change
pdf: 'text-n-slate-12',
pdf: 'dark:text-[#EDEEF0] text-n-slate-12',

Comment on lines +44 to +46
actions.fetchAccountSummary({ commit }, reportObj);
await flushPromises();

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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

Comment on lines 206 to +217
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;
Copy link

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.

Suggested change
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;
},

Comment on lines 17 to 30
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
Copy link

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.

  1. Is that desirable for all future channels (e.g. Email, Telegram)?
  2. 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.

Suggested change
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

Comment on lines +167 to +178
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();
}
};
Copy link

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 });

Comment on lines +32 to +36
const store = useStore();
const { t } = useI18n();
const dialogRef = ref(null);
const uiFlags = useMapGetter('agentBots/getUIFlags');

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Copy link
@coderabbitai coderabbitai bot left a 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:

  1. The favicon link check could be more specific to avoid potential false positives from similar HTML elements
  2. The tests could verify the actual URL of the favicon points to the portal's logo
  3. 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
 end
spec/mailers/conversation_reply_mailer_spec.rb (2)

177-178: Consider parameterizing the attachment size threshold

The 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 verification

Using 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

📥 Commits

Reviewing files that changed from the base of the PR and between ef6949e and 540f67a.

⛔ 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 handling

The 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 the Conversations::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 service

This 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.

@gabrieljablonski gabrieljablonski merged commit f9f0bde into fazer-ai/main Apr 25, 2025
1 check passed
@gabrieljablonski gabrieljablonski deleted the chore/merge-upstream branch April 25, 2025 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants
0