Commit Graph

6340 Commits

Author SHA1 Message Date
Vishnu Narayanan
4381be5f3e
feat: disable helpcenter on hacker plans (#12068)
This change blocks Help Center access for default/Hacker-plan accounts
and closes the downgrade gap that could leave `help_center` enabled
after a subscription falls back to the default cloud plan.

Fixes: none
Closes: none

## Why

Default-plan accounts should not be able to access the Help Center, but
the downgrade fallback path only reset the plan name and did not
reconcile premium feature flags. That meant some accounts could keep
`help_center` enabled even after landing back on the Hacker/default
plan.

## What this change does

- blocks Help Center portal and article access for default/Hacker-plan
accounts
- reconciles premium feature flags when a subscription falls back to the
default cloud plan, so `help_center` is disabled immediately instead of
waiting for a later webhook
- preserves existing account `custom_attributes` during Stripe customer
recreation instead of overwriting them
- adds Enterprise coverage for the default-plan access checks on hosted
and custom-domain Help Center routes
- fixes the public access check to use the resolved portal object so
blocked requests return the intended response instead of raising an
error

## Validation

1. Create or use an account on the default/Hacker cloud plan with an
active portal.
2. Visit the portal home page and a published article on both the
Chatwoot-hosted URL and a configured custom domain.
3. Confirm the Help Center is blocked for that account.
4. Downgrade a paid account back to the default/Hacker plan through the
Stripe webhook flow.
5. Confirm `help_center` is disabled right after the downgrade fallback
is processed and the account can no longer access the Help Center.

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-26 23:48:46 -07:00
Shivam Mishra
127ac0a6b2
fix: show backend error message on API channel creation failure (#13855) 2026-03-27 11:42:33 +05:30
Sivin Varghese
5d9d754961
chore(editor): Auto-linkify URLs immediately on paste (#13900)
# Pull Request Template

## Description

This PR upgrades the ProseMirror editor and enables automatic URL
linkification on paste. Previously, URLs were only linkified after a
user input event (e.g., typing a space). With this change, URLs are now
linkified instantly when pasted.

Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working

### https://github.com/chatwoot/prosemirror-schema/pull/42

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

**Screencast**

**Before**


https://github.com/user-attachments/assets/d38725c9-a152-4c2c-8c33-3ee717f1628f



**After**


https://github.com/user-attachments/assets/9a69a0b6-93ee-421e-896b-5a4e01a167ba


## 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
2026-03-27 11:29:54 +05:30
Sivin Varghese
cac7438fff
fix: Email Channel links are not working (backend) (#13898)
# Pull Request Template

## Description

This PR fixes the link formatting issue on the backend by adding
`:autolink` to `ChatwootMarkdownRenderer#render_message`, ensuring all
URLs are converted to `<a>` tags.

Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)


## 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

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-26 21:44:57 -07:00
Haruma HIRABAYASHI
0b41d7f483
docs(swagger): fix operationId typo converation -> conversation (#13920)
issue: https://github.com/chatwoot/chatwoot/issues/13921

Fix a typo in the Messages API operationId where `converation` was used
instead of `conversation`. This causes cspell errors when generating
client code with tools like Orval.

## What changed

- `swagger/paths/public/inboxes/messages/index.yml`: fixed operationId
from `list-all-converation-messages` to `list-all-conversation-messages`
- `swagger/swagger.json` and `swagger/tag_groups/client_swagger.json`:
regenerated to reflect the fix


## Note

If you are using a code generator like Orval against this swagger spec,
the generated function name will change from
`listAllConverationMessages` to `listAllConversationMessages`.
2026-03-27 09:23:55 +05:30
Gabriel Jablonski
8728db8869
fix(conversation): restore scroll-to-bottom after switching conversations (#250)
The scheduled-messages refactor (9a05ff524) split setActiveChat into
two branches but dropped the .then() callback that emits
SCROLL_TO_MESSAGE for normal conversation loads (no messageId).
Without this event scrollToBottom() never fires after messages load,
leaving the scroll at the top and triggering infinite history fetch.
2026-03-26 12:46:53 -03:00
Sivin Varghese
4517c50227
feat: support bulk select and delete for documents (#13907) 2026-03-26 19:48:12 +05:30
Vishnu Narayanan
4c4b70da25
fix: Skip email rate limiting for self-hosted instances (#13915)
Self-hosted installations were incorrectly hitting the daily email rate
limit of 100, seeded from `installation_config`. Since self-hosted users
control their own infrastructure, email rate limiting should only apply
to Chatwoot Cloud.

Closes #13913
2026-03-26 18:06:10 +05:30
Tanmay Deep Sharma
d84ef4cfd6
fix(whatsapp): skip health check during reauthorization flow (#13911)
After a successful WhatsApp OAuth reauthorization, the health check runs
immediately and finds the phone number in a pending provisioning state
(`platform_type: NOT_APPLICABLE`). This incorrectly triggers
`prompt_reauthorization!`, re-setting the Redis disconnect flag and
sending a disconnect email — even though the reauth just succeeded.

The fix skips the health check during reauthorization flows. It still
runs for new channel creation.

Closes https://github.com/chatwoot/chatwoot/pull/12556

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How to reproduce

1. Have a WhatsApp channel with a phone number in pending provisioning
state (display name not yet approved by Meta)
2. Complete the OAuth reauthorization flow
3. Observe that the user receives a "success" response but immediately
gets a disconnect email

## What changed

- `Whatsapp::EmbeddedSignupService#perform` now skips
`check_channel_health_and_prompt_reauth` when `inbox_id` is present
(reauthorization flow)


🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-26 15:00:09 +05:30
Sivin Varghese
23786bcb52
chore: mark conversation notifications as read on visit (#13906) 2026-03-26 14:01:26 +05:30
Shivam Mishra
e4c3f0ac2f
feat: fallback on phone number to update lead (#13910)
When syncing contacts to LeadSquared, the `Lead.CreateOrUpdate` API
defaults to searching by email. If a contact has no email (or a
different email) but a phone number matching an existing lead, the API
fails with `MXDuplicateEntryException` instead of finding and updating
the existing lead. This accounted for ~69% of all LeadSquared
integration errors, and cascaded into "Lead not found" failures when
posting transcript and conversation activities (~14% of errors).

## What changed

- `LeadClient#create_or_update_lead` now catches
`MXDuplicateEntryException` and retries the request once with
`SearchBy=Phone` appended to the body, telling the API to match on phone
number instead
- Once the retry succeeds, the returned lead ID is stored on the contact
(existing behavior), so all future events use the direct `update_lead`
path and never hit the duplicate error again

## How to reproduce

1. Create a lead in LeadSquared with phone number `+91-75076767676` and
email `a@example.com`
2. In Chatwoot, create a contact with the same phone number but a
different email (or no email)
3. Trigger a contact sync (via conversation creation or contact update)
4. Before fix: `MXDuplicateEntryException` error in logs, contact fails
to sync
5. After fix: retry with `SearchBy=Phone` finds and updates the existing
lead, stores the lead ID on the contact
2026-03-26 12:32:27 +05:30
Alok Dangre
742c5cc1f4
feat(dialogflow): make language_code configurable instead of hardcoded (#13221)
# 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.
- Add language_code setting to Dialogflow integration configuration
- Support 'auto' mode to detect language from contact's
additional_attributes
- Fallback to 'en-US' when no language is configured or detected
- Include comprehensive language options (22 languages)
- Add tests for language code configuration scenarios

Fixes #3071

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] 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.

<img width="815" height="506" alt="Screenshot 2026-01-10 220410"
src="https://github.com/user-attachments/assets/26d2619c-ed42-4c9a-a41d-9fb07ef91a30"
/>


## 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

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-25 21:30:17 -07:00
Sivin Varghese
d9e732c005
chore(v5): update priority icons (#13905)
# Pull Request Template

## Description

This PR updates the priority icons with a new set and makes them
consistent across the app.


## How Has This Been Tested?

**Screenshots**
<img width="420" height="550" alt="image"
src="https://github.com/user-attachments/assets/cb392934-6c4d-46b4-9fde-244461da62ef"
/>
<img width="358" height="340" alt="image"
src="https://github.com/user-attachments/assets/cb18df47-9a17-42f8-9367-e8b7c4e3958d"
/>
<img width="344" height="468" alt="image"
src="https://github.com/user-attachments/assets/9de92374-e732-48eb-a8a9-85c5b5100931"
/>
<img width="445" height="548" alt="image"
src="https://github.com/user-attachments/assets/ecc4ce51-165c-4593-a9a2-e70b08a29006"
/>


## 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

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2026-03-26 09:20:36 +05:30
Mazen Khalil
e0e321b8e2
fix: Annotaterb model annotation incomplete migration (#13132)
This pull request fixes the model annotation tooling due to previous
incomplete migration from `annotate` to `annotaterb` gem (#12600). It
also improves the handling of serialized values in the
`InstallationConfig` model by ensuring a default value is set,
simplifying the code, and removing a workaround for YAML
deserialization.

**Annotation tooling updates:**

* Added `.annotaterb.yml` to configure the `annotate_rb` gem with
project-specific options, centralizing annotation settings.
* Replaced the custom `auto_annotate_models.rake` task with the standard
rake task from `annotate_rb`, and added `lib/tasks/annotate_rb.rake` to
load annotation tasks in development environments.
[[1]](diffhunk://#diff-9450d2359e45f1db407b3871dde787a25d60bb721aed179a65ffd2692e95fb4bL1-L61)
[[2]](diffhunk://#diff-578cdfc7ad56637e42472ea891ea286dff8803d9a1750afdbfeafec164d9b8b2R1-R8)

**Model serialization improvements:**

* Updated the `InstallationConfig` model to set a default value for the
`serialized_value` attribute, ensuring it always has a hash with
indifferent access and removing the need for a deserialization
workaround in the `value` method.
[[1]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L22-R22)
[[2]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L36-L39)

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-25 17:51:06 -07:00
Sojan Jose
ecc66e064d Merge branch 'release/4.12.1' into develop 2026-03-25 16:21:38 -07:00
Sojan Jose
7144d55334 Bump version to 4.12.1 2026-03-25 16:20:58 -07:00
Muhsin Keloth
250650dd7a
feat(platform): Add email channel migration endpoint for bulk OAuth channel creation (#13902)
Adds a Platform API endpoint that allows migrating existing Google and
Microsoft email channels (with OAuth credentials) into Chatwoot without
requiring end-users to re-authenticate. This enables customers who lack
Rails console access to programmatically migrate email channels from
legacy systems.
    
 ### How to test

1. Create a Platform App and grant it permissible access to a target
account
2. `POST /platform/api/v1/accounts/:account_id/email_channel_migrations`
with a payload like:
  ```json
  {
    "migrations": [
      {
        "email": "support@example.com",
        "provider": "google",
        "provider_config": {
          "access_token": "...",
          "refresh_token": "...",
          "expires_on": "..."
        },
        "inbox_name": "Migrated Support"
      }
    ]
  }
```
  3. Verify channels are created with correct provider, provider_config, and IMAP defaults
  4. Verify partial failures (e.g. duplicate email) don't roll back other migrations in the batch
  5. Verify unauthenticated and non-permissible requests return 401

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-25 15:58:08 -07:00
Muhsin Keloth
608be1036b
fix: Send raw content in webhook payloads instead of channel-rendered markdown (#13896)
Webhook payloads (`message_created`, `message_updated`) started sending
channel-rendered HTML in the `content` field instead of the original raw
message content after PR #12878. This broke downstream agent bots and
integrations that expected plain text or markdown. 
Closes https://linear.app/chatwoot/issue/PLA-109/webhook-payloads-send-channel-rendered-html-instead-of-raw-content

## How to reproduce

1. Connect an agent bot to a WebWidget inbox
2. Send a message with markdown formatting (e.g. `**bold**`) from the
widget
3. Observe the agent bot webhook payload — `content` field contains
`<p><strong>bold</strong></p>` instead of `**bold**`

## What changed

Split `MessageContentPresenter` into two public methods:
- `outgoing_content` — renders markdown for the target channel (used by
channel delivery services)
- `webhook_content` — returns raw content with CSAT survey URL when
applicable, no markdown rendering (used by `webhook_data`)

Updated `Message#webhook_data` to use `webhook_content` instead of
`outgoing_content`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2026-03-25 16:56:22 +04:00
salmonumbrella
6ff643b045
fix(i18n): add zh_TW snooze parser locale (#13822) 2026-03-25 16:54:18 +05:30
Vishnu Narayanan
775b73d1f9
fix: raise open file descriptor limit to prevent EMFILE errors (#13895)
## Summary
- Adds `LimitNOFILE=65536` to both web and worker systemd service units
- Fixes recurring `Errno::EMFILE` (Too many open files) errors during
peak traffic after deploys

## Context
Puma workers idle at 720-770 open FDs against the default soft limit of
1024, leaving ~250 FDs of headroom. During deploy-triggered instance
refreshes at peak traffic, concurrent requests exhaust the remaining
FDs, causing EMFILE across all web instances.

3 incidents in March 2026 with escalating event counts. The hard limit
is already 524288, so this just raises the soft limit to a standard
production value.

Self-hosted instances pick this up automatically via `cwctl --upgrade`.

Fixes
https://linear.app/chatwoot/issue/CW-6685/errnoemfile-too-many-open-files-rb-sysopen
2026-03-24 17:37:07 -07:00
gabrieljablonski
3aba6affd4 fix(deploy): run migrations before server boot to prevent stale schema errors
Sidekiq was starting before migrations ran, causing RuntimeError on the
group_type enum. Moved db:chatwoot_prepare from post_start into the rails
entrypoint and made sidekiq depend on the rails healthcheck.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:43:12 -03:00
gabrieljablonski
ec587bb5ee feat(portal): add rake task to move portal between accounts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:03:59 -03:00
Aakash Bakhle
14df7b3bc1
fix: ai-assist 404 on CE (#13891)
# Pull Request Template

## Description

Relocate controller from enterprise/ to app/ and add
Api::V1::Accounts::Captain::TasksController.prepend_mod_with for EE
overrides.

Fixes: Ai assist giving 404 on CE

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

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

before:
<img width="482" height="130" alt="image"
src="https://github.com/user-attachments/assets/f51dc28a-ac54-45c4-9015-6f956fdf5057"
/>

after:
<img width="458" height="182" alt="image"
src="https://github.com/user-attachments/assets/eb86a679-5482-4157-9f4e-f3e9953d8649"
/>


## 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
- [x] Any dependent changes have been merged and published in downstream
modules

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2026-03-24 16:58:11 +05:30
Sivin Varghese
6946859ba4
fix: normalize "in less than a minute" to "now" in chat list timestamp (#13874)
# Pull Request Template

## Description

This PR fixes the conversation list showing raw "**in less than a
minute**" text instead of "**now**" for very recent conversations.

Fixes https://linear.app/chatwoot/issue/CW-6666/issue-with-timestamps

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)


## 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
2026-03-24 16:16:35 +05:30
Sivin Varghese
c129ab00ba
fix: normalize "in less than a minute" to "now" in chat list timestamp (#13874) 2026-03-24 15:40:31 +05:30
Muhsin Keloth
7edae93ee8
fix(agent-bot): Include payload in webhook retry failure logs (#13879)
Webhook retry failure logs for agent-bot now include the event payload,
making it easier to identify which event failed when debugging transient
upstream errors (429/500).

Previously the log only showed:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError`

Now it includes the payload:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError payload={"event":"message_created",...}`

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:52:37 +04:00
Captain
4b315bc2ec
chore: Update translations (#13884)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-23 20:06:17 -07:00
Sivin Varghese
30c0479e9a
fix: show agent name in unread bubble for Captain replies (#13876) 2026-03-23 20:03:31 +05:30
Aakash Bakhle
3c0d55f87a
fix: handoff only if conversation pending (#13882)
# Pull Request Template

## Description

- Skip handoff when the conversation is no longer pending. 
- Fetch conversation status with Conversation.uncached to avoid stale
query cache when checking pending state.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

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

Difficult to reproduce locally, but seen a couple of conversations where
bot hands off an extra time or Captain re-generates usually due to a
retry on FaradayTimeoutError but at perform time, the conversation
status is stale so it appears as if Captain responded in an open
conversation.

## 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
- [x] Any dependent changes have been merged and published in downstream
modules
2026-03-23 17:09:58 +05:30
Aakash Bakhle
4af3e830fc
fix: conversation completion prompt to auto-resolve gibberish/no-intent messages after inactivity (#13875)
# Pull Request Template

## Description

For our account, the conversation completion evaluator was proving to be
too conservative. This resulted in queue noise.
This PR adds a line to handle gibberish/single worded messages with no
further context.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

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

<img width="3012" height="978" alt="CleanShot 2026-03-23 at 11 44 55@2x"
src="https://github.com/user-attachments/assets/328195e8-6ea0-4c3a-9049-ee80196eecad"
/>

<img width="2202" height="866" alt="CleanShot 2026-03-23 at 11 47 15@2x"
src="https://github.com/user-attachments/assets/8d51cff4-b18f-4582-9455-8119ec7eff3a"
/>


## 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
- [x] Any dependent changes have been merged and published in downstream
modules
2026-03-23 12:18:23 +05:30
gabrieljablonski
f3e2b31518 ci: migrate Run Chatwoot CE spec workflow to Blacksmith CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:45:59 -03:00
Gabriel Jablonski
d120c25917
fix(groups): restrict enable CTA to superadmin users (#248)
Non-superadmin users now see a "contact your administrator" message
instead of the "how to enable" action button on group disabled banners.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:01:02 -03:00
Captain
b974993886
chore: Update translations (#13845)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2026-03-21 02:20:27 -07:00
dependabot[bot]
4b849cdd11
chore(deps): bump bcrypt from 3.1.20 to 3.1.22 (#13852)
Bumps [bcrypt](https://github.com/bcrypt-ruby/bcrypt-ruby) from 3.1.20
to 3.1.22.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bcrypt-ruby/bcrypt-ruby/releases">bcrypt's
releases</a>.</em></p>
<blockquote>
<h2>v3.1.22</h2>
<h2>What's Changed</h2>
<ul>
<li>Move compilation after bundle install by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/291">bcrypt-ruby/bcrypt-ruby#291</a></li>
<li>Add TruffleRuby in CI by <a
href="https://github.com/tjschuck"><code>@​tjschuck</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/293">bcrypt-ruby/bcrypt-ruby#293</a></li>
<li>fix env url by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/294">bcrypt-ruby/bcrypt-ruby#294</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.21...v3.1.22">https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.21...v3.1.22</a></p>
<h2>v3.1.21</h2>
<h2>What's Changed</h2>
<ul>
<li>Provide a 'Changelog' link on rubygems.org/gems/bcrypt by <a
href="https://github.com/mark-young-atg"><code>@​mark-young-atg</code></a>
in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/274">bcrypt-ruby/bcrypt-ruby#274</a></li>
<li>Support ruby 3.3 and 3.4.0-preview1 by <a
href="https://github.com/m-nakamura145"><code>@​m-nakamura145</code></a>
in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/276">bcrypt-ruby/bcrypt-ruby#276</a></li>
<li>Mark as ractor-safe by <a
href="https://github.com/mohamedhafez"><code>@​mohamedhafez</code></a>
in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/280">bcrypt-ruby/bcrypt-ruby#280</a></li>
<li>Add == gotcha that can be unintuitive at first by <a
href="https://github.com/federicoaldunate"><code>@​federicoaldunate</code></a>
in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/279">bcrypt-ruby/bcrypt-ruby#279</a></li>
<li>Constant compare by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/282">bcrypt-ruby/bcrypt-ruby#282</a></li>
<li>try to modernize CI by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/287">bcrypt-ruby/bcrypt-ruby#287</a></li>
<li>Try to deal with flaky tests by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/288">bcrypt-ruby/bcrypt-ruby#288</a></li>
<li>Configure trusted publishing by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/289">bcrypt-ruby/bcrypt-ruby#289</a></li>
<li>bump version by <a
href="https://github.com/tenderlove"><code>@​tenderlove</code></a> in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/290">bcrypt-ruby/bcrypt-ruby#290</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/mark-young-atg"><code>@​mark-young-atg</code></a>
made their first contribution in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/274">bcrypt-ruby/bcrypt-ruby#274</a></li>
<li><a
href="https://github.com/m-nakamura145"><code>@​m-nakamura145</code></a>
made their first contribution in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/276">bcrypt-ruby/bcrypt-ruby#276</a></li>
<li><a
href="https://github.com/mohamedhafez"><code>@​mohamedhafez</code></a>
made their first contribution in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/280">bcrypt-ruby/bcrypt-ruby#280</a></li>
<li><a
href="https://github.com/federicoaldunate"><code>@​federicoaldunate</code></a>
made their first contribution in <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/pull/279">bcrypt-ruby/bcrypt-ruby#279</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.20...v3.1.21">https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.20...v3.1.21</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bcrypt-ruby/bcrypt-ruby/blob/master/CHANGELOG">bcrypt's
changelog</a>.</em></p>
<blockquote>
<p>3.1.22 Mar 18 2026</p>
<ul>
<li>[CVE-2026-33306] Fix integer overflow in Java extension</li>
</ul>
<p>3.1.21 Dec 31 2025</p>
<ul>
<li>Use constant time comparisons</li>
<li>Mark as Ractor safe</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="831ce64cb0"><code>831ce64</code></a>
Merge commit from fork</li>
<li><a
href="32e687ec5f"><code>32e687e</code></a>
bump version update changelog</li>
<li><a
href="5faa274833"><code>5faa274</code></a>
Fix integer overflow in JRuby BCrypt rounds calculation</li>
<li><a
href="aafc0332ac"><code>aafc033</code></a>
Merge pull request <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/issues/294">#294</a>
from bcrypt-ruby/fix-publishing</li>
<li><a
href="01f947a66a"><code>01f947a</code></a>
fix env url</li>
<li><a
href="92ca1d67de"><code>92ca1d6</code></a>
Merge pull request <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/issues/293">#293</a>
from bcrypt-ruby/truffleruby-ci-alt-implementation</li>
<li><a
href="4d1d95b8ec"><code>4d1d95b</code></a>
Add TruffleRuby in CI</li>
<li><a
href="36a04a2278"><code>36a04a2</code></a>
Merge pull request <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/issues/291">#291</a>
from tenderlove/fix-publishing</li>
<li><a
href="01cc68835f"><code>01cc688</code></a>
Move compilation after bundle install</li>
<li><a
href="82e6c4c6cf"><code>82e6c4c</code></a>
Merge pull request <a
href="https://redirect.github.com/bcrypt-ruby/bcrypt-ruby/issues/290">#290</a>
from tenderlove/bump</li>
<li>Additional commits viewable in <a
href="https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.20...v3.1.22">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bcrypt&package-manager=bundler&previous-version=3.1.20&new-version=3.1.22)](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 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>
2026-03-20 16:30:50 -07:00
dependabot[bot]
310590cae3
chore(deps): bump json from 2.18.1 to 2.19.2 (#13849)
Bumps [json](https://github.com/ruby/json) from 2.18.1 to 2.19.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ruby/json/releases">json's
releases</a>.</em></p>
<blockquote>
<h2>v2.19.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix a format string injection vulnerability in <code>JSON.parse(doc,
allow_duplicate_key: false)</code>. <code>CVE-2026-33210</code></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/ruby/json/compare/v2.19.1...v2.19.2">https://github.com/ruby/json/compare/v2.19.1...v2.19.2</a></p>
<h2>v2.19.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix a compiler dependent GC bug introduced in
<code>2.18.0</code>.</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/ruby/json/compare/v2.19.0...v2.19.1">https://github.com/ruby/json/compare/v2.19.0...v2.19.1</a></p>
<h2>v2.19.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix <code>allow_blank</code> parsing option to no longer allow
invalid types (e.g. <code>load([], allow_blank: true)</code> now raise a
type error).</li>
<li>Add <code>allow_invalid_escape</code> parsing option to ignore
backslashes that aren't followed by one of the valid escape
characters.</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/ruby/json/compare/v2.18.1...v2.19.0">https://github.com/ruby/json/compare/v2.18.1...v2.19.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ruby/json/blob/master/CHANGES.md">json's
changelog</a>.</em></p>
<blockquote>
<h3>2026-03-18 (2.19.2)</h3>
<ul>
<li>Fix a format string injection vulnerability in <code>JSON.parse(doc,
allow_duplicate_key: false)</code>. <code>CVE-2026-33210</code>.</li>
</ul>
<h3>2026-03-08 (2.19.1)</h3>
<ul>
<li>Fix a compiler dependent GC bug introduced in
<code>2.18.0</code>.</li>
</ul>
<h3>2026-03-06 (2.19.0)</h3>
<ul>
<li>Fix <code>allow_blank</code> parsing option to no longer allow
invalid types (e.g. <code>load([], allow_blank: true)</code> now raise a
type error).</li>
<li>Add <code>allow_invalid_escape</code> parsing option to ignore
backslashes that aren't followed by one of the valid escape
characters.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="54f8a878ae"><code>54f8a87</code></a>
Release 2.19.2</li>
<li><a
href="393b41c3e5"><code>393b41c</code></a>
Fix a format string injection vulnerability</li>
<li><a
href="dbf6bb12aa"><code>dbf6bb1</code></a>
Merge pull request <a
href="https://redirect.github.com/ruby/json/issues/953">#953</a> from
ruby/dependabot/github_actions/actions/create-gi...</li>
<li><a
href="7187315b45"><code>7187315</code></a>
Bump actions/create-github-app-token from 2 to 3</li>
<li><a
href="4a42a04280"><code>4a42a04</code></a>
Release 2.19.1</li>
<li><a
href="13689c2699"><code>13689c2</code></a>
Add missing GC_GUARD in <code>fbuffer_append_str</code></li>
<li><a
href="a11acc1ff4"><code>a11acc1</code></a>
Release 2.19.0</li>
<li><a
href="0a4fb79cd9"><code>0a4fb79</code></a>
fbuffer.h: Use size_t over unsigned long</li>
<li><a
href="a29fcdcb4a"><code>a29fcdc</code></a>
Add depth validation to Jruby and TruffleRuby implementations</li>
<li><a
href="de993aa766"><code>de993aa</code></a>
Reject negative depth; add overflow guards to prevent hang/crash</li>
<li>Additional commits viewable in <a
href="https://github.com/ruby/json/compare/v2.18.1...v2.19.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=json&package-manager=bundler&previous-version=2.18.1&new-version=2.19.2)](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 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>
2026-03-20 16:30:35 -07:00
Gabriel Jablonski
4a608a01e8
Merge pull request #246 from fazer-ai/fix/baileys-on-whatsapp-array-response
fix(baileys): handle array response in on_whatsapp endpoint
2026-03-20 16:49:25 -03:00
gabrieljablonski
0da4c52ee8 fix(baileys): handle array response in on_whatsapp endpoint
The Baileys `/on-whatsapp` endpoint can return a bare JSON array instead
of a hash with a `data` key. Calling `Array#dig` with a string key
raises `TypeError (no implicit conversion of String into Integer)`,
causing a 500 error. Handle both response shapes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:41:22 -03:00
Sivin Varghese
251e9980fd
chore: Auto-focus editor when replying to a message (#13857)
# Pull Request Template

## Description

This PR adds support to auto-focus the editor when clicking reply to
this message, the editor now automatically receives focus so users can
start typing immediately.

Fixes
https://linear.app/chatwoot/issue/CW-6661/typing-box-not-focused-after-clicking-reply-to-message

## Type of change


- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Screencast


https://github.com/user-attachments/assets/c5e77055-3f68-4ad8-934e-cfc465166e8a




## 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
2026-03-20 16:59:27 +05:30
Tanmay Deep Sharma
2b50909d9b
fix: use last_activity_at for orphan conversation cleanup timeframe (#13859)
## Description

The RemoveOrphanConversationsService filters orphan conversations by a
time window before deleting them. Previously it used created_at, which
could miss old conversations that still had recent activity.
Switching to last_activity_at ensures the cleanup window reflects actual
conversation activity rather than creation time.

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

- By running Rake task 
- Run the job from console 

## 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
2026-03-20 16:28:05 +05:30
Aakash Bakhle
290dd3abf5
feat: allow captain to access contact attributes (#13850)
# Pull Request Template

## Description

Captain v1 does not have access to contact attributes. Added a toggle to
let user choose if they want contact information available to Captain.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

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

Specs and locally
<img width="1924" height="740" alt="CleanShot 2026-03-19 at 18 48 19@2x"
src="https://github.com/user-attachments/assets/353cfeaa-cd58-40eb-89e7-d660a1dc1185"
/>

![Uploading CleanShot 2026-03-19 at 18.53.26@2x.png…]()


## 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
- [x] Any dependent changes have been merged and published in downstream
modules
2026-03-20 16:15:06 +05:30
gabrieljablonski
eb22a96ba5 fix(baileys): read groups_enabled flag dynamically from ENV
GROUPS_ENABLED was a Ruby constant evaluated once at class load time,
so changes to the env var were not picked up on boot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 01:53:36 -03:00
Gabriel Jablonski
2bf55d261a
Merge pull request #244 from fazer-ai/chore/merge-upstream-4.12.0
Chore/merge upstream 4.12.0
2026-03-20 01:00:39 -03:00
gabrieljablonski
89cd41c4cf refactor: update webhook job parameters to include account webhook and delivery ID 2026-03-20 00:48:50 -03:00
gabrieljablonski
8fcef79847 Merge branch 'chatwoot/develop' into chore/merge-upstream-4.12.0 2026-03-20 00:27:45 -03:00
gabrieljablonski
d154edf8ea feat: add utility analyzer messages for improved classification 2026-03-19 23:59:18 -03:00
gabrieljablonski
d3ce2a4cf9 Merge branch 'main' into chore/merge-upstream-4.12.0 2026-03-19 23:24:55 -03:00
Cayo P. R. Oliveira
c6bfd1eed3
feat: schedule messages recurrence (#240)
* feat(scheduled-messages): add recurring scheduled messages

Implements the recurring scheduled messages feature allowing agents to
configure recurrence rules when scheduling messages, with automatic
creation of subsequent scheduled messages after each send.

Backend:
- RecurringScheduledMessage model with JSONB recurrence_rule validation
- RecurrenceCalculatorService for next occurrence date calculation
- RecurrenceDescriptionService for human-readable rule descriptions
- CreateNextOccurrenceService for auto-creating child ScheduledMessages
- RecurringScheduledMessagesController with CRUD operations
- RecurringScheduledMessagePolicy for authorization
- Modified SendScheduledMessageJob to handle recurrence after send
- Updated due_for_sending scope to exclude resolved conversations
- ActionCable events for real-time updates
- Activity message i18n (en + pt-BR)

Frontend:
- RecurrenceDropdown.vue with contextual shortcut options
- RecurrenceCustomModal.vue for custom recurrence configuration
- RecurringScheduledMessageItem.vue for sidebar display
- Integration into ScheduledMessageModal.vue
- Updated ScheduledMessages.vue with recurrence section and filtering
- Vuex store module for recurring scheduled messages
- API client for CRUD operations
- WebSocket handlers in actionCable.js
- recurrenceHelpers.js utility functions
- i18n keys for en and pt-BR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): fix creation and edit visibility

- Fix API payload key mismatch (snake_case vs camelCase) in modal submit
- Add status: 'active' to recurring creation payload
- Fix strong params to permit recurrence_rule array fields (week_days)
- Cast string values from strong params to integers for JSONB validation
- Show RecurrenceDropdown when editing (remove isEditing gate)
- Populate recurrenceRule from scheduled message's recurring parent
- Include recurring_scheduled_message_id and recurrence_rule in
  scheduled message jbuilder response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): fix dropdown toggle and locale tag errors

- Use DropdownItem :click prop instead of @click to use the injected
  closeMenu from DropdownContainer context (default slot doesn't
  expose toggle)
- Normalize locale from pt_BR to pt-BR for Intl.DateTimeFormat
  compatibility in RecurringScheduledMessageItem

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(recurring-messages): add separator line and expandable history

- Add border separator between recurring messages section and
  pending/draft messages, matching the history section separator
- Replace static 'N enviadas' counter with clickable toggle that
  expands to show individual sent/failed child messages with
  status badges and formatted timestamps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(recurring-messages): add click-to-navigate on sent children

Make sent child messages in recurring message history clickable.
Clicking navigates to the actual message in the conversation using
the messageId query param, same pattern as ScheduledMessageItem.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(recurring-messages): allow editing recurring messages

- Add edit button to RecurringScheduledMessageItem (active only)
- Transform recurring message into scheduledMessage-compatible shape
  with recurring_scheduled_message_id set, so the modal reuses
  the existing update path
- Handle edge case of removing recurrence from a recurring message
  (cancels series without trying to update a non-existent standalone)
- Sent history is preserved by the backend update action

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): show recurrence field without date selection

Remove v-if="scheduledDate" gate so the recurrence dropdown is
always visible in the modal. Falls back to today's date for
contextual shortcut labels when no date is selected yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): ensure recurrence visible on edit and add pending_scheduled_message to API

- Add pending_scheduled_message to recurring_scheduled_message jbuilder
  so REST API data matches WebSocket push_event_data
- Add fallback in openEditRecurringModal to find pending child from
  scheduled_messages array when pending_scheduled_message is absent
- Add same fallback in nextSendLabel computed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): add sections for Drafts and Pending messages in the UI

* refactor(scheduled-messages): merge RecurringScheduledMessageItem into ScheduledMessageItem

Consolidate the recurring message card into the existing
ScheduledMessageItem component instead of maintaining a separate
component. The unified component detects recurring messages via
recurrence_rule and conditionally shows:
- Recurrence description header with repeat icon
- Next send time label
- Expandable sent/failed children history with click-to-navigate
- Stop button (replaces delete) with confirmation modal
- Active/completed/cancelled status badges

Delete the now-unused RecurringScheduledMessageItem.vue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): use blue badge for active, keep green for sent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): remove draft and pending sections from UI and update recurrence title

* fix(recurring-messages): use Teleport for recurrence dropdown

Replace DropdownContainer with Teleport-based floating dropdown so
options render outside the modal. Fixes:
- Dropdown no longer enlarges the modal or causes scrolling
- Dropdown closes before Custom modal opens (no overlap)
- Auto-detects available space and opens above/below trigger

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): recalculate next date when recurrence rule changes

When editing a recurring message and changing the recurrence rule,
the pending occurrence date is now validated against the new rule.
If the user-provided date doesn't match (e.g. Thursday removed from
weekly days), the system computes the next valid date using
RecurrenceCalculatorService instead of blindly using the old date.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): resolve deprecated onClose and disabled type warnings

- Replace :on-close prop with @close event on woot-modal components
- Cast hasTemplate computed to boolean to fix disabled prop type check

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): replace remaining deprecated on-close props

- ScheduledMessages.vue delete confirm modal
- RecurrenceCustomModal.vue modal

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): address review feedback on PR #240

- Fix v-if/v-else bug hiding message list behind resolved warning
- Fix occurrences_sent incrementing on failed sends (skip_increment flag)
- Fix compute_next_valid_date using .min instead of .max
- Fix Vuex delete action to update state on cancel (not remove)
- Use atomic update_counters for occurrences_sent increment
- Add safe Date.iso8601 parsing with rescue in should_complete?
- Add null: false to occurrences_sent migration column
- Fix pt-BR accent: Recorrencia → Recorrências
- Use I18n.with_locale(account.locale) for all activity messages
- Fix N+1 in jbuilder partials (Ruby filtering + eager loading)
- Add interval >= 1 validation to RecurrenceCustomModal isValid
- Validate recurrence_rule presence when status is active
- Add ISO8601 date format validation for end_date
- Add unknown_agent i18n key for fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): wrap create/update in transactions, clean pending on deactivation

- Wrap create and update flows in ActiveRecord transactions
- Move attachment purge after save! to prevent data loss on validation failure
- Destroy pending children when status transitions to non-active
- Fixes critical bug where stopping recurrence could leave armed pending messages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): prevent monthly/yearly day-of-month drift

Store the original intended day in recurrence_rule JSONB as month_day
(for monthly) and year_day/year_month (for yearly). The calculator
now uses these stored values instead of @last_date.day, preventing
drift after short months cap the day (e.g., Jan 31 → Feb 28 → all
subsequent months stuck on 28).

Backend:
- RecurrenceCalculatorService: use rule[:month_day] for monthly and
  rule[:year_day]/rule[:year_month] for yearly calculations
- Controller: permit and cast the new integer keys

Frontend:
- recurrenceHelpers: yearly shortcuts include year_day/year_month
- RecurrenceCustomModal: emit month_day for monthly day_of_month
  rules and year_day/year_month for yearly rules

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): i18n description services, align due_for_sending scope

Backend:
- RecurrenceDescriptionService: replace hardcoded English with I18n.t()
  calls; accept locale parameter and use I18n.with_locale
- RecurringScheduledMessage model: pass account locale to description service
- ScheduledMessage: align due_for_sending? instance method with scope by
  checking conversation status (open/pending)

Frontend:
- buildRecurrenceDescription: use t() i18n function instead of manual
  isPt locale branching
- Add DESCRIPTION i18n keys to en/conversation.json and pt_BR/conversation.json
- Update RecurrenceDropdown and ScheduledMessageItem callers to pass t

i18n:
- Add recurring_scheduled_messages.description.* keys to en.yml and pt_BR.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): keep pending child when deactivating recurrence

When editing a recurring message to disable recurrence while setting a
future send date, the pending child message is now preserved instead of
being destroyed. This allows a 'final send' without creating new
recurrences (the send job already guards with recurring&.active?).

Backend:
- Add update_pending_on_deactivation: updates pending child's
  scheduled_at or creates a final pending occurrence
- Replace destroy_all in update's non-active branch

Frontend:
- activeRecurringMessages now includes non-active recurring messages
  that still have a pending child (pending_scheduled_message)
- Stop button hidden for already-cancelled recurring messages
- inactiveRecurringMessages excludes messages with pending children

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): prevent removing recurrence from existing recurring message

Once a scheduled message has recurrence, the 'Does not repeat' option is
hidden from the RecurrenceDropdown when editing. This avoids edge cases
where deactivating recurrence leaves the message in an ambiguous display
state.

- RecurrenceDropdown: add hideNoRepeat prop, filter NO_REPEAT from shortcuts
- ScheduledMessageModal: pass hideNoRepeat when isEditingRecurring
- Revert update_pending_on_deactivation (no longer reachable from UI)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): address CodeRabbit review feedback

- ScheduledMessage#push_event_data: expose recurring_scheduled_message_id
  in ActionCable payloads so frontend correctly classifies children
- RecurringScheduledMessagePolicy: add agent_bot? check for parity with
  ScheduledMessagePolicy
- RecurrenceCalculatorService: guard against nil/empty week_days
- Factory: bind inbox and account to conversation to prevent cross-account
  flakiness in specs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(recurring-messages): update schema to enforce non-null constraint on occurrences_sent

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2026-03-19 22:51:14 -03:00
Cayo P. R. Oliveira
8ffdc16faf
fix(schedule): enhance attachment handling in scheduled message modal (#239)
* fix(schedule): enhance attachment handling in scheduled message modal

* fix(schedule): enable attachment removal in scheduled message updates
2026-03-19 22:45:21 -03:00
Cayo P. R. Oliveira
a06354c6b2
feat: scheduled message shortcuts (#238)
* feat(scheduled-messages): add predefined time shortcuts for scheduling

Replace the manual date/time picker with predefined day and time period
chip selectors for faster message scheduling.

Day shortcuts: Today, Tomorrow, This weekend (Sat), Next week (Mon),
Next weekend (next Sat), Next month (1st), Custom (date picker fallback).

Time period shortcuts: Morning (8:00), Afternoon (13:00), Evening (18:00).

Each day chip shows the corresponding date (dd/MM) in secondary color.
Past time periods for 'Today' are automatically disabled.
The existing date picker is preserved as the 'Custom' option.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scheduled-messages): use dropdown selectors and remove header

Replace chip/button selectors with native <select> dropdowns for day
and time period selection. Remove the 'Date and time to send' header
from the modal since the dropdown labels serve as placeholders.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): close datepicker on confirm and click-outside

Removed manual open/close state management and @click.stop that was
blocking click-outside detection. Added confirm prop so the picker
has an explicit OK button and auto-closes properly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): initialize custom mode when editing existing schedule

When editing a scheduled message, the ScheduleDateShortcuts component
now detects the pre-existing datetime and opens in Custom mode with
the datepicker pre-filled, preserving the original date and time.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): use locale-aware date format in shortcuts

Replace hardcoded dd/MM format with Intl.DateTimeFormat using
navigator.language, matching the existing locale-aware pattern
in DatePickerHelper.js. Removes unused date-fns format import.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): use app locale for date formatting

Replace navigator.language with the i18n app locale for shortcut date
labels and datepicker calendar. Add getDatePickerLang helper that
generates locale-aware day/month names via Intl.DateTimeFormat.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): normalize locale tag to BCP 47 format

Chatwoot uses underscore locale tags (pt_BR) but Intl.DateTimeFormat
requires BCP 47 hyphens (pt-BR). Add toBcp47 normalizer to prevent
RangeError: invalid language tag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): locale-aware time period display

Add formatHour helper using Intl.DateTimeFormat so time periods
show 8:00/13:00/18:00 in pt-BR and 8:00 AM/1:00 PM/6:00 PM in en.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(scheduled-messages): add unit tests for scheduleDateShortcutHelpers

Cover getShortcutDate (weekday/Saturday/Sunday edge cases),
applyTimePeriod, isTimePeriodPast, formatShortDate, formatHour,
getDatePickerLang, and getDayShortcutOptions including locale
normalization for underscore tags like pt_BR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): sync customDateTime when modelValue changes in custom mode

Keep DatePicker in sync when the parent changes modelValue while
already in Custom mode (e.g. switching between scheduled messages).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): add aria-labels to schedule dropdown selects

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): use empty string instead of null for dateTimeError

Avoids Vue prop validation warning since ScheduleDateShortcuts
declares dateTimeError as a String prop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scheduled-messages): simplify to 3 fixed schedule shortcuts

Replace two dropdown selectors (6 day options × 3 time periods) with
3 pre-computed clickable shortcut buttons:
- Tomorrow morning (08:00)
- Tomorrow afternoon (13:00)
- Monday morning (08:00)

Each shortcut shows the exact calendar date and time for clarity.

Special Sunday rule: 'Monday' points to next week's Monday since
'Tomorrow' already covers the immediate Monday.

The 'Custom' option with full DatePicker is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(scheduled-messages): Gmail-style list design for schedule shortcuts

Replace chip buttons with full-width clickable rows in a bordered
container, matching Gmail's 'Schedule send' dialog pattern:
- Label on left, formatted date/time on right in gray
- Calendar icon for the custom date/time option
- Subtle border separators between rows
- Selected state with blue highlight

Also improves date formatting from '15/03' to '15 de mar.' using
month short names for better readability.

Updates i18n:
- PT_BR: 'Amanhã à tarde', 'Escolher data e hora'
- EN: 'Choose date and time'

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(scheduled-messages): improve modal layout and datepicker positioning

- Add 'Schedule send' / 'Programar envio' section header above the
  schedule shortcuts for clear visual identification
- Move attachment/template controls next to the message editor so
  content-related actions stay grouped together
- Move datepicker outside the bordered shortcut container to avoid
  cramped positioning; add rounded-xl and proper text sizing
- Add i18n key SCHEDULE_LABEL (en: 'Schedule send', pt_BR: 'Programar envio')

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(schedule): replace popup datepicker with inline calendar

Replace the popup-style vue-datepicker-next with an inline calendar
(same approach used by the snooze feature), rendering the calendar
directly within the modal for a more intuitive UX.

- Switch DatePicker to inline mode (no popup/z-index issues)
- Add disablePastTimes validation (prevents selecting past times)
- Full-width responsive calendar with scoped deep styles
- Remove unused DATETIME_PLACEHOLDER and DATETIME_FORMAT i18n keys

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(schedule): replace datepicker with natural language input

Replace the broken inline datepicker with a natural language text input
powered by chrono-node, similar to Chatwoot's upcoming snooze UX.

Users can now type dates naturally:
- EN: 'tomorrow at 2pm', 'next friday morning', 'in 3 hours'
- PT: 'amanhã às 14h', 'próxima sexta de manhã', '20 de março às 10h'

Changes:
- Add chrono-node dependency for natural language date parsing
- Add preProcessDateInput() to normalize PT time expressions (8h→8:00,
  de manhã→8:00, à tarde→13:00, de noite→18:00)
- Add parseNaturalDate() with locale-aware parsing (PT/EN)
- Add formatFullDateTime() for parsed date preview display
- Replace DatePicker with text input + real-time parsed date preview
- Show green checkmark when date is valid, amber warning if in the past,
  hint text if input is unrecognizable
- Remove unused getDatePickerLang() and vue-datepicker-next import
- Remove unused DATETIME_PLACEHOLDER/DATETIME_FORMAT i18n keys
- Update i18n: CUSTOM label, placeholder, hint, and past-date warning
- Update tests: 40 tests covering new functions (was 29)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): improve PT natural language preprocessing

Normalize accent-less input common in casual Brazilian Portuguese:
- 'amanha' → 'amanhã', 'sabado' → 'sábado', 'proxima' → 'próxima'
- 'as' → 'às' before digits or time-of-day words (e.g. 'as 19h')
- Support 'pela manhã/tarde/noite' and 'no período da manhã/tarde/noite'

Previously 'Amanhã as 19h' failed because chrono-node requires 'às'
(with accent) as a time connector. Now all common casual PT patterns
work reliably.

Tests: 50 passing (was 40)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): use forwardDate to always parse weekdays as upcoming

chrono-node defaults to the *most recent* occurrence of a weekday,
so 'sexta' on Monday returned last Friday (past). Adding
{ forwardDate: true } makes it always return the next occurrence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(schedule): add datepicker fallback and dual-language parsing

- Add discrete calendar icon button next to text input that toggles
  an inline vue-datepicker-next with date+time selection
- Try both chrono.pt and chrono (EN) parsers, pick the one that
  matches more of the input text — supports mixed-language input
  like 'quarta 10am' or 'friday às 14h'
- Insert 'às' connector between PT weekday names and bare numbers
  so 'quarta 10' parses correctly (chrono.pt requires the connector)
- Add DATEPICKER_TOOLTIP i18n key (EN + PT_BR)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): toggle between text input and datepicker views

When calendar button is clicked, hide the natural language text input
and show the inline datepicker full-width. A small 'Type a date and
time' link below the calendar lets users switch back to text input.
Calendar button is vertically centered with the input field (size-[34px]
matches input height).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): align calendar button with text input height

Use self-stretch instead of fixed size so the button stretches to
match the input height in the flex row, eliminating misalignment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): adjust margin for custom text input in date shortcuts

* fix(schedule): use popup datepicker with datetime and confirm

Replace the inline datepicker toggle with a popup DatePicker that
opens directly from the calendar button. Uses type='datetime' with
confirm mode so users can pick both date and time. On confirm, the
selected datetime populates the natural language text field. The text
input and calendar button are always visible side by side.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): hide seconds column in datepicker

Add :show-second='false' to only show hour and minute selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(schedule): adjust DatePicker width for better responsiveness

* refactor(schedule): use locale for parser priority and add aria-label

- parseNaturalDate now uses locale to call the matching parser first
  (chrono.pt for PT, chrono for EN) before falling back to the other,
  removing the eslint-disable comment for unused locale param
- Add aria-label to the natural language date input for screen readers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: remove leftover planning files

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2026-03-19 22:43:51 -03:00
Cayo P. R. Oliveira
9a05ff5247
feat: find scheduled message (#237)
* feat(scheduled-messages): scroll to sent message from sidebar

- Expose message_id in JBuilder serialization and push_event_data
- Add HIGHLIGHT_MESSAGE bus event for in-page message highlighting
- Add 'Go to message' button on sent scheduled messages in sidebar
- Enhance onScrollToMessage to fetch messages around target when not in DOM
- Extend Message.vue highlight to work with bus events (not just route query)
- Add i18n keys for EN and pt-BR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scheduled-messages): make sent card clickable instead of button

Replace the 'Go to message' button with a clickable card. The entire
sent scheduled message card now has cursor-pointer, hover highlight,
and a tooltip — clicking anywhere on it scrolls to the message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): address PR review feedback

- Use camelCase value for HIGHLIGHT_MESSAGE bus event ('highlightMessage')
- Show toast alert when message not found after fetch or on fetch error
- Use the MESSAGE_NOT_FOUND i18n key that was previously unused

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): use messageId query param for find message

Replace direct bus event emission with route navigation using
?messageId= query param, reusing the same proven mechanism used by
search results and copy-message-link.

Changes:
- ScheduledMessageItem: router.replace with ?messageId= instead of
  emitting SCROLL_TO_MESSAGE directly
- ConversationView: handle ?messageId= on same-conversation (was
  previously skipped), fetch messages around target and scroll
- MessagesView: clean up ?messageId= from URL after scroll/error

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scheduled-messages): add toast feedback for find message

Show a persistent "Searching for message..." toast while fetching,
auto-dismissed on success. Show "Message not found" error toast if
the message cannot be located.

Uses usePendingAlert for the loading state in both ConversationView
(initial fetch) and MessagesView (fallback fetch).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: prevent scroll overshoot when navigating to message

Remove the immediate fetchPreviousMessages() call after
scrollIntoView({ behavior: smooth }). The fetch was prepending
messages above the target while the smooth scroll animation was
still running, shifting the DOM and causing the scroll to stop
short of the target message. The scroll event handler will
naturally trigger message loading when the user scrolls up later.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(scheduled-messages): remove redundant clearMessageIdFromRoute calls

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 22:35:04 -03:00