Commit Graph

295 Commits

Author SHA1 Message Date
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
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
Gabriel Jablonski
a996b920e8
feat: group conversations (#228)
* feat: add group and conversation types to contacts and conversations, and implement conversation group membership model

* chore: add factory and specs for conversation group member model

* chore: add group type checks and associations for contacts and conversations

* refactor: remove scopes from ConversationGroupMember model

* refactor: remove scopes from ConversationGroupMember model specs

* refactor: enhance conversation type migration with concurrent indexing

* feat: add is_active index and scopes to ConversationGroupMember model

* feat: implement GroupConversationHandler for managing group conversations

* feat: add group_type attribute to contact creation

* fix: update WHATSAPP_CHANNEL_REGEX to allow up to 20 digits to handle group jid

* feat: handle group JID format in remote_jid method

* chore: update group contact info when finding or creating group contact

* chore: refactor and implement contact message handling and message creation logic for baileys single contact conversation

* feat: implement group message handling and metadata fetching in WhatsApp service

* chore: add spec for group type handling in contact creation for individual and group contacts

* chore: add specs for test scopes in conversation group members

* chore: update documentation for sender phone extraction in group conversation handler

* chore: move GroupConversationHandler concern to correct dir

* chore: implement specs for recipient_id handling to individual and group contacts

* chore: add group message handling specs for incoming messages

* chore: enhance group message handling to prevent race conditions

* chore: add group_metadata method to with error handling

* chore: add test for sending messages to group recipients in WhatsappBaileysService

* chore: raise error for unsuccessful response in group_metadata method

* chore: adds tests for group metadata retrieval and error handling

* chore: refactor build_sender_contact_attributes to avoid double call methods

* chore: update error handling for attachment download failure in message creation

* chore: optimize update_contact_info method to use compact hash for updates

* chore: simplify find_or_create_sender_contact method return values

* chore: rename group and individual contact message handlers

* chore: remove pointless comments from group contact message handler methods

* chore: refine sender JID extraction logic to remove unnecessary checks

* chore: remove phone number in spec for group contact attributes

* chore: implement sync_group route

* chore: implement get group_members route

* fix: sync_group participants creation handling

* chore: update contact avatar handling in group message processing

* chore: move sync_group functionality for conversation model

* feat: add sync_group action to ConversationsController and route

* fix: set contact name to phone in group message processing

* chore: refine group member retrieval logic in sync_group service and view

* feat: implement group participants update handling

* feat: implement group updates handling and localization for group activities

* chore: add handling for group membership requests and icon changes

* chore: add authorization for sync_group action in ContactsController

* chore: add sync_group endpoint specs for contact management

* chore: add authorization for sync_group action in ConversationsController

* chore: add specs for sync_group endpoint in ConversationsController

* chore: refactor index action in GroupMembersController for improved conversation filtering

* chore: add request specs for group_members endpoint in ContactsController

* chore: add specs for sync_group method in Conversation model

* chore: add specs for sync_group method in Channel::Whatsapp model

* chore: remove comments in find_or_create_group_conversation method

* chore: add specs for Contacts::SyncGroupService to validate group contact behavior

* chore: add specs for Whatsapp::BaileysHandlers::GroupsUpdate to validate group updates

* chore: add specs for Whatsapp::BaileysHandlers::GroupParticipantsUpdate to handle group participant actions

* chore: add fallback for identifier when contact has no phone_number in SendOnWhatsappService

* chore: add specs for group membership request and icon change handling in MessagesUpsert

* chore: add specs for sync_group method to handle group metadata and participant updates

* chore: update sync_group method to retrieve group members and adjust JSON response

* chore: update conversation query to filter by group type in GroupMembersController

* chore: update conversation creation in group_members_controller_spec to specify conversation_type as group

* chore: update find_or_create_group_conversation to include pending conversations

* chore: refactor sync_group method and enhance specs for group conversation handling

* feat: add GroupEventHelper module for managing group activities and contacts

* chore: refactor group contact inbox and conversation creation methods in group handlers

* chore: remove unnecessary check for blank participant contacts in sync_group_members method

* feat: implement message receipt update handling for WhatsApp integration

* chore: resolve rubocop rule for update_last_seen_at method

* chore: update swagger with endpoints for syncing group information and listing group members

* chore: integrate Contacts::SyncGroupService in group members controller, enhance error handling and update swagger

* chore: include participant information in reaction and quoted message keys for send message in group conversations

* chore: enhance whatsapp_baileys_service with participant handling for message keys

* feat: add skill for writing RSpec tests in the project

* fix: update recipient_id logic to directly use contact identifier for group contacts

* chore: implement group stub message handling for membership requests and icon changes

* fix: update whatsapp inbox source_id validation regex spec

* chore: fix spec for contact syncing group

* chore: remove readTimestamp handling and related tests for message read updates in group

* Cayo oliveira/cu 86af01932/4 backend gerenciamento dos grupos (#221)

* feat: add is_active index and scopes to ConversationGroupMember model

* feat: implement GroupConversationHandler for managing group conversations

* feat: add group_type attribute to contact creation

* fix: update WHATSAPP_CHANNEL_REGEX to allow up to 20 digits to handle group jid

* feat: handle group JID format in remote_jid method

* chore: update group contact info when finding or creating group contact

* chore: refactor and implement contact message handling and message creation logic for baileys single contact conversation

* feat: implement group message handling and metadata fetching in WhatsApp service

* chore: add spec for group type handling in contact creation for individual and group contacts

* chore: add specs for test scopes in conversation group members

* chore: update documentation for sender phone extraction in group conversation handler

* chore: move GroupConversationHandler concern to correct dir

* chore: implement specs for recipient_id handling to individual and group contacts

* chore: add group message handling specs for incoming messages

* chore: enhance group message handling to prevent race conditions

* chore: add group_metadata method to with error handling

* chore: add test for sending messages to group recipients in WhatsappBaileysService

* chore: raise error for unsuccessful response in group_metadata method

* chore: adds tests for group metadata retrieval and error handling

* chore: refactor build_sender_contact_attributes to avoid double call methods

* chore: update error handling for attachment download failure in message creation

* chore: optimize update_contact_info method to use compact hash for updates

* chore: simplify find_or_create_sender_contact method return values

* chore: rename group and individual contact message handlers

* chore: remove pointless comments from group contact message handler methods

* chore: refine sender JID extraction logic to remove unnecessary checks

* chore: remove phone number in spec for group contact attributes

* chore: implement sync_group route

* chore: implement get group_members route

* fix: sync_group participants creation handling

* chore: update contact avatar handling in group message processing

* chore: move sync_group functionality for conversation model

* feat: add sync_group action to ConversationsController and route

* fix: set contact name to phone in group message processing

* chore: refine group member retrieval logic in sync_group service and view

* feat: implement group participants update handling

* feat: implement group updates handling and localization for group activities

* chore: add handling for group membership requests and icon changes

* chore: add authorization for sync_group action in ContactsController

* chore: add sync_group endpoint specs for contact management

* chore: add authorization for sync_group action in ConversationsController

* chore: add specs for sync_group endpoint in ConversationsController

* chore: refactor index action in GroupMembersController for improved conversation filtering

* chore: add request specs for group_members endpoint in ContactsController

* chore: add specs for sync_group method in Conversation model

* chore: add specs for sync_group method in Channel::Whatsapp model

* chore: remove comments in find_or_create_group_conversation method

* chore: add specs for Contacts::SyncGroupService to validate group contact behavior

* chore: add specs for Whatsapp::BaileysHandlers::GroupsUpdate to validate group updates

* chore: add specs for Whatsapp::BaileysHandlers::GroupParticipantsUpdate to handle group participant actions

* chore: add fallback for identifier when contact has no phone_number in SendOnWhatsappService

* chore: add specs for group membership request and icon change handling in MessagesUpsert

* chore: add specs for sync_group method to handle group metadata and participant updates

* chore: update sync_group method to retrieve group members and adjust JSON response

* chore: update conversation query to filter by group type in GroupMembersController

* chore: update conversation creation in group_members_controller_spec to specify conversation_type as group

* chore: update find_or_create_group_conversation to include pending conversations

* chore: refactor sync_group method and enhance specs for group conversation handling

* feat: add GroupEventHelper module for managing group activities and contacts

* chore: refactor group contact inbox and conversation creation methods in group handlers

* chore: remove unnecessary check for blank participant contacts in sync_group_members method

* chore: update swagger with endpoints for syncing group information and listing group members

* chore: integrate Contacts::SyncGroupService in group members controller, enhance error handling and update swagger

* fix: update recipient_id logic to directly use contact identifier for group contacts

* chore: implement group stub message handling for membership requests and icon changes

* fix: update whatsapp inbox source_id validation regex spec

* chore: fix spec for contact syncing group

* fix: optimize update_last_seen_at method to use update_columns

* feat: Implement full frontend and backend support for group conversations

- Added PRD for group conversations detailing frontend and backend requirements.
- Created new Baileys TypeScript definitions for group-related functions.
- Renamed `conversation_type` to `group_type` in the database and updated all references.
- Implemented API serialization for `group_type` in conversation and contact responses.
- Developed Vuex store module for managing group members.
- Created UI components for group management, including group creation, member management, and metadata editing.
- Integrated @mention functionality for group conversations and real-time updates via ActionCable.

* feat: [US-001] - Rename conversation_type to group_type on conversations

- Add migration to rename column and indexes
- Update Conversation model enum to group_type
- Update GroupConversationHandler concern
- Update controllers (contacts, group_members)
- Update all backend specs

* chore: mark US-001 complete, update progress log, fix rubocop annotation

* feat: [US-002] - Serialize group_type fields in API responses

* feat: [US-003] - Add group_type filter to conversations index

* feat: [US-004] - Add group_type to filter_keys.yml and FilterService

* feat: US-005 - Backend group creation endpoint

- Add POST /api/v1/accounts/:account_id/groups endpoint
- Add Groups::CreateService to orchestrate Baileys group creation
- Extend WhatsappBaileysService and BaseService with group management methods
- Add routes for group members, metadata, invite, and join requests
- Returns 403 when agent lacks inbox access, 422 when provider is unavailable

* feat: US-006 - Backend add/remove members and role management endpoints

- Add create/destroy/update actions to GroupMembersController
- Delegate group management methods from Channel::Whatsapp to provider_service
- create adds members via Baileys and creates ConversationGroupMember records
- destroy removes a member by ID and sets is_active false
- update promotes/demotes a member and updates their role

* feat: US-007 - Backend group metadata update endpoint

- Add PATCH /contacts/:id/group_metadata endpoint
- Updates group subject via Baileys and syncs contact name
- Updates group description via Baileys and syncs additional_attributes.description
- Returns 422 when provider is unavailable

* feat: US-008 - Backend invite link management endpoints

- Add GET /contacts/:id/group_invite to retrieve current invite code/url
- Add POST /contacts/:id/group_invite/revoke to revoke and get new invite code/url
- Returns 422 when provider is unavailable

* feat: US-009 - Backend join request management endpoints

- Add GET /contacts/:id/group_join_requests to list pending join requests
- Add POST /contacts/:id/group_join_requests/handle to approve/reject requests
- Uses request_action param to avoid conflict with Rails reserved params[:action]
- Returns 422 when provider is unavailable

* feat: US-010 - Extend MentionService for contact mentions

- Extract mention://contact/ID/Name URIs from message content
- Store mentioned contact IDs in message.content_attributes[mentioned_contacts]
- Existing user/team mention handling unchanged

* feat: US-011 - Frontend API clients for all group endpoints

- Add app/javascript/dashboard/api/groupMembers.js
- Exports 11 methods: getGroupMembers, syncGroup, createGroup, updateGroupMetadata,
  addMembers, removeMembers, updateMemberRole, getInviteLink, revokeInviteLink,
  getPendingRequests, handleJoinRequest

* feat: US-012 - Frontend Vuex store module groupMembers

- Add groupMembers store module with fetch, sync, addMembers, removeMembers, updateMemberRole actions
- Add SET_GROUP_MEMBERS and SET_GROUP_MEMBERS_UI_FLAG mutation types
- Register module in store index

* feat: US-013 - Frontend i18n keys for group features

- Add groups.json with keys for group info, filter, creation modal, metadata editing, invite link, member management, join requests, and mention dropdown
- Register groups.json in i18n locale en/index.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: US-014 - Frontend group_type filter in ConversationBasicFilter

- Add chatGroupTypeFilter state, getter, mutation, and action to conversations store
- Add getChatGroupTypeFilter getter
- Add group_type param to ConversationApi.get()
- Add Type filter section to ConversationBasicFilter with All/Individual/Group options
- Persist group_type to UI settings under conversations_filter_by.group_type
- Restore group_type from UI settings on page load
- Include groupType in conversationFilters and pass as group_type param to API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: US-013 - Frontend — i18n keys for group features (en + pt-BR)

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

* feat: [US-014] - Frontend — add group_type filter to ConversationBasicFilter

All implementation was already in place from prior work:
- ConversationBasicFilter.vue has Type section with All/Individual/Group options
- ChatList.vue handles group_type in conversationFilters and restores from UI settings
- Store has setChatGroupTypeFilter action, getChatGroupTypeFilter getter
- API maps groupType → group_type query param

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

* feat: US-015 - Frontend — add group_type to advanced filter system

- Add GROUP_TYPE to CONVERSATION_ATTRIBUTES in filterHelper.js
- Add group_type filter definition in provider.js (components-next)
- Add group_type to legacy advancedFilterItems/index.js and filterAttributeGroups
- Add group_type to automationHelper conditionFilterMaps
- Add group_type to customViewsHelper getValuesForFilter
- Add group_type options to ChatList setParamsForEditFolderModal
- Add GROUP_TYPE i18n key in en and pt_BR advancedFilters.json

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

* feat: US-016 - Frontend — GroupContactInfo basic display

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

* feat: US-017 - Frontend — GroupContactInfo sync button

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

* feat: US-018 - integrate GroupContactInfo in ContactPanel

- Import GroupContactInfo component
- Conditionally render GroupContactInfo when group_type === 'group'
- Keep ContactInfo for individual conversations (no regression)
- Dynamic sidebar title: 'Group' for groups, 'Contact' for individual
- contact_notes and contact_attributes accordion sections unchanged

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

* feat: US-019 - Frontend — group creation UI modal

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

* feat: US-020 - Frontend — member management UI in GroupContactInfo

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

* feat: US-021 - Frontend — group metadata editing UI

Add inline editing for group name, description, and avatar in GroupContactInfo:
- Click group name to edit inline, save on Enter/blur
- Click description to edit inline with textarea, save on blur
- Click avatar to open file picker for upload via contacts/update
- Loading states on all fields during save
- Success/error alerts for all operations
- updateGroupMetadata action added to groupMembers store

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

* feat: US-022 - Frontend — invite link management UI

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

* feat: US-023 - Frontend — join request management UI

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

* feat: US-024 - Frontend — group message bubbles: sender name with color

- Add sender name display above incoming message bubbles in group conversations
- Deterministic color per sender using AVATAR_COLORS palette (name.length % 6)
- Sender name hidden for consecutive messages from the same sender
- Individual conversation bubbles unchanged
- Pass groupWithPrevious and isGroupConversation props through MessageList → Message

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

* feat: US-025 - Frontend — group message bubbles: sender avatar

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

* feat: Add Ralph Wiggum AI agent script for managing tool execution and progress tracking

* feat: US-026 - Frontend — @mention dropdown for group conversations

- Create TagGroupMembers.vue component for group member mention suggestions
- Modify Editor.vue: add isGroupConversation/groupContactId props, render
  TagGroupMembers for group non-private context
- Modify ReplyBox.vue: compute isGroupConversation and groupContactId from
  currentChat, pass to WootMessageEditor
- @ mention plugin isAllowed now triggers for group conversations
- In individual conversations or private notes, existing behavior unchanged

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

* feat: US-027 - Frontend — mention rendering in group message bubbles

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

* feat: US-028 - Frontend ActionCable handler for contact.group_synced event

- Backend: Include group_members data in contact.group_synced ActionCable payload
- Frontend: Register contact.group_synced handler in ActionCableConnector
- Frontend: Add setGroupMembers action to groupMembers store for direct commits
- Tests: ActionCable handler spec + groupMembers store spec for new action

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

* feat: Update progress tracking for group conversations feature - mark tests as passing

* fix: sender click case mismatch and filter dropdown spacing

- Message.vue: use case-insensitive comparison for sender type check
  (Contact.push_event_data returns 'contact' but SENDER_TYPES.CONTACT is 'Contact')
- ConversationBasicFilter.vue: replace last:mt-4 with flex-col gap-4
  for consistent spacing between all three filter sections

* fix: four bugs found during manual testing review

- ContactPanel.vue: fix i18n key GROUP.INFO.SIDEBAR_TITLE → GROUP.SIDEBAR_TITLE
- groupMembers.js API: fix syncGroup HTTP method GET → POST to match backend route
- group_members_controller.rb: remove SyncGroupService from index action
- filterHelpers.js: add missing group_type case to getValueFromConversation

* docs: update progress with bug fix learnings

* chore: implement group creation functionality in UI components

* chore: add copy invite link functionality and update UI components

* feat: US-041 - Backend — ensure group_type is set on existing contacts and conversations

GroupConversationHandler#update_group_contact_info now sets group_type: :group
on contacts that are incorrectly typed as individual.

GroupConversationHandler#find_or_create_group_conversation updates existing
conversation's group_type to :group if it is currently :individual.

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

* chore: mark US-041 as complete

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

* feat: US-029 - i18n keys for You badge and group settings (en + pt-BR)

All i18n keys already existed from prior iterations. Verified presence
and updated PRD status.

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

* feat: US-030 - fix Baileys API route/method mismatches

Fix on_whatsapp to dig('data') before accessing first element.
Update spec stubs to match { data: [...] } response envelope.

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

* feat: US-031 - group_leave, group_setting_update, group_join_approval_mode methods

All methods, delegates, and error handling already implemented.
Verified specs pass.

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

* feat: US-032 - persist group settings, invite code, and profile picture during sync

Add try_update_group_avatar to fetch and attach group profile picture
during sync_group. Update spec stubs for profile-picture-url endpoint.

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

* feat: US-033 - GroupSettingsController with leave, update, toggle

Controller and routes already implemented. Verified rubocop passes.

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

* feat: US-034 - remove inbox_contact_id from provider_config and jbuilder

Already removed in prior iterations. Verified no references remain.

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

* feat: US-035 - refactor TagGroupMembers to phone_number matching

Already implemented. Verified excludePhoneNumber prop and filtering.

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

* feat: US-036 - remove InboxContact.vue and settings tab

Already removed in prior iterations. Verified no references remain.

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

* feat: US-037 - add You badge in GroupContactInfo member list

Already implemented with isOwnMember check and blue badge styling.

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

* feat: US-038 - fix inline edit for group name and description

Already implemented with phone number normalization. Verified code.

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

* feat: US-039 - group settings section UI with toggles

Already implemented. Settings toggles, API calls, and i18n verified.

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

* feat: US-040 - leave group UI with confirmation and auto-resolve

Already implemented. Leave button, confirmation, and API call verified.

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

* feat: US-050 - Create GroupMember model and migration

New group_members table with group_contact_id, contact_id, role, is_active.
Unique index on (group_contact_id, contact_id). Associations added to Contact.

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

* feat: US-064 - Helper method to find channel from group contact

Add Contact#group_channel to decouple channel lookup from conversations.
Update GroupMembersController and GroupSettingsController to use it.

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

* feat: US-052 - Update GroupConversationHandler to use GroupMember

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

* feat: US-057 - Update GroupMembersController to query GroupMember

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

* feat: US-058 - Update GroupSettingsController to not depend on conversations

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

* feat: US-060 - Update group_members jbuilder views

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

* feat: US-059 - Remove group_members association from Conversation model

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

* feat: US-051 - Remove ConversationGroupMember model and table

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

* chore: mark all stories complete, update progress

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

* feat(groups): real-time group panel, avatar refresh on icon change, editable name/description

- Add group_type to Conversations::EventDataPresenter#push_data and
  Contact#push_event_data so WebSocket events carry the field, enabling
  the frontend to switch to GroupContactInfo in real-time
- Update handle_icon_change_stub to call try_update_group_avatar with
  force: true, purging the cached avatar and fetching the new one
- Add force parameter to try_update_group_avatar to support re-fetching
- Remove isInboxAdmin gate from name/description editing in
  GroupContactInfo so any user can click to edit (server validates)

* fix(groups): rewrite SyncGroupService and simplify group metadata channel lookup

- Rewrite SyncGroupService to use contact.group_channel directly instead
  of iterating conversations; find or create a conversation for sync
- Simplify GroupMetadataController to use @contact.group_channel instead
  of querying conversations; remove local contact/attribute updates since
  the Baileys API handles persistence via webhook events

* feat(groups): resolve conversations when inbox phone leaves or is removed

- Add resolve_conversations_if_inbox_left to GroupParticipantsUpdate
- Resolves all open/pending conversations when the inbox phone number
  is removed from or leaves a group

* feat(groups): add paginated member list with infinite scroll

- Backend: add pagination to GroupMembersController (page/per_page,
  default 10, ordered admins first); add meta with pagination info
  to the jbuilder response
- Frontend: update groupMembers API to accept page param; add
  APPEND_GROUP_MEMBERS and SET_GROUP_MEMBERS_META mutations; implement
  paginated fetch with append and isFetchingMore flag in store

* feat(groups): support Ctrl+Click on group message sender to open in new tab

- navigateToGroupSender now accepts the event and checks for
  Ctrl/Cmd+Click to open the sender contact in a new tab

* chore(i18n): update leave group confirmation text in en and pt_BR

* fix(groups): handle phone format differences in You badge and admin detection

- Extract phonesMatch helper that compares last 8 digits as fallback,
  handling Brazilian 9th digit discrepancy (e.g. +5587988465072 vs
  +558788465072)
- Apply to both isOwnMember and isInboxAdmin computed properties

* feat(groups): auto-sync members on mount, show existing members immediately

- On mount, fetch existing DB members first so they display instantly
- Then silently attempt a background sync to refresh from WhatsApp
- If sync fails (e.g. WhatsApp disconnected), existing members remain
  displayed without any user-facing error

* fix(groups): pin own member on first page and return inbox phone in meta

The "You" badge was not appearing because the inbox's own member could be
missing from the first paginated page in large groups (admins sorted first).

Backend:
- Pin the inbox's own member at the top of page 1 regardless of sort order
- Return inbox_phone_number in the group members meta response
- Use last-8-digit SQL fallback for Brazilian 9th-digit phone mismatches

Frontend:
- Use meta.inbox_phone_number for the inboxPhone computed
- Fix declaration order to satisfy no-use-before-define lint rule

* fix(groups): fix member action dropdown clipped by overflow container

The promote/demote/remove dropdown menu was invisible because the member
list had `overflow-y-auto max-h-80`, clipping any absolutely-positioned
dropdown rendered inside it.

- Remove overflow container from member list; let the sidebar scroll
- Replace scroll-based infinite loading with IntersectionObserver on a
  sentinel element for cleaner pagination trigger
- All member action logic (promote, demote, remove) was already wired;
  the dropdown is now visible on hover

* fix(groups): keep member action dropdown visible when menu is open

The opacity-0/group-hover classes on the action menu wrapper caused the
DropdownMenu to become invisible as soon as the mouse left the row.
Now the wrapper stays fully opaque while the menu is active.

* fix(groups): move clickaway to member list wrapper to prevent instant close

v-on-clickaway was bound to every member's action div individually.
Clicking the three-dot button on one member fired closeMemberMenu from
all other members' clickaway handlers, closing the menu instantly.

Moved the directive to the single member list container instead.

* feat: add WhatsApp mention conversion (incoming + outgoing)

- New MentionConverterService for bidirectional mention handling
- Incoming: converts @phone/mentionedJid to mention://contact/ URIs
- Outgoing: extracts mention://contact/ URIs into WhatsApp mentions array
- Supports @everyone/todos group mentions
- WhatsApp renderer preserves mention display text instead of raw URI

* fix: preserve mention display text in WhatsApp renderer

mention:// URIs now render as display name text instead of the raw URL
when converting markdown to WhatsApp format

* feat: add @everyone mention option in group conversations

- Everyone item shown at top of mention dropdown
- Searchable by 'all', 'todos', 'everyone' keywords
- i18n keys added for en and pt-BR

* refactor: use Switch component for group settings toggles

- Add disabled prop to Switch component
- Replace custom toggle buttons in GroupContactInfo with Switch
- Loading spinner shown alongside toggle while toggling

* feat(whatsapp): add group sync status tracking (group_left, group_last_synced_at)

* feat(whatsapp): hide group management UI when group_left is true

* fix(groupMembers): include inbox phone number in group members state and sync event

* feat(whatsapp): wrap group settings and leave in Accordion component

* feat(groupMembers): handle group creator modification errors and update error messages

* feat(groupMembers): enhance invite link functionality and clean up UI state after copying

* refactor: remove sync_group functionality from conversations and related specs

* feat(GroupContactInfo): implement scroll-based loading for group members

* docs(swagger): add group API endpoints and remove conversation sync_group

- Remove dead conversation/{id}/sync_group swagger entry and file
- Update group_members.yml with pagination params, POST operation, and $ref schema
- Add swagger for: group_members_member (PATCH/DELETE), group_metadata,
  group_invite, group_invite_revoke, group_join_requests,
  group_join_requests_handle, group_settings, group_settings_leave,
  group_settings_toggle_join_approval, groups/create
- Add group_member schema definition
- Add Groups tag to application tag_groups
- Register all 12 group endpoints in paths/index.yml

* feat(WhatsappBaileysService): enhance mention handling by replacing @DisplayName with @lid/@phone in outgoing text

* feat(groups): move group sync to background job with 15-min cooldown

- Create Contacts::SyncGroupJob that checks group_last_synced_at
  before calling SyncGroupService (skips if < 15 min)
- Controller sync_group now enqueues the job and returns 202 Accepted
- Delete sync_group.json.jbuilder (no longer needed)
- Frontend sync action is fire-and-forget; results via ActionCable
- Auto-trigger sync on conversation select and panel mount
- Remove manual sync button from GroupContactInfo

* fix: show group members list even after leaving group\n\nKeep the members section visible in read-only mode when\ngroup_left is true. Admin actions (add member, promote,\ndemote, remove) remain hidden. Pending Join Requests and\nAdvanced Options also stay hidden.

* fix: disable group name/description/avatar editing when group_left is true

* fix: remove @all mention and fix Enter key in group mention dropdown\n\n- Remove the @all/everyone special mention from TagGroupMembers since\n  no channel provider currently supports mentioning all participants\n- Fix Enter key sending message instead of inserting selected mention\n  in group conversations. The root cause was Editor.vue only emitting\n  toggleUserMention=true for private notes (isPrivate), leaving\n  ReplyBox unaware the group mention dropdown was open. Now also\n  emits for isGroupConversation.\n- Add TagGroupMembers spec covering filtering, exclusion, and emission"

* fix: address PR review feedback for group conversations

- Fix nil safety in group_invites and group_join_requests controllers
  by replacing group_conversation.inbox.channel with @contact.group_channel
- Add before_action guard in group_members_controller to validate
  contact is a group with identifier before create/update/destroy
- Persist metadata locally in group_metadata_controller after
  provider calls (subject -> name, description -> additional_attributes)
- Add server-side allow_group_creation? check in groups_controller
- Add word boundary to mention regex to prevent matching inside words
- Remove useless catch clauses in groupMembers store (try/finally only)
- Default groupType to [] in customViewsHelper to prevent crash
- Fix swagger parameter name mismatch (contact_id -> id) across
  all group endpoint YML files for consistency

* fix: address PR #228 review feedback - strong params, guards, and safety fixes

* fix: dispatch real-time events for Baileys group participant and metadata updates

Both group-participants.update and groups.update handlers were updating
backend data (GroupMember records, Contact attributes) but never
dispatching ActionCable events, leaving the frontend member list and
group metadata stale until manual sync.

Changes:
- Add dispatch_group_synced_event helper to GroupEventHelper concern
- Dispatch CONTACT_GROUP_SYNCED after participant add/remove/promote/demote
- Dispatch CONTACT_GROUP_SYNCED after group subject/description/settings changes
- Frontend: onContactGroupSynced also dispatches contacts/updateContact
  to refresh group name, description and settings in the sidebar

* fix: enhance member menu positioning and close behavior on sidebar scroll

* feat: implement group property updates and enhance toast notifications

* fix: update WhatsApp channel regex to allow optional hyphenated numbers

* feat: implement group admin functionalities including leave, update properties, and toggle join approval

* refactor: simplify group message handling by removing metadata fetching and syncing methods

* chore: remove raph files

* feat: update Portuguese translations for 'Read More' and 'Insert Read More' phrases

* feat: enhance group admin functionalities with join approval and member add modes

* feat: enhance group join request handling by adding removal of handled requests and updating pending join requests

* feat: restrict message sending in announcement mode groups

When a Baileys WhatsApp group has announcement mode enabled (announce=true),
only admin members can send messages. This adds:
- Frontend: disabled editor + banner for non-admin inbox in announcement groups
- Backend: validation in SendOnWhatsappService to reject messages
- Shared phone helper utility extracted from GroupContactInfo
- i18n keys for en and pt_BR

* feat: add group sync job enqueueing and improve avatar update handling

* feat: add functionality to reset invite link and confirm member addition restrictions

* feat: update group name extraction logic to handle nil values

* feat: add inbox admin status handling and update related components

* feat: remove group conversation resolution on leave action

* feat: enhance group sender avatar interaction with tooltip and cursor pointer

* feat: add force option to SyncGroupJob and update related specs

* feat: enhance invite link handling and avatar update logic in group conversations

* chore: remove prd.json

* fix: change group sender name display from block to inline-block for better layout

* feat: add group members loading check and fetch logic in MessagesView and ReplyBox components

* feat: allow id and firstUnreadId props to accept both Number and String types
feat: add vOnClickOutside import to Editor component
feat: enhance Portuguese translations for integrations and settings
fix: change button color in GroupContactInfo component from green to teal

* feat: soft-disabled group conversations with activity tracking

Groups start in a soft-disabled state by default when using Baileys.
Chatwoot still creates group conversations but does not process every
incoming message. Instead, Baileys accumulates group messages and sends
periodic groups.activity webhook events to update last_activity_at.

Backend:
- Add WHATSAPP_GROUPS_ENABLED env var and groups_enabled? class method
- Send groupsEnabled in Baileys connection setup
- Create groups.activity handler to update conversation last_activity_at
- Gate group message processing behind groups_enabled? check
- Expose groups_enabled via inbox API

Frontend:
- Add warning banner with CTA to app.fazer.ai on disabled group conversations
- Disable reply editor for non-private-note mode when groups disabled
- Add i18n strings for en and pt_BR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use method for groups disabled banner action to avoid window scope issue

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: broadcast conversation update after groups.activity event

update_columns bypasses ActiveRecord callbacks, so the ActionCable
broadcast was never triggered when last_activity_at changed. Dispatch
a CONVERSATION_UPDATED event explicitly so the sidebar updates in
real-time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: show unread dot for soft-disabled group conversations with activity

Since soft-disabled groups don't create messages, unread_count is
always 0 and the standard badge won't show. Detect unread state by
comparing last_activity_at > agent_last_seen_at for these groups
and display a teal dot indicator instead of a count badge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: clear unread dot when agent opens soft-disabled group conversation

The update_last_seen endpoint skipped updating agent_last_seen_at when
there were no unread messages (the throttle path). For soft-disabled
groups that never create messages, this meant the dot indicator could
never be cleared. Add an unseen_activity? check that bypasses the
throttle when last_activity_at > agent_last_seen_at.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: group avatar upload to provider and fix icon change sync

- Route avatar upload through GroupMetadataController to push to WhatsApp
  provider before saving locally
- Add update_group_picture to baileys service and base service
- Fix buildContactFormData crash when social_profiles is undefined
- Make try_update_group_avatar public so GROUP_CHANGE_ICON stub handler
  can call it from outside the service class

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update specs for group conversations feature changes

- Add groupsEnabled param to setup_channel_provider and handle_channel_error WebMock stubs
- Add group-request-participants-list stub for sync_group tests
- Add group_type to push_event_data expected hash
- Set last_activity_at in throttle tests to prevent unseen_activity? bypass
- Update sync_group delegation expectation to include soft: false
- Stub groups_enabled? in group message handling tests
- Update WhatsApp source_id regex expectation for group contact IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add settings file for additional directories configuration

* chore: undo unrelated changes

* chore: remove planning doc, fix migration version, fix swagger param consistency

- Remove planejamento-chat-interno.md (unrelated planning document)
- Fix CreateGroupMembers migration API version from 7.0 to 7.1
- Fix swagger.json: normalize group endpoint paths from {contact_id} to {id}
  to match YAML sources and existing contact sub-resource conventions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: CayoPOliveira <cayoproliveira@gmail.com>
Co-authored-by: Cayo P. R. Oliveira <cayo@fazer.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 21:56:58 -03:00
Sivin Varghese
7b2b3ac37d
feat(V5): Update settings pages UI (#13396)
# Pull Request Template

## Description

This PR updates settings page UI


## Type of change

- [x] New feature (non-breaking change which adds functionality)


## 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
2026-02-19 15:04:40 +05:30
gabrieljablonski
9a4c5058f3 Merge branch 'main' into chore/merge-upstream-4.11.0 2026-02-17 23:05:26 -03:00
Sivin Varghese
229f56d6e3
chore: Remove vue-multiselect and migrate to next components (#13506)
# Pull Request Template

## Description

This PR includes:
1. Removes multiselect usage from the Merge Contact modal (Conversation
sidebar) and replaces it with the existing component used on the Contact
Details page.
2. Replaces legacy form and multiselect elements in Add and Edit
automations flows with next components.**(Also check Macros)**
3. Replace multiselect with ComboBox in contact form country field.
4. Replace multiselect with TagInput in create/edit attribute form.
5. Replace multiselect with TagInput for agent selection in inbox
creation.
6. Replace multiselect with ComboBox in Facebook channel page selection

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

**Screenshots**

1. **Merge modal**
<img width="741" height="449" alt="image"
src="https://github.com/user-attachments/assets/a05a96ec-0692-4d94-9e27-d3e85fd143e4"
/>
<img width="741" height="449" alt="image"
src="https://github.com/user-attachments/assets/fc1dc977-689d-4440-869d-2124e4ca9083"
/>

2. **Automations**
<img width="849" height="1089" alt="image"
src="https://github.com/user-attachments/assets/b0155f06-ab21-4f90-a2c8-5bfbd97b08f7"
/>
<img width="813" height="879" alt="image"
src="https://github.com/user-attachments/assets/0921ac4a-88f5-49ac-a776-cc02941b479c"
/>
<img width="849" height="826" alt="image"
src="https://github.com/user-attachments/assets/44358dae-a076-4e10-b7ba-a4e40ccd817f"
/>

3. **Country field**
<img width="462" height="483" alt="image"
src="https://github.com/user-attachments/assets/d5db9aa1-b859-4327-9960-957d7091678f"
/>

4. **Add/Edit attribute form**
<img width="619" height="646" alt="image"
src="https://github.com/user-attachments/assets/6ab2ea94-73e5-40b8-ac29-399c0543fa7b"
/>
<img width="619" height="646" alt="image"
src="https://github.com/user-attachments/assets/b4c5bb0e-baa0-4ef7-a6a2-adb0f0203243"
/>
<img width="635" height="731" alt="image"
src="https://github.com/user-attachments/assets/74890c80-b213-4567-bf5f-4789dda39d2d"
/>

5. **Agent selection in inbox creation**
<img width="635" height="534" alt="image"
src="https://github.com/user-attachments/assets/0003bad1-1a75-4f20-b014-587e1c19a620"
/>
<img width="809" height="602" alt="image"
src="https://github.com/user-attachments/assets/5e7ab635-7340-420a-a191-e6cd49c02704"
/>

7. **Facebook channel page selection**
<img width="597" height="444" alt="image"
src="https://github.com/user-attachments/assets/f7ec8d84-0a7d-4bc6-92a1-a1365178e319"
/>
<img width="597" height="444" alt="image"
src="https://github.com/user-attachments/assets/d0596c4d-94c1-4544-8b50-e7103ff207a6"
/>
<img width="597" height="444" alt="image"
src="https://github.com/user-attachments/assets/be097921-011b-4dbe-b5f1-5d1306e25349"
/>



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

---------

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2026-02-17 16:40:12 +05:30
Aakash Bakhle
101eca3003
feat: add captain editor events (#13524)
## Description

Adds missing analytics instrumentation for the editor AI funnel so we
can measure end-to-end usage and outcome quality.

### What was added

- Captain: Editor AI menu opened
- Captain: Generation failed
- Captain: AI-assisted message sent

### Behavior covered

- Tracks AI button click + menu open from both entry points:
    - top panel sparkle button
    - inline editor copilot button
- Tracks generation failures (initial + follow-up stages).
- Tracks whether accepted AI content was sent as-is or edited before
send.

### Notes

- Applies to editor Captain accept/send flow
(rewrite/summarize/reply_suggestion + follow-ups).
- Does not change Copilot sidebar flow instrumentation.

## Type of change

- [x] 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?

### Manual verification steps

<img width="1906" height="832" alt="image"
src="https://github.com/user-attachments/assets/f0ade43b-aa8d-41be-8ca2-20a091a81f60"
/>

<img width="828" height="280" alt="image"
src="https://github.com/user-attachments/assets/be76219e-fb61-4a6e-bff5-dc085b0a3cc9"
/>

<img width="415" height="147" alt="image"
src="https://github.com/user-attachments/assets/36802c5c-33a7-49ed-bf7e-f0b02d86dccc"
/>

<img width="2040" height="516" alt="image"
src="https://github.com/user-attachments/assets/74b95288-bc86-4312-a282-14211ae8f25c"
/>


1. Open a conversation with Captain tasks enabled.
2. Click AI button in top panel and inline editor.
3. Confirm analytics events fire for:
    - AI menu opened
4. Run an AI action and force a failure scenario (or empty response
path) and confirm generation-failed event.
5. Accept AI output, then:
    - send without changes -> editedBeforeSend: false
    - edit then send -> editedBeforeSend: true

## 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
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
2026-02-17 13:26:56 +05:30
Gabriel Jablonski
614b887110
feat: enhance scheduled message + improved automations (#208)
* feat: enhance scheduled message delay handling

* fix: update scheduled message delay validation

* feat: message templates + liquid variables

* feat: enhance validation and handling for scheduled messages and attachments

* test: add validation for scheduled message creation and enhance Liquid variable processing

* chore: define constants for scheduled message delay limits and update validations

* chore: correct scheduled message delay constants for consistency

* feat: enhance scheduled message validation and processing for templates
2026-02-03 15:42:31 -03:00
Shivam Mishra
133fb1bcf6
feat: add mark pending action to automation (#13378) 2026-02-02 11:59:51 +05:30
Cayo P. R. Oliveira
f9d1146cb0
feat: mensagens agendadas (#198)
* feat:  Adds model for scheduling messages

* feat: Implement scheduled message handling and processing jobs

* feat: Add ScheduledMessagesController and associated specs for managing scheduled messages

* refactor: Simplify scheduled message job specs and improve metadata handling

* feat: Add ScheduledMessagePolicy for managing access to scheduled messages

* feat: Add routes for managing scheduled messages

* feat: Add scheduled message event handling and broadcasting

* feat: Add JSON views for scheduled messages creation, destruction, updating, and indexing

* feat: Update scheduled message status and dispatch update event after message creation

* feat: Ensure scheduled message updates trigger dispatch event

* feat: Add mutation types for managing scheduled messages

* feat: Add additionalAttributes prop to Message component and provider

* feat: Implement scheduled message handling in ActionCable and Vuex store

* feat: Add unit tests for scheduled messages actions and mutations

* feat: implement scheduled messages functionality

- Added support for scheduling messages in the conversation dashboard.
- Introduced new components: ScheduledMessageModal and ScheduledMessages for managing scheduled messages.
- Enhanced ReplyBottomPanel to include scheduling options.
- Updated Base.vue to handle scheduled message styling.
- Integrated Vuex store module for managing scheduled messages state.
- Added necessary translations for scheduled messages in English and Portuguese.

* feat: add pagination to scheduled messages index and update tests accordingly

* chore: update scheduled messages specs for future time validation and response status

* chore: enhance scheduled messages API with pagination and add skeleton loader component

* feat: add create_scheduled_message action to automation rule attributes

* feat: implement create_scheduled_message action and enhance attachment handling

* feat: add scheduled message functionality with UI components and localization

* test: enhance scheduledMessages mutations tests with meta handling and structure

* chore: update label to display file name upon successful upload in AutomationFileInput component

* feat: add initialAttachment prop to ScheduledMessageModal and update ReplyBox to pass attachment

* chore: prepend_mod_with to ScheduledMessagesController for better module handling

* fix: attachment visibility in ScheduledMessageItem component

* chore: enhance ScheduledMessage model with validations and reduce controller load

* refactor: simplify ScheduledMessagesAPI methods by removing unnecessary instance variable

* chore: update event emission for scheduled message creation in ReplyBox and ScheduledMessageModal

* refactor: update status configuration to use label keys

* chore: update date formatting in ScheduledMessageItem component

* refactor: collapse logic to checkOverflow and update related functionality

* chore: add author indication for current user in scheduled messages

* chore: enhance scheduled message metadata with author information and localization

* fix: send message shortcut

* chore: handle errors in scheduled message submission

* chore: update scheduled message modal to use combined date and time input

* chore: refactor scheduled messages handling to remove pagination and update related tests

* fix: ensure scheduled messages update status and dispatch on failure

* fix: update scheduled message due date logic and simplify sending checks

* refactor: rename build_message method for send_message

* fix: update scheduled message creation time and improve test reliability

* chore: ignore unnecessary check

* chore: add scheduled message metadata handling  in message builder, add scheduled message factorie and update specs

* refactor: use scheduled message factorie creation in specs

* chore: streamline error handling in scheduled message job and remove dispatch logic

* fix: change scheduled_messages association to destroy dependent records

* refactor: remove unused attributes from scheduled message payload builder

* chore: update scheduled message retrieval to use conversation association

* chore: correct cron format for scheduled messages job

* chore: remove migration for author_type in scheduled_messages

* feat: enhance scheduled messages management with delete confirmation and error handling

* chore: set cron poll interval to 10 seconds for improved scheduling precision

* feat: include additional_attributes in message JSON response

* feat: enhance scheduled message validation and localization support

* chore: update scheduled message display

* Merge branch 'main' into Cayo-Oliveira/CU-86aenh268/Mensagens-agendadas

* feat: add scheduled message indicators and validation for message length

* fix: remove unnecessary condition from line-clamp class binding

* feat: update scheduled messages localization and enhance content validation

* feat: update scheduled messages order, enhance scheduledAt computation, and add message association

* fix: reorder condition for Facebook channel message length computation

* fix:  change detection for attachments in scheduled messages

* fix: remove unnecessary colon from close-on-backdrop-click prop in ScheduledMessageModal

* chore: add error handling for scheduled message deletion and update localization for delete failure

* fix: enforce minimum delay of 1 minute for scheduled messages and update validation

* fix: remove unused private property and improve locale formatting for scheduled messages

* fix: adjust positioning of DropdownBody in ReplyBottomPanel and clean up schema foreign keys

* docs: add scheduled messages management APIs and payload definitions

---------

Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2026-01-30 22:08:16 -03:00
Shivam Mishra
8eb6fd1bff
feat: track copilot events (#13342) 2026-01-22 18:38:04 +05:30
Shivam Mishra
6a482926b4
feat: new Captain Editor (#13235)
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: aakashb95 <aakashbakhle@gmail.com>
2026-01-21 13:39:07 +05:30
gabrieljablonski
6ab1898992 Merge branch 'main' into chore/merge-upstream-4.10 2026-01-16 14:01:53 -03:00
Sivin Varghese
1a220b2982
chore: Improve compose new conversation form (#13176)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2026-01-13 18:52:10 +05:30
Sojan Jose
9407cc2ad5 Merge branch 'hotfix/4.9.2' into develop 2026-01-12 09:15:23 -08:00
Shivam Mishra
58cec84b93
feat: sanitize html before assiging it to tempDiv (#13252) 2026-01-12 22:41:37 +05:30
Shivam Mishra
34b42a1ce1
feat: add global config for captain settings (#13141)
Co-authored-by: aakashb95 <aakashbakhle@gmail.com>
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
2026-01-12 19:54:19 +05:30
Sivin Varghese
a2e348df06
fix: Backslash issue with -- and improve autolink handling (#13208) 2026-01-12 11:22:44 +05:30
Pranav
837026c146
feat: Use amplitude for Cloud Analytics (#13217)
Migrates our analytics integration on Cloud from PostHog to Amplitude.
This change updates the core AnalyticsHelper class to use the Amplitude
SDK while maintaining the same tracking interface. Rest of all existing
analytics calls throughout the codebase continue to work without
modification.

**Changes:**
- Replace PostHog analytics with Amplitude SDK
- Rename ANALYTICS_TOKEN to CLOUD_ANALYTICS_TOKEN for clarity
- Fix bug in page() method signature that was causing malformed payloads
2026-01-09 09:32:09 -08:00
Sivin Varghese
ed0e87405c
fix: Strip autolinks <...> when links are not supported (#13204)
# Pull Request Template

## Description

This PR fixes the crash that occurred when inserting canned responses
containing **autolinks** (e.g. `<https://example.com>`) into reply
channels that **do not support links**, such as **Twilio SMS**.

### Steps to reproduce

1. Create a canned response with an autolink, for example:
`<https://example.com>`.
2. Open a conversation in a channel that does not support links (e.g.
SMS).
3. Insert the canned response into the reply box.

### Cause

* Currently, only standard markdown links (`[text](url)`) are handled
when stripping unsupported formats from canned responses.
* Autolinks (`<https://example.com>`) are not handled during this
process.
* As a result, **Error: Token type link_open not supported by Markdown
parser**

### Solution

* Extended the markdown link parsing logic to explicitly handle
**autolinks** in addition to standard markdown links.
* When a canned response containing an autolink is inserted into a reply
box for a channel that does not support links (e.g. SMS), the angle
brackets (`< >`) are stripped.
* The autolink is safely pasted as **plain text URL**, preventing parser
errors and editor crashes.



Fixes
https://linear.app/chatwoot/issue/CW-6256/error-token-type-link-open-not-supported-by-markdown-parser
Sentry issues
[[1](https://chatwoot-p3.sentry.io/issues/7103543778/?environment=production&project=4507182691975168&query=is%3Aunresolved%20markdown&referrer=issue-stream)],
[[2](https://chatwoot-p3.sentry.io/issues/7104325962/?environment=production&project=4507182691975168&query=is%3Aunresolved%20markdown&referrer=issue-stream
)]


## 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
- [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
2026-01-08 18:37:52 +04:00
Sivin Varghese
92422f979d
chore: Replace plain editor with advanced editor (#13071)
# Pull Request Template

## Description

This PR reverts the plain text editor back to the **advanced editor**,
which was previously removed in
[https://github.com/chatwoot/chatwoot/pull/13058](https://github.com/chatwoot/chatwoot/pull/13058).
All channels now use the **ProseMirror editor**, with formatting applied
based on each channel’s configuration.

This PR also fixes issues where **new lines were not properly preserved
during Markdown serialization**, for both:

* `Enter or CMD/Ctrl+enter` (new paragraph)
* `Shift+Enter` (`hard_break`)

Additionally, it resolves related **[Sentry
issue](https://chatwoot-p3.sentry.io/issues/?environment=production&project=4507182691975168&query=is%3Aunresolved%20markdown&referrer=issue-list&statsPeriod=7d)**.

With these changes:

* Line breaks and spacing are now preserved correctly when saving canned
responses.
* When editing a canned response, the content retains the exact spacing
and formatting as saved in editor.
* Canned responses are now correctly converted to plain text where
required and displayed consistently in the canned response list.

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

---

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?



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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2026-01-08 15:17:54 +05:30
gabrieljablonski
549214e96d Merge branch main into chore/merge-upstream 2025-12-20 12:44:31 -03:00
Sojan Jose
c22a31c198
feat: Voice Channel (#11602)
Enables agents to initiate outbound calls and receive incoming calls
directly from the Chatwoot dashboard, with Twilio as the initial
provider.

Fixes:  #11481 

> This is an integration branch to ensure features works well and might
be often broken on down merges, we will be extracting the
functionalities via smaller PRs into develop

- [x] https://github.com/chatwoot/chatwoot/pull/11775
- [x] https://github.com/chatwoot/chatwoot/pull/12218
- [x] https://github.com/chatwoot/chatwoot/pull/12243
- [x] https://github.com/chatwoot/chatwoot/pull/12268
- [x] https://github.com/chatwoot/chatwoot/pull/12361
- [x]  https://github.com/chatwoot/chatwoot/pull/12782
- [x] #13064
- [ ] Ability for agents to join the inbound calls ( included in this PR
)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
2025-12-19 12:41:33 -08:00
Mazen Khalil
ca5e112a8c
feat: TikTok channel (#12741)
fixes: #11834

This pull request introduces TikTok channel integration, enabling users
to connect and manage TikTok business accounts similarly to other
supported social channels. The changes span backend API endpoints,
authentication helpers, webhook handling, configuration, and frontend
components to support TikTok as a first-class channel.


**Key Notes**
* This integration is only compatible with TikTok Business Accounts
* Special permissions are required to access the TikTok [Business
Messaging
API](https://business-api.tiktok.com/portal/docs?id=1832183871604753).
* The Business Messaging API is region-restricted and is currently
unavailable to users in the EU.
* Only TEXT, IMAGE, and POST_SHARE messages are currently supported due
to limitations in the TikTok Business Messaging API
* A message will be successfully sent only if it contains text alone or
one image attachment. Messages with multiple attachments or those
combining text and attachments will fail and receive a descriptive error
status.
* Messages sent directly from the TikTok App will be synced into the
system
* Initiating a new conversation from the system is not permitted due to
limitations from the TikTok Business Messaging API.


**Backend: TikTok Channel Integration**

* Added `Api::V1::Accounts::Tiktok::AuthorizationsController` to handle
TikTok OAuth authorization initiation, returning the TikTok
authorization URL.
* Implemented `Tiktok::CallbacksController` to handle TikTok OAuth
callback, process authorization results, create or update channel/inbox,
and handle errors or denied scopes.
* Added `Webhooks::TiktokController` to receive and verify TikTok
webhook events, including signature verification and event dispatching.
* Created `Tiktok::IntegrationHelper` module for JWT-based token
generation and verification for secure TikTok OAuth state management.

**Configuration and Feature Flags**

* Added TikTok app credentials (`TIKTOK_APP_ID`, `TIKTOK_APP_SECRET`) to
allowed configs and app config, and registered TikTok as a feature in
the super admin features YAML.
[[1]](diffhunk://#diff-5e46e1d248631a1147521477d84a54f8ba6846ea21c61eca5f70042d960467f4R43)
[[2]](diffhunk://#diff-8bf37a019cab1dedea458c437bd93e34af1d6e22b1672b1d43ef6eaa4dcb7732R69)
[[3]](diffhunk://#diff-123164bea29f3c096b0d018702b090d5ae670760c729141bd4169a36f5f5c1caR74-R79)

**Frontend: TikTok Channel UI and Messaging Support**

* Added `TiktokChannel` API client for frontend TikTok authorization
requests.
* Updated channel icon mappings and tests to include TikTok
(`Channel::Tiktok`).
[[1]](diffhunk://#diff-b852739ed45def61218d581d0de1ba73f213f55570aa5eec52aaa08f380d0e16R16)
[[2]](diffhunk://#diff-3cd3ae32e94ef85f1f2c4435abf0775cc0614fb37ee25d97945cd51573ef199eR64-R69)
* Enabled TikTok as a supported channel in contact forms, channel
widgets, and feature toggles.
[[1]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R47)
[[2]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R69)
[[3]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R26-R29)
[[4]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R51-R54)
[[5]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R68)
* Updated message meta logic to support TikTok-specific message statuses
(sent, delivered, read).
[[1]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696R23)
[[2]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L63-R65)
[[3]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L81-R84)
[[4]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L103-R107)
* Added support for embedded message attachments (e.g., TikTok embeds)
with a new `EmbedBubble` component and updated message rendering logic.
[[1]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR31)
[[2]](diffhunk://#diff-047859f9368a46d6d20177df7d6d623768488ecc38a5b1e284f958fad49add68R1-R19)
[[3]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR316)
[[4]](diffhunk://#diff-cbc85e7c4c8d56f2a847d0b01cd48ef36e5f87b43023bff0520fdfc707283085R52)
* Adjusted reply policy and UI messaging for TikTok's 48-hour reply
window.
[[1]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R208-R210)
[[2]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R224-R226)

These changes collectively enable end-to-end TikTok channel support,
from configuration and OAuth flow to webhook processing and frontend
message handling.


------------

# TikTok App Setup & Configuration
1. Grant access to the Business Messaging API
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832184145137922))
2. Set the app authorization redirect URL to
`https://FRONTEND_URL/tiktok/callback`
3. Update the installation config with TikTok App ID and Secret
4. Create a Business Messaging Webhook configuration and set the
callback url to `https://FRONTEND_URL/webhooks/tiktok`
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832190670631937))
. You can do this by calling
`Tiktok::AuthClient.update_webhook_callback` from rails console once you
finish Tiktok channel configuration in super admin ( will be automated
in future )
5. Enable TikTok channel feature in an account

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
2025-12-17 07:54:50 -08:00
Pranav
b6a14bda48
chore: Enable YearInReview for everyone, include analytics (#13090)
Remove the query param condition and enable it for everyone.
2025-12-16 18:20:10 -08:00
Sivin Varghese
df4c8cf58b
chore: Strip unsupported signature formatting by channel (#13046)
# Pull Request Template

## Description

1. This PR is an enhancement to
https://github.com/chatwoot/chatwoot/pull/13045
It strips unsupported formatting from **message signatures** based on
each channel’s formatting capabilities defined in the `FORMATTING`
config

2. Remove usage of plain editor in Compose new conversation modal

Only the following signature elements are considered:
<strong>bold (<code inline="">strong</code>), italic (<code
inline="">em</code>), links (<code inline="">link</code>), images (<code
inline="">image</code>)</strong>.</p>

Any formatting not supported by the target channel is automatically
removed before the signature is appended.

<h3>Channel-wise Signature Formatting Support</h3>

Channel | Keeps in Signature | Strips from Signature
-- | -- | --
Email | bold, italic, links, images | —
WebWidget | bold, italic, links, images | —
API | bold, italic | links, images
WhatsApp | bold, italic | links, images
Telegram | bold, italic, links | images
Facebook | bold, italic | links, images
Instagram | bold, italic | links, images
Line | bold, italic | links, images
SMS | — | everything
Twilio SMS | — | everything
Twitter/X | — | everything


<hr>
<h3>📝 Note</h3>
<blockquote>
<p>Message signatures only support <strong>bold, italic, links, and
images</strong>.<br>
Other formatting options available in the editor (lists, code blocks,
strike-through, etc.) do <strong>not apply</strong> to signatures and
are ignored.</p>
</blockquote>

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video
https://www.loom.com/share/d325ab86ca514c6d8f90dfe72a8928dd


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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-12-11 19:58:59 +05:30
Sivin Varghese
f2054e703a
fix: Handle rich message signatures & attachment overflow (#13045) 2025-12-10 23:13:04 +05:30
Gabriel Jablonski
7e2ad3b3e1
fix: handle potential null values in automation type retrieval (#163)
* fix: handle potential null values in automation type retrieval

* fix: handle cases where custom attribute keys are not found, returning default values
2025-12-09 12:59:48 -03:00
Sivin Varghese
863c033699
fix: Strip unsupported markdown formatting from canned responses (#13028)
# Pull Request Template

## Description

This PR fixes, 
1. **Issue with canned response insertion** - Canned responses with
formatting (bold, italic, code, lists, etc.) were not being inserted
into channels that don't support that formatting.
Now unsupported markdown syntax is automatically stripped based on the
channel's schema before insertion.
2. **Make image node optional** - Images are now stripped while paste.
9e269fca04
3. Enable **bold** and _italic_ for API channel

Fixes
https://linear.app/chatwoot/issue/CW-6091/editor-breaks-when-inserting-canned-response

## 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/9a5215dfef2949fcaa3871f51bdec4bb


## 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
2025-12-09 09:15:35 +05:30
Sivin Varghese
399c91adaa
feat: Standardize rich editor across all channels (#12600)
# Pull Request Template

## Description

This PR includes,

1. **Channel-specific formatting and menu options** for the rich reply
editor.
2. **Removal of the plain reply editor** and full **standardization** on
the rich reply editor across all channels.
3. **Fix for multiple canned responses insertion:**
* **Before:** The plain editor only allowed inserting canned responses
at the beginning of a message, making it impossible to combine multiple
canned responses in a single reply. This caused inconsistent behavior
across the app.
* **Solution:** Replaced the plain reply editor with the rich
(ProseMirror) editor to ensure a unified experience. Agents can now
insert multiple canned responses at any cursor position.
4. **Floating editor menu** for the reply box to improve accessibility
and overall user experience.
5. **New Strikethrough formatting option** added to the editor menu.

---

**Editor repo PR**:
https://github.com/chatwoot/prosemirror-schema/pull/36

Fixes https://github.com/chatwoot/chatwoot/issues/12517,
[CW-5924](https://linear.app/chatwoot/issue/CW-5924/standardize-the-editor),
[CW-5679](https://linear.app/chatwoot/issue/CW-5679/allow-inserting-multiple-canned-responses-in-a-single-message)

## Type of change

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

## How Has This Been Tested?

### Screenshot
**Dark**
<img width="850" height="345" alt="image"
src="https://github.com/user-attachments/assets/47748e6c-380f-44a3-9e3b-c27e0c830bd0"
/>

**Light**
<img width="850" height="345" alt="image"
src="https://github.com/user-attachments/assets/6746cf32-bf63-4280-a5bd-bbd42c3cbe84"
/>


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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>
2025-12-08 14:43:45 +05:30
Gabriel Jablonski
b03dfdb751
Chore/merge upstream 4.8.0 (#150)
* chore: Hide "Learn More" button in feature spotlight for self-hosted (#12675)

* feat: single query for reporting event stats (#12664)

This PR collapses multiple queries fetching stats from a single table to
a single query

```sql
SELECT 
  user_id as user_id,
  COUNT(CASE WHEN name = 'conversation_resolved' THEN 1 END) as resolved_count,
  AVG(CASE WHEN name = 'conversation_resolved' THEN value END) as avg_resolution_time,
  AVG(CASE WHEN name = 'first_response' THEN value END) as avg_first_response_time,
  AVG(CASE WHEN name = 'reply_time' THEN value END) as avg_reply_time 
FROM "reporting_events"
WHERE 
  "reporting_events"."account_id" = <account_id> AND 
  "reporting_events"."created_at" >= '2025-09-14 18:30:00' AND 
  "reporting_events"."created_at" < '2025-10-14 18:29:59'
GROUP BY "reporting_events"."user_id";
```

### Why this works?

Here's why this optimization is faster based on PostgreSQL internals:

- Single Table Scan vs Multiple Scans: Earlier we did 4 sequential scans
(or 4 index scans) of the same data, with the same where clause, now in
a single scan all 4 `CASE` expressions are evaluated in a single pass.
- Shared Buffer Cache Efficiency: PostgreSQL's shared buffer cache
stores recently accessed pages, with this, pages are loaded once and
re-used for all aggregation, earlier with separate queries we were
forced to re-read all from the disk each time
- Reduced planning and network overhead (4 vs 1 query)


### How is it tested

1. The specs all pass without making any changes
2. Verified the reports side by side after generating from report seeder

#### How to test

Generate seed data using the following command

```bash
ACCOUNT_ID=1 ENABLE_ACCOUNT_SEEDING=true bundle exec rake db:seed:reports_data
```

Once done download the reports, checkout to this branch and download the
reports again and compare them

* chore: Update translations (#12625)

* chore: Migrate mailers from the worker to jobs (#12331)

Previously, email replies were handled inside workers. There was no
execution logs. This meant if emails silently failed (as reported by a
customer), we had no way to trace where the issue happened, the only
assumption was “no error = mail sent.”

By moving email handling into jobs, we now have proper execution logs
for each attempt. This makes it easier to debug delivery issues and
would have better visibility when investigating customer reports.

Fixes
https://linear.app/chatwoot/issue/CW-5538/emails-are-not-sentdelivered-to-the-contact

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>

* chore(deps-dev): bump vite from 5.4.20 to 5.4.21 (#12700)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 5.4.20 to 5.4.21.
<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.21</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v5.4.21/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.21/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted -->5.4.21 (2025-10-20)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix(dev): trim trailing slash before <code>server.fs.deny</code>
check (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20970">#20970</a>)
(<a
href="cad1d31d06">cad1d31</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/20968">#20968</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/20970">#20970</a></li>
<li>chore: update CHANGELOG (<a
href="ca88ed7398">ca88ed7</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="adce3c22c6"><code>adce3c2</code></a>
release: v5.4.21</li>
<li><a
href="cad1d31d06"><code>cad1d31</code></a>
fix(dev): trim trailing slash before <code>server.fs.deny</code> check
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20970">#20970</a>)</li>
<li><a
href="ca88ed7398"><code>ca88ed7</code></a>
chore: update CHANGELOG</li>
<li>See full diff in <a
href="https://github.com/vitejs/vite/commits/v5.4.21/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.20&new-version=5.4.21)](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>

* chore: Update translations (#12708)

* chore(sidekiq): log ActiveJob class and job_id on dequeue (#12704)

## Context

Sidekiq logs only showed the Sidekiq wrapper class and JID, which wasn’t
helpful when debugging ActiveJobs.

## Changes

- Updated `ChatwootDequeuedLogger` to log the actual `ActiveJob class`
and `job_id` instead of the generic Sidekiq wrapper and JID.

> Example
> ```
> Dequeued ActionMailer::MailDeliveryJob
123e4567-e89b-12d3-a456-426614174000 from default
> ```

- Remove sidekiq worker and unify everything to `ActiveJob`

* chore: Enforce custom role permissions on conversation access (#12583)

## Summary
- ensure conversation lookup uses the permission filter before fetching
records
- add request specs covering custom role access to unassigned
conversations

## Testing
- bundle exec rspec
spec/enterprise/controllers/api/v1/accounts/conversations_controller_spec.rb

------
https://chatgpt.com/codex/tasks/task_e_68de1f62b9b883268a54882e608a8bb8

* fix: parameterize agent name (#12709)

* chore: Remove channel icons from the create inbox page (#12727)

# Pull Request Template

## Description
This PR removes the frame containing all channel icons from the “Create
Inbox” page.

## Type of change

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

## How Has This Been Tested?

### Screenshots

**Before**
<img width="1314" height="1016" alt="image"
src="https://github.com/user-attachments/assets/2b773495-9ddb-48b4-b15d-9aef18259ce1"
/>


**After**
<img width="1314" height="979" alt="image"
src="https://github.com/user-attachments/assets/f4dc64cf-516c-4faf-a45c-2f7de05cc29b"
/>



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

* fix: Use gap-4 instead of margins to define space between elements (#12728)

We should avoid using margins to define space between elements, instead
use the gap utility.

The problem with this particular instance was that if Google auth was
turned off and SSO is available, there is a weird spacing at the top
caused by the margin from the SSO element.

This PR will fix that. It also introduces a gap between the divider and
the button, but that should be okay.

* feat(ee): Add a service to fetch website content and prepare a persona of Captain Assistant (#12732)

This PR is the first of many to simplify the process of building an
assistant. The new flow will only require the user’s website. We’ll
automatically crawl it, identify the business name and what the business
does, and then generate a suggested assistant persona, complete with a
proposed name and description.

This service returns the following.
Example: tooljet.com
<img width="795" height="217" alt="Screenshot 2025-10-25 at 2 55 04 PM"
src="https://github.com/user-attachments/assets/9cb3594a-9c9c-4970-a0a1-4c9c8869c193"
/>

Example: replit.com
<img width="797" height="176" alt="Screenshot 2025-10-25 at 2 56 42 PM"
src="https://github.com/user-attachments/assets/6a1b4266-aab6-455f-a5e3-696d3a8243c9"
/>

* chore: Adds URL-based search and tab selection (#12663)

# Pull Request Template

## Description

This PR enables URL-based search and tab selection, allowing search
queries and active tabs to persist in the URL for easy sharing.

Fixes
[CW-5766](https://linear.app/chatwoot/issue/CW-5766/cannot-impersonate-an-account),
https://github.com/chatwoot/chatwoot/issues/12623

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video

https://www.loom.com/share/422a1d61f3fe4278a88e352ef98d2b78?sid=35fabee7-652f-4e17-83bd-e066a3bb804c

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

* chore: Add tab params for inbox configuration (#12665)

# Pull Request Template

## Description

This PR enables active tabs in inbox settings to persist in the URL for
easy sharing.

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video

https://www.loom.com/share/63820ecb17ea491a9082339f8bb457b6?sid=4fef1acd-b4fd-431f-855c-7647015a330f


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

---------

Co-authored-by: Muhsin <muhsinkeramam@gmail.com>

* feat: Changelog card components (#12673)

# Pull Request Template

## Description

This PR introduces a new changelog component that can be used in the
sidebar.

Fixes
https://linear.app/chatwoot/issue/CW-5776/changelog-card-ui-component

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Screencast



https://github.com/user-attachments/assets/42e77e82-388a-4fc9-9b37-f3d0ea1a9d7f







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

---------

Co-authored-by: Muhsin <muhsinkeramam@gmail.com>

* chore: Remove linear integration feature flag (#12716)

This PR removes the linear integration feature flag since the
integration is pretty much stable and we do display the Linear CTA for
users who aren't connected.
Fixes
https://linear.app/chatwoot/issue/CW-5819/remove-linear-feature-flag-from-front-end

* chore: Update translations (#12722)

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* perf: Add database index on conversations identifier (#12715)

**Problem**
Slack webhook processing was failing with 500 errors due to database
timeouts. The query `Conversation.where(identifier:
params[:event][:thread_ts]).first` was performing full table scans and
hitting PostgreSQL statement timeout.

**Solution**
Added database index on conversations.identifier and account_id.

* fix: Extend phone number normalization to Twilio WhatsApp (#12655)

### Problem
WhatsApp Cloud channels already handle Brazil/Argentina phone number
format mismatches (PRs #12492, #11173), but Twilio WhatsApp channels
were creating duplicate contacts
  when:
  - Template sent to new format: `whatsapp:+5541988887777` (13 digits)
  - User responds from old format: `whatsapp:+554188887777` (12 digits)

### Solution

The solution extends the existing phone number normalization
infrastructure to support both WhatsApp providers while handling their
different payload formats:

  ### Provider Format Differences
  - **WhatsApp Cloud**: `wa_id: "919745786257"` (clean number)
- **Twilio WhatsApp**: `From: "whatsapp:+919745786257"` (prefixed
format)
  
  
 ### Test Coverage

#### Brazil Phone Number Tests
  **Case 1: New Format (13 digits with "9")**
- **Test 1**: No existing contact → Creates new contact with original
format
- **Test 2**: Contact exists in same format → Appends to existing
conversation

  **Case 2: Old Format (12 digits without "9")**
- **Test 3**: Contact exists in old format → Appends to existing
conversation
- **Test 4** *(Critical)*: Contact exists in new format, message in old
format → Finds existing contact, prevents duplicate
- **Test 5**: No contact exists → Creates new contact with incoming
format

#### Argentina Phone Number Tests
  **Case 3: With "9" after country code**
  - **Test 6**: No existing contact → Creates new contact
- **Test 7**: Contact exists in normalized format → Uses existing
contact

  **Case 4: Without "9" after country code**
  - **Test 8**: Contact exists in same format → Appends to existing
  - **Test 9**: No contact exists → Creates new contact

Fixes
https://linear.app/chatwoot/issue/CW-5565/inconsistencies-for-mobile-numbersargentina-brazil-and-mexico-numbers

* fix: Timezone offset reports broken by DST transition (#12747)

## Description

Fixes timezone offset parameter in V2 reports API that was broken by DST
transitions. The issue occurred when UK DST ended on October 26, 2025,
causing the test to fail starting October 27th.

~~**Initial diagnosis:** The root cause was that
`timezone_name_from_offset` used `zone.now.utc_offset` to match
timezones, which changes based on the current date's DST status rather
than the data being queried.~~

**Actual root cause:** The test was accidentally passing before DST
transition. During BST, `timezone_name_from_offset(0)` matched "Azores"
(UTC-1) instead of "Edinburgh" (UTC+0), and the -1 hour offset
coincidentally split midnight data into [1,5]. After DST ended, it
correctly matched "Edinburgh" (UTC+0), but this grouped all
conversations into one day [6], exposing that the test data was flawed.

The real issue: Test data created all 6 conversations starting at
midnight on a single day, which cannot produce a [1,5] split in true
UTC.

Fixes CW-5846

## Type of change

- [x] 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?

**Test that was failing:**
```bash
bundle exec rspec spec/controllers/api/v2/accounts/reports_controller_spec.rb:25
```

**Changes:**
~~1. Fixed `timezone_name_from_offset` to use January 1st as reference
date instead of current date~~
~~2. Converted timezone string to `ActiveSupport::TimeZone` object for
`group_by_period` compatibility~~

**Revised approach:**
1. Freeze test time to January 2024 using `travel_to`, making timezone
matching deterministic and aligned with test data period
2. Start test conversations at 23:00 instead of midnight to properly
span two days and test timezone boundary grouping
3. Keep `zone.now.utc_offset` (correct behavior for real users during
DST)

**Why this works:**
- Test runs "in January 2024" → `zone.now.utc_offset` returns January
offsets consistently
- Offset `-8` correctly matches Pacific Standard Time (UTC-8 in January)
- Real users in PDT (summer) with offset `-7` → correctly match Pacific
Daylight Time
- No production impact, test is deterministic year-round

**Verification:**
- Test now passes consistently regardless of current DST status
- Timezone matching works correctly for real users during DST periods
- Reports correctly group data by timezone offset across all seasons

## 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>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* fix: Captain response builder not getting triggered (#12729)

## Summary
- Fix captain response builder not getting triggered for cases where
responses are created as completed.

## Testing Instructions 
- Test articles with firecrawl
- Test articles without firecrawl
- Test PDF documents

---------

Co-authored-by: Pranav <pranav@chatwoot.com>

* chore: Update captain pending FAQ interface (#12752)

# Pull Request Template

## Description

**This PR includes,**
- Added new pending FAQs view with approve/edit/delete actions for each
response.
- Implemented banner notification showing pending FAQ count on main
approved responses page.
- Created dedicated route for pending FAQs review at
/captain/responses/pending.
- Added automatic pending count updates when switching assistants or
routes.
- Modified ResponseCard component to show action buttons instead of
dropdown in pending view.

Fixes
https://linear.app/chatwoot/issue/CW-5833/pending-faqs-in-a-different-ux

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video
https://www.loom.com/share/5fe8f79b04cd4681b9360c48710b9373


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

---------

Co-authored-by: Pranav <pranav@chatwoot.com>

* fix: Exclude authentication templates from WhatsApp template selection (#12753)

This PR add the changes for excluding the authentication templates from
the WhatsApp template selection in the frontend, as these templates are
not supported at the moment. Reference:
https://www.chatwoot.com/hc/user-guide/articles/1754940076-whatsapp-templates#what-is-not-supported

* feat: Template types components (#12714)

# Pull Request Template

## Description

Fixes
https://linear.app/chatwoot/issue/CW-5806/create-the-story-book-components-for-template-typestext-media-list

**Pending**
Need to standardize the structure to match the template/campaigns.


## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Screenshots

<img width="669" height="179" alt="image"
src="https://github.com/user-attachments/assets/42efd292-8520-4b05-81ec-8bc526fc12db"
/>
<img width="646" height="304" alt="image"
src="https://github.com/user-attachments/assets/431dd964-006c-4877-a693-dae39b90df4c"
/>
<img width="646" height="380" alt="image"
src="https://github.com/user-attachments/assets/9052e31f-9292-4afb-8897-13931655fa00"
/>
<img width="646" height="272" alt="image"
src="https://github.com/user-attachments/assets/873d2488-e856-4a0d-8579-cc1bcc61cc8e"
/>
<img width="646" height="490" alt="image"
src="https://github.com/user-attachments/assets/14c2aa42-bf27-475f-aa70-fe59c1d00e9b"
/>
<img width="646" height="281" alt="image"
src="https://github.com/user-attachments/assets/1f42408e-03e8-4863-b4c7-715d13d67686"
/>



## 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: Muhsin Keloth <muhsinkeramam@gmail.com>

* fix: update omniauth to latest to resolve heroku deployment issues (#12749)

# Pull Request Template

## Description

Fixes https://github.com/chatwoot/chatwoot/issues/12553

Heroku build was failing due to `omniauth` version mismatch. Also, added
`NODE_OPTIONS=--max-old-space-size=4096` to handle OOM during Vite
build.

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

- Tested on heroku

## 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
- [x] 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

* chore: Improvements in pending FAQs (#12755)

# Pull Request Template

## Description

**This PR includes:**

1. Added URL-based filter persistence for the responses pages, including
page and search parameters.
2. Introduced a new empty state variant for pending FAQs — without a
backdrop and with a “Clear Filters” option.
3. Made the actions, filter, and search row remain fixed at the top
while scrolling.

Fixes
https://linear.app/chatwoot/issue/CW-5852/improvements-in-pending-faqs

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom video
https://www.loom.com/share/1d9eee68c0684f0ab05e08b4ca1e0ce9


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

* fix: run captain v2 outside the transaction (#12756)

* feat: Always process email content (#12734)

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* feat: Bulk actions for contacts (#12763)

Introduces APIs and UI for bulk actions in contacts table. The initial
action available will be assign labels

Fixes: #8536 #12253 

## Screens

<img width="1350" height="747" alt="Screenshot 2025-10-29 at 4 05 08 PM"
src="https://github.com/user-attachments/assets/0792dff5-0371-4b2e-bdfb-cd32db773402"
/>
<img width="1345" height="717" alt="Screenshot 2025-10-29 at 4 05 19 PM"
src="https://github.com/user-attachments/assets/ae510404-c6de-4c15-a720-f6d10cdac25b"
/>

---------

Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* feat: Enable opensearch on paid plans automatically (#12770)

- enable `advanced_search feature` on all paid plans automatically

ref: https://github.com/chatwoot/chatwoot/pull/12503

* chore: Make contacts bulk action bar sticky (#12773)

# Pull Request Template

## Description

This PR makes the contacts bulk action bar sticky while scrolling.

## Type of change

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

## How Has This Been Tested?

### Screenshots
<img width="1080" height="300" alt="image"
src="https://github.com/user-attachments/assets/21f8f3c6-813e-4ef6-b40a-8dd14e6ffb26"
/>
<img width="1080" height="300" alt="image"
src="https://github.com/user-attachments/assets/bb939f1d-9a13-4f9f-953d-b9872c984b74"
/>



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

* chore: Add dependant destroy_async for sla events (#12774)

Added the destroy_async to prevent timeout during SLA policy deletion by
processing SLA events asynchronously.

* chore: Update translations (#12748)

* feat: Add company backfill migration for existing contacts (Part 1) (#12657)

## Description

Implements company backfill migration infrastructure for existing
contacts. This is **Part 1 of 2** for the company model production
rollout as described in
[CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production).

Creates jobs and services to associate existing contacts with companies
based on their email domains, filtering out free email providers (gmail,
yahoo, etc.) and disposable addresses.
 

**What's included:**
- Business email detector service with ValidEmail2 (uses
`disposable_domain?` to avoid DNS lookups)
- Per-account batch job to process contacts for one account
- Orchestrator job to iterate all accounts
- Rake task: `bundle exec rake companies:backfill`

~~*NOTE*: I'm using a hard-coded approach to determine if something is a
"business" email by filtering out emails that are usually personal. I've
also added domains that are common to some of our customers' regions.
This should be simpler. I looked into `Valid_Email2` and I couldn't find
anything to dictate whether an email is a personal email or a business
one. I don't think the approach used in the frontend is valid here.~~
UPDATE: Using `email_provider_info` gem instead.


**Pending - Part 2 (separate PR):** Real-time company creation for new
contacts

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

```bash
# Run all new tests
bundle exec rspec spec/enterprise/services/companies/business_email_detector_service_spec.rb \\
                   spec/enterprise/jobs/migration/company_account_batch_job_spec.rb \\
                   spec/enterprise/jobs/migration/company_backfill_job_spec.rb

# Run RuboCop
bundle exec rubocop enterprise/app/services/companies/business_email_detector_service.rb \\
                     enterprise/app/jobs/migration/company_account_batch_job.rb \\
                     enterprise/app/jobs/migration/company_backfill_job.rb \\
                     lib/tasks/companies.rake
```

**Performance optimization:**
- Uses `disposable_domain?` instead of `disposable?` to avoid DNS MX
lookups (discovered via tcpdump analysis - `disposable?` was making
network calls for every email, causing 100x slowdown)

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

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>

* feat: Add company auto-association for contacts (CW-5726 Part 2) (#12711)

## Description

Implements real-time company auto-association for contacts based on
email domains. This is **Part 2** of the company model production
rollout (CW-5726).

**Task:**
- When a contact is created with a business email, automatically create
and associate a company from the email domain
- When a contact is updated with an email for the first time (email was
previously nil), associate with a company
- Preserve existing company associations when email changes to avoid
user confusion
- Skip free email providers and disposable domains

**Dependencies:**
⚠️ Requires PR #12657 (Part 1: Backfill migration) to be merged first

**Linear ticket:**
[CW-5726](https://linear.app/chatwoot/issue/CW-5726/company-model-setting-it-up-on-production)

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Service specs: Tests business email detection, company creation,
association logic, edge cases (existing companies, free emails, nil
emails)
- Integration specs: Tests full callback flow for contact create/update
scenarios
- All tests passing: 10 examples, 0 failures
- RuboCop: 0 offenses

## 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
- [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 (PR #12657 pending)

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>

* fix: Optimize Message search_data to prevent OpenSearch field explosion (#12786)

## Description

Refactored the `Message#search_data` method to prevent exceeding
OpenSearch's 1000 field limit during reindex operations.

**Problem:** The previous implementation serialized entire ActiveRecord
objects (Inbox, Sender, Conversation) with all their JSONB fields,
causing dynamic field explosion in OpenSearch. This resulted in
`Searchkick::ImportError` with "Limit of total fields [1000] has been
exceeded".

**Solution:** Whitelisted only necessary fields for search and
filtering, and flattened JSONB `custom_attributes` into key-value pair
arrays to prevent unbounded field creation.

Linked to: CW-5861

## Type of change

- [x] 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)
- [x] This change requires a documentation update

## How Has This Been Tested?

- Verified rubocop passes with no offenses
- Code review of search field usage from
`enterprise/app/services/enterprise/search_service.rb`
- Analyzed actual search queries to determine required indexed fields

**Still needed:**
- Full reindex test on staging/production environment
- Verify search functionality still works after reindex
- Confirm field count is under 1000 limit

## Changes Made

### Before
- Indexed 1000+ fields (entire AR objects with JSONB)
- `inbox` = full Inbox object (23+ fields + JSONB)
- `sender` = full Contact/User/AgentBot object (10+ fields + JSONB)
- `conversation` = full push_event_data
- Dynamic JSONB keys creating unlimited fields

### After
- ~35-40 controlled fields
- Whitelisted search fields: `content`, `attachment_transcribed_text`,
`email_subject`
- Filter fields: `account_id`, `inbox_id`, `conversation_id`,
`sender_id`, `sender_type`, etc.
- Flattened `custom_attributes`: `[{key, value, value_type}]` format
- Helper methods: `search_conversation_data`, `search_inbox_data`,
`search_sender_data`, `search_additional_data`

## 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
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

## Post-merge Steps

After merging, the following steps are required:

1. **Reindex all messages:**
   ```bash
   bundle exec rails runner "Message.reindex"
   ```

2. **Verify field count:**
   ```bash
   bundle exec rails runner "
     client = Searchkick.client
     index_name = Message.searchkick_index.name
     mapping = client.indices.get_mapping(index: index_name)
     fields = mapping.dig(index_name, 'mappings', 'properties')
     puts 'Total fields: ' + fields.keys.count.to_s
   "
   ```

3. **Test search functionality** to ensure queries still work as
expected

---------

Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>

* fix: Avoid introducing new attributes in search (#12791)

Fix `Limit of total fields [1000] has been exceeded`


https://linear.app/chatwoot/issue/CW-5861/searchkickimporterror-type-=-illegal-argument-exception-reason-=-limit#comment-6b6e41bd

* fix: Gate Sidekiq dequeue logger behind env (#12790)

## Summary
- wrap the dequeue middleware registration in a boolean env flag
- document the ENABLE_SIDEKIQ_DEQUEUE_LOGGER option in .env.example

* feat: Bulk delete for contacts (#12778)

Introduces a new bulk action `delete` for contacts

ref: https://github.com/chatwoot/chatwoot/pull/12763

## Screens

<img width="1492" height="973" alt="Screenshot 2025-10-31 at 6 27 21 PM"
src="https://github.com/user-attachments/assets/30dab1bb-2c2c-4168-9800-44e0eb5f8e3a"
/>
<img width="1492" height="985" alt="Screenshot 2025-10-31 at 6 27 32 PM"
src="https://github.com/user-attachments/assets/5be610c4-b19e-4614-a164-103b22337382"
/>

* fix: Video bubble click and play issue (#12764)

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* feat: Differentiate bot and user in the summary (#12801)

While generating the summary, use the appropriate sender type for the
message.

* fix: Invalid image URL issue in Help Center articles (#12806)

* feat: allow bots to handle campaigns when sender_id is nil (#12805)

* fix: Add empty line before signature in compose conversation editor (#12702)

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>

* feat: Enhance button interactions (#12738)

* fix: Remove the same account validation for whatsapp channels (#12811)

## Description

Modified the phone number validation in Whatsapp::ChannelCreationService
to check for duplicate phone numbers across ALL accounts, not just
within the current account.

## Type of change

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

## How Has This Been Tested?

- Added test coverage for cross-account phone number validation
- Using actual UI flow 
<img width="1493" height="532" alt="image"
src="https://github.com/user-attachments/assets/67d2bb99-2eb9-4115-8d56-449e4785e0d8"
/>


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

* feat: Update Captain navigation structure (#12761)

# Pull Request Template

## Description

This PR includes an update to the Captain navigation structure.

## Route Structure

```javascript
1. captain_assistants_responses_index    → /captain/:assistantId/faqs
2. captain_assistants_documents_index    → /captain/:assistantId/documents
3. captain_assistants_scenarios_index    → /captain/:assistantId/scenarios
4. captain_assistants_playground_index   → /captain/:assistantId/playground
5. captain_assistants_inboxes_index      → /captain/:assistantId/inboxes
6. captain_tools_index                   → /captain/tools
7. captain_assistants_settings_index     → /captain/:assistantId/settings
8. captain_assistants_guardrails_index   → /captain/:assistantId/settings/guardrails
9. captain_assistants_guidelines_index   → /captain/:assistantId/settings/guidelines
10. captain_assistants_index             → /captain/:navigationPath
```

**How it works:**

1. User clicks sidebar item → Routes to `captain_assistants_index` with
`navigationPath`
2. `AssistantsIndexPage` validates route and gets last active assistant,
if not redirects to assistant create page.
3. Routes to actual page: `/captain/:assistantId/:page`
4. Page loads with correct assistant context

Fixes
https://linear.app/chatwoot/issue/CW-5832/updating-captain-navigation

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?




## 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>
Co-authored-by: Sojan Jose <sojan@pepalo.com>

* fix: Handle login when there are no accounts (#12816)

* chore: Update translations (#12794)

* chore(docs): Fix typos in some files (#12817)

This PR fixes typos in the file file using codespell.

* refactor: strategy pattern for mailbox conversation finding (#12766)

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>

* fix: Issue with processing variables in outgoing email content (#12799)

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>

* fix: hide pdf citations in captain faq responses (#12839)

* fix: Use contact_id instead of sender_id for Instagram message locks (#12841)

Previously, the lock key for Instagram used sender_id, which for echo
messages (outgoing) would be the account's own ID. This caused all
outgoing messages to compete for the same lock, creating a bottleneck
during bulk messaging.

The fix introduces contact_instagram_id method that correctly identifies
the contact's ID regardless of message direction:
- For echo messages (outgoing): uses recipient.id (the contact)
- For incoming messages: uses sender.id (the contact)

This ensures each conversation has a unique lock, allowing parallel
processing of webhooks while maintaining race condition protection
within individual conversations.

Fixes lock acquisition errors in Sidekiq when processing bulk Instagram
messages.

Fixes
https://linear.app/chatwoot/issue/CW-5931/p0-mutexapplicationjoblockacquisitionerror-failed-to-acquire-lock-for

## Type of change

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

* fix: label tags for contactable inboxes (#12838)

* chore: Improve captain layout (#12820)

* feat: allow selecting month range in overview reports (#12701)

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>

* fix: respect status parameter when creating articles via API (#12846)

## Description

The Articles API was ignoring the `status` parameter when creating new
articles. All articles were forced to be drafts due to a hardcoded
`@article.draft!` call in the controller, even when users explicitly
sent `status: 1` (published) in their API request.

This PR removes the hardcoded draft enforcement and allows the status
parameter to be respected while maintaining backward compatibility.

Fixes #12063

## Type of change

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

## How Has This Been Tested?

**Before:**
- API POST with `status: 1` → Created as draft (ignored parameter)
- API POST without status → Created as draft

**After:**
- API POST with `status: 1` → Created as published 
- API POST without status → Created as draft (backward compatible) 
- UI creates articles → Still creates as draft (UI doesn't send status)


**Tests run:**
```bash
bundle exec rspec spec/controllers/api/v1/accounts/articles_controller_spec.rb
# 17 examples, 0 failures
```

Updated tests:
1. Changed 2 existing tests that were verifying the broken behavior
(expecting draft when published was sent)
2. Added new test to verify articles default to draft when status is not
provided
3. All existing tests pass, confirming backward compatibility

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

Co-authored-by: Sojan Jose <sojan@pepalo.com>

* feat: allow querying reporting events via the API (#12832)

* feat(webhooks): add name to webhook (#12641)

## Description

When working with webhooks, it's easy to lose track of which URL is
which. Adding a `name` (optional) column to the webhook model is a
straight-forward solution to make it significantly easier to identify
webhooks.

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

Model and controller specs, and also running in production over several
months without any issues.

| Before | After |
| --- | --- |
| <img width="949" height="990" alt="image copy 3"
src="https://github.com/user-attachments/assets/6b33c072-7d16-4a9c-a129-f9c0751299f5"
/> | <img width="806" height="941" alt="image"
src="https://github.com/user-attachments/assets/77f3cb3a-2eb0-41ac-95bf-d02915589690"
/> |
| <img width="1231" height="650" alt="image copy 2"
src="https://github.com/user-attachments/assets/583374af-96e0-4436-b026-4ce79b7f9321"
/> | <img width="1252" height="650" alt="image copy"
src="https://github.com/user-attachments/assets/aa81fb31-fd18-4e21-a40e-d8ab0dc76b4e"
/> |


## 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
- [x] 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

* perf: speed up docker builds (#12859)

- Use separate keys to avoid cache overwrites across different
architecture builds


https://linear.app/chatwoot/issue/CW-5945/perf-speed-up-docker-builds

### 25 mins  ---> 5mins


## before

<img width="971" height="452" alt="image"
src="https://github.com/user-attachments/assets/535cebd6-6c16-48d1-a62d-ffb6f2fc9b08"
/>


## after
<img width="940" height="428" alt="image"
src="https://github.com/user-attachments/assets/359eb313-4bb5-4e0e-9492-a8ad48645159"
/>

* chore: Update missing places with new colors (#12862)

# Pull Request Template

## Description

This PR updates the colors in places that were missed during the color
update migration.

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

* fix: Brand installation name not showing (#12861)

# Pull Request Template

## Description

Fixes
https://linear.app/chatwoot/issue/CW-5946/fix-brand-installation-name-issue-in-dyte

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

* fix: migrate from deprecated annotate gem to annotaterb (#12845)

## Description

The `annotate` gem has been deprecated and users are experiencing
annotation errors with the new Rails 7 `serialize` syntax. This PR
migrates to `annotaterb`, the actively maintained fork.

Users reported errors when running `make db`:
```
Unable to annotate app/models/installation_config.rb: no implicit conversion of Hash into String  
Unable to annotate app/models/installation_config.rb: no implicit conversion of nil into Array
```

This PR updates the Gemfile and rake configuration to use `annotaterb`
instead.

Fixes #11673

## Type of change

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

## How Has This Been Tested?

Tested locally with the following steps:
1. Run `bundle install` - successfully installed annotaterb 4.20.0
2. Run `RAILS_ENV=development bundle exec rails db:chatwoot_prepare` -
completed without annotation errors
3. Run `RAILS_ENV=development bundle exec rails annotate_rb:models` -
successfully annotated all models including InstallationConfig
4. Verified InstallationConfig model annotations are present and correct

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

* chore: disable worker MemoryHigh throttling in systemd unit (#12871)

- set MemoryHigh to infinity in deployment/chatwoot-worker.1.service so
the worker is throttled only by the existing
    MemoryMax hard limit
- prevents cgroup reclaim from slowing Sidekiq under transient spikes
while still keeping the hard stop at 1.5 GB

* chore: Update translations (#12818)

* fix: revert annotaterb migration due to persistent annotation errors (#12881)

## Description

This PR reverts the migration from the `annotate` gem to `annotaterb`
introduced in PR #12845. The annotation errors reported in #11673
persist with both gems, and the old `annotate` gem handles the errors
more gracefully by continuing to process other models instead of
crashing.

**Testing reveals both gems fail with the same underlying issue:**

**Old annotate gem (3.2.0):**
```
Unable to annotate app/models/installation_config.rb: no implicit conversion of Hash into String
Unable to annotate app/models/installation_config.rb: no implicit conversion of nil into Array
Model files unchanged.
```
(Logs error but continues processing)

**New annotaterb gem (4.20.0):**
```
❯ bundle exec annotaterb models
ruby/3.4.4/lib/ruby/gems/3.4.0/gems/reline-0.3.6/lib/reline/terminfo.rb:2: warning: ruby/3.4.4/lib/ruby/3.4.0/fiddle.rb was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.5.0.
You can add fiddle to your Gemfile or gemspec to silence this warning.
Also please contact the author of reline-0.3.6 to request adding fiddle into its gemspec.
Annotating models
bundler: failed to load command: annotaterb (ruby/3.4.4/bin/annotaterb)
ruby/3.4.4/lib/ruby/3.4.0/psych/parser.rb:62:in 'Psych::Parser#_native_parse': no implicit conversion of Hash into String (TypeError)

      _native_parse @handler, yaml, path
                    ^^^^^^^^^^^^^^^^^^^^
        from ruby/3.4.4/lib/ruby/3.4.0/psych/parser.rb:62:in 'Psych::Parser#parse'
        from ruby/3.4.4/lib/ruby/3.4.0/psych.rb:457:in 'Psych.parse_stream'
        from ruby/3.4.4/lib/ruby/3.4.0/psych.rb:401:in 'Psych.parse'
        from ruby/3.4.4/lib/ruby/3.4.0/psych.rb:325:in 'Psych.safe_load'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-7.1.5.2/lib/active_record/coders/yaml_column.rb:37:in 'ActiveRecord::Coders::YAMLColumn::SafeCoder#load'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-7.1.5.2/lib/active_record/coders/column_serializer.rb:37:in 'ActiveRecord::Coders::ColumnSerializer#load'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-7.1.5.2/lib/active_record/type/serialized.rb:22:in 'ActiveRecord::Type::Serialized#deserialize'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-7.1.5.2/lib/active_model/attribute.rb:175:in 'ActiveModel::Attribute::FromDatabase#type_cast'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-7.1.5.2/lib/active_model/attribute.rb:43:in 'ActiveModel::Attribute#value'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-7.1.5.2/lib/active_model/attribute_set.rb:37:in 'block in ActiveModel::AttributeSet#to_hash'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activesupport-7.1.5.2/lib/active_support/core_ext/enumerable.rb:78:in 'block in Enumerable#index_with'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activesupport-7.1.5.2/lib/active_support/core_ext/enumerable.rb:78:in 'Array#each'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activesupport-7.1.5.2/lib/active_support/core_ext/enumerable.rb:78:in 'Enumerable#index_with'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activemodel-7.1.5.2/lib/active_model/attribute_set.rb:37:in 'ActiveModel::AttributeSet#to_hash'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/activerecord-7.1.5.2/lib/active_record/model_schema.rb:499:in 'ActiveRecord::ModelSchema::ClassMethods#column_defaults'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/model_wrapper.rb:68:in 'AnnotateRb::ModelAnnotator::ModelWrapper#column_defaults'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/model_wrapper.rb:139:in 'block in AnnotateRb::ModelAnnotator::ModelWrapper#built_attributes'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/model_wrapper.rb:136:in 'Array#map'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/model_wrapper.rb:136:in 'AnnotateRb::ModelAnnotator::ModelWrapper#built_attributes'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/column_annotation/annotation_builder.rb:15:in 'AnnotateRb::ModelAnnotator::ColumnAnnotation::AnnotationBuilder#build'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:52:in 'block in AnnotateRb::ModelAnnotator::Annotation::AnnotationBuilder::Annotation#columns'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:51:in 'Array#map'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:51:in 'AnnotateRb::ModelAnnotator::Annotation::AnnotationBuilder::Annotation#columns'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:26:in 'AnnotateRb::ModelAnnotator::Annotation::AnnotationBuilder::Annotation#body'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:35:in 'AnnotateRb::ModelAnnotator::Annotation::AnnotationBuilder::Annotation#build'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotation/annotation_builder.rb:71:in 'AnnotateRb::ModelAnnotator::Annotation::AnnotationBuilder#build'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/project_annotator.rb:43:in 'AnnotateRb::ModelAnnotator::ProjectAnnotator#build_instructions_for_file'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/project_annotator.rb:17:in 'block in AnnotateRb::ModelAnnotator::ProjectAnnotator#annotate'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/project_annotator.rb:13:in 'Array#map'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/project_annotator.rb:13:in 'AnnotateRb::ModelAnnotator::ProjectAnnotator#annotate'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotator.rb:21:in 'AnnotateRb::ModelAnnotator::Annotator#do_annotations'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/model_annotator/annotator.rb:8:in 'AnnotateRb::ModelAnnotator::Annotator.do_annotations'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/commands/annotate_models.rb:17:in 'AnnotateRb::Commands::AnnotateModels#call'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/runner.rb:38:in 'AnnotateRb::Runner#run'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/lib/annotate_rb/runner.rb:11:in 'AnnotateRb::Runner.run'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/annotaterb-4.20.0/exe/annotaterb:18:in '<top (required)>'
        from ruby/3.4.4/bin/annotaterb:25:in 'Kernel#load'
        from ruby/3.4.4/bin/annotaterb:25:in '<top (required)>'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli/exec.rb:58:in 'Kernel.load'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli/exec.rb:58:in 'Bundler::CLI::Exec#kernel_load'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli/exec.rb:23:in 'Bundler::CLI::Exec#run'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli.rb:455:in 'Bundler::CLI#exec'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/vendor/thor/lib/thor.rb:527:in 'Bundler::Thor.dispatch'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/exe/bundle:28:in 'block in <top (required)>'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/lib/bundler/friendly_errors.rb:117:in 'Bundler.with_friendly_errors'
        from ruby/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.5.16/exe/bundle:20:in '<top (required)>'
        from ruby/3.4.4/bin/bundle:25:in 'Kernel#load'
        from ruby/3.4.4/bin/bundle:25:in '<main>'


```
(Crashes immediately, stops all processing)

**Root cause:** The `InstallationConfig` model uses YAML serialization
(`serialize :serialized_value, coder: YAML`) on a JSONB database column.
When annotation tools read column defaults, PostgreSQL returns JSONB as
a Hash, but YAML expects a String, causing the type error.

The migration to annotaterb doesn't solve the problem - both gems
encounter the same error. The old gem is preferable as it continues
working despite the error.

Reverts #12845
Related to #11673

## Type of change

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

## How Has This Been Tested?

1. Reverted commit 559d1b657
2. Ran `bundle install` to reinstall annotate gem v3.2.0
3. Ran `RAILS_ENV=development bundle exec annotate` 
- Result: Logs errors for InstallationConfig but completes successfully
4. Re-applied the annotaterb changes and tested `bundle exec annotaterb
models`
   - Result: Crashes with full stack trace and stops processing

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


---
*Edited to truncate environment-specific info from error dump*

* chore: Hide assistant switcher on paywall screen (#12875)

* feat: Assignment service (v2) (#12320)

## Linear Link

 
## Description

This PR introduces a new robust auto-assignment system for conversations
in Chatwoot. The system replaces the existing round-robin assignment
with a more sophisticated service-based architecture that supports
multiple assignment strategies, rate limiting, and Enterprise features
like capacity-based assignment and balanced distribution.

## Type of change

- [ ] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Unit test cases
- Test conversations getting assigned on status change to open
- Test the job directly via rails 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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a new service-based auto-assignment system with scheduled jobs,
rate limiting, enterprise capacity/balanced selection, and wiring via
inbox/handler; includes Redis helpers and comprehensive tests.
> 
> - **Auto-assignment v2 (core services)**:
> - Add `AutoAssignment::AssignmentService` with bulk assignment,
configurable conversation priority, RR selection, and per-agent rate
limiting via `AutoAssignment::RateLimiter`.
>   - Add `AutoAssignment::RoundRobinSelector` for agent selection.
> - **Jobs & scheduling**:
> - Add `AutoAssignment::AssignmentJob` (per-inbox bulk assign;
env-based limit) and `AutoAssignment::PeriodicAssignmentJob` (batch over
accounts/inboxes).
> - Schedule periodic run in `config/schedule.yml`
(`periodic_assignment_job`).
> - **Model/concerns wiring**:
> - Include `InboxAgentAvailability` in `Inbox`; add
`Inbox#auto_assignment_v2_enabled?`.
> - Update `AutoAssignmentHandler` to trigger v2 job when
`auto_assignment_v2_enabled?`, else fallback to legacy.
> - **Enterprise extensions**:
> - Add `Enterprise::InboxAgentAvailability` (capacity-aware filtering)
and `Enterprise::Concerns::Inbox` association `inbox_capacity_limits`.
> - Extend service via `Enterprise::AutoAssignment::AssignmentService`
(policy-driven config, capacity filtering, exclusion rules) and add
selectors/services: `BalancedSelector`, `CapacityService`.
> - **Infrastructure**:
> - Enhance `Redis::Alfred` with `expire`, key scan/count, and extended
ZSET helpers (`zadd`, `zcount`, `zcard`, `zrangebyscore`).
> - **Tests**:
> - Add specs for jobs, core service, rate limiter, RR selector, and
enterprise features (capacity, balanced selection, exclusions).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0ebe187c8aea73765b0122a44b18d6f465c2477f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>

* fix: Change contact_inboxes.source_id to text column (#12882)

## Description

Fixes CW-5961 where IMAP email processing failed with
`ActiveRecord::RecordInvalid: Validation failed: Source is too long
(maximum is 255 characters)` error.

This changes the `contact_inboxes.source_id` column from `string` (255
character limit) to `text` (unlimited) to accommodate long email message
IDs that were causing validation failures.

Fixes CW-5961

## Type of change

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

## How Has This Been Tested?

- Added spec test validating `source_id` values longer than 255
characters (300 chars)
- All existing `contact_inbox_spec.rb` tests pass (7 examples, 0
failures)
- Migration applied successfully with reversible up/down methods
- Verified `source_id` column type changed to `text` with `null: false`
constraint preserved

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

* feat: allow configuring attachment upload limit (#12835)

## Summary
- add a configurable MAXIMUM_FILE_UPLOAD_SIZE installation setting and
surface it through super admin and global config payloads
- apply the configurable limit to attachment validations and shared
upload helpers on dashboard and widget
- introduce a reusable helper with unit tests for parsing the limit and
extend attachment specs for configurability


------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_6912644786b08326bc8dee9401af6d0a)

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>

* feat: Customizable webhook timeout configuration (#12777)

## Summary
- Ability to configure the webhook timeout for Chatwoot self hosted
installations

fixes: https://github.com/chatwoot/chatwoot/issues/12754

* feat: Control the allowed login methods via Super Admin (#12892)

- Control the allowed authentication methods for a chatwoot installation
via super admin configs. [SAML, Google Auth etc]
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_6917d503b6e48326a261672c1de91462)

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* chore: Update translations (#12876)

* feat: Backend - Companies API endpoint with pagination and search (#12840)

## Description

Adds API endpoint to list companies with pagination, search, and
sorting.

Fixes
https://linear.app/chatwoot/issue/CW-5930/add-backend-routes-to-get-companies-result
Parent issue:
https://linear.app/chatwoot/issue/CW-5928/add-companies-tab-to-dashboard

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

Added comprehensive specs to
`spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb`:
- Pagination (25 per page, multiple pages)
- Search by name and domain (case-insensitive)
- Counter cache for contacts_count
- Account scoping
- Authorization

To reproduce:
```bash
bundle exec rspec spec/enterprise/controllers/api/v1/accounts/companies_controller_spec.rb
bundle exec rubocop enterprise/app/controllers/api/v1/accounts/companies_controller.rb
```

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

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>

* feat: Companies page (#12842)

# Pull Request Template

## Description

This PR introduces a new Companies section in the Chatwoot dashboard. It
lists all companies associated with the account and includes features
such as **search**, **sorting**, and **pagination** to enable easier
navigation and efficient management.

Fixes
https://linear.app/chatwoot/issue/CW-5928/add-companies-tab-to-dashboard

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Screenshot
<img width="1619" height="1200" alt="image"
src="https://github.com/user-attachments/assets/21f0a666-c3d6-4dec-bd02-1e38e0cd9542"
/>



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

---------

Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>

* feat: Add Amazon SES inbound email support (#12893)

## Summary
- add AWS ActionMailbox SES gems
- document SES as incoming email provider
- note SES option in configuration

## Testing
- `bundle exec rubocop config/initializers/mailer.rb
config/environments/production.rb Gemfile`


------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_68bbb7d482288326b8f04bb795af0322)

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>

* feat: hide email forwarding address if INBOUND_EMAIL_DOMAIN is not configured (#12768)

#### Summary

- Improved email inbox setup flow to handle cases where inbound email
forwarding is not configured on the installation
- Added conditional display of email forwarding address based on
MAILER_INBOUND_EMAIL_DOMAIN environment variable availability
- Enhanced user messaging to guide users toward configuring SMTP/IMAP
settings when forwarding is unavailable

#### Changes

**Backend (app/views/api/v1/models/_inbox.json.jbuilder)**
- Added forwarding_enabled boolean flag to inbox API response based on
MAILER_INBOUND_EMAIL_DOMAIN presence
- Made forward_to_email conditional - only included when forwarding is
enabled

  **Frontend - Inbox Creation Flow**
- Created new EmailInboxFinish.vue component to handle email inbox setup
completion
  - Shows different messages based on whether forwarding is enabled:
- With forwarding: displays forwarding address and encourages SMTP/IMAP
configuration
- Without forwarding: warns that SMTP/IMAP configuration is required for
emails to be processed
- Added link to configuration page for easy access to SMTP/IMAP settings

<img width="988" height="312" alt="Screenshot 2025-11-18 at 3 27 27 PM"
src="https://github.com/user-attachments/assets/928aff78-df73-49fa-9a26-dbbd1297b26a"
/>

<img width="765" height="489" alt="Screenshot 2025-11-18 at 3 24 46 PM"
src="https://github.com/user-attachments/assets/6a182c7d-087f-4e88-92a5-30f147a567a7"
/>


Fixes
https://linear.app/chatwoot/issue/CW-5881/hide-forwaring-email-section-if-inbound-email-domain-is-not-configured


## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Tested locally

## 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: Pranav <pranav@chatwoot.com>

* feat: APIs to assign agents_bots as assignee in conversations (#12836)

## Summary
- add an assignee_agent_bot_id column as an initital step to prototype
this before fully switching to polymorphic assignee
- update assignment APIs and conversation list / show endpoints to
reflect assignee as agent bot
- ensure webhook payloads contains agent bot assignee


[Codex
Task](https://chatgpt.com/codex/tasks/task_e_6912833377e48326b6641b9eee32d50f)

---------

Co-authored-by: Pranav <pranav@chatwoot.com>

* Bump version to 4.8.0

* chore: remove migration

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Chatwoot Bot <92152627+chatwoot-bot@users.noreply.github.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com>
Co-authored-by: Lê Nam Khánh <55955273+khanhkhanhlele@users.noreply.github.com>
2025-11-19 16:25:58 -03:00
Lê Nam Khánh
fb1aa085cf
chore(docs): Fix typos in some files (#12817)
This PR fixes typos in the file file using codespell.
2025-11-07 07:57:37 -08:00
gabrieljablonski
8d4a6b856a Merge branch main into chore/merge-upstream-4.7.0 2025-10-16 12:08:20 -03:00
Shivam Mishra
ecff66146a
fix: Rendering on email without html content (#12561)
<img width="983" height="579" alt="image"
src="https://github.com/user-attachments/assets/2972e8d9-5145-4958-8f66-9e84bd9c8c4b"
/>
2025-10-01 13:43:05 +05:30
Shivam Mishra
21366e1c3b
feat: allow quoted email thread in reply (#12545)
This PR adds the ability to include the thread history as a quoted text

## Preview


https://github.com/user-attachments/assets/c96a85e5-8ac8-4021-86ca-57509b4eea9f
2025-09-30 17:47:09 +05:30
Shivam Mishra
406a470c81
feat: Log push notification error (#12543)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-29 15:19:45 +05:30
Sojan Jose
2ba4780bda
feat: Add UI to manage web widget allowed domains (#12495)
## Summary
- add allowed domains controls in the web widget configuration page.

<img width="1064" height="699" alt="Screenshot 2025-09-23 at 8 52 21 PM"
src="https://github.com/user-attachments/assets/8afd60b6-c81d-4f52-9cbe-07e70ad003d2"
/>


fixes:
https://linear.app/chatwoot/issue/CW-5661/add-the-options-for-configure-allowed-domains-for-web-widget

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
2025-09-24 16:46:19 +05:30
gabrieljablonski
18c672c204 Merge branch 'main' into chore/merge-upstream-4.6.0 2025-09-19 19:37:28 -03:00
Aguinaldo Tupy
f03a52bd77
feat: Add media_name support for WhatsApp templates document files (#12462)
## Description

This implementation adds support for the `media_name` parameter for
WhatsApp document templates, resolving the issue where documents appear
as "untitled" when sent via templates.

**Problem solved:** Documents sent via WhatsApp templates always
appeared as "untitled" because Chatwoot didn't process the `filename`
field required by the WhatsApp API.

**Solution:** Added support for the `media_name` parameter that maps to
the WhatsApp API's `filename` field.

## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update

## How Has This Been Tested?

Created and executed **7 comprehensive test scenarios**:

1.  Document without `media_name` (backward compatibility)
2.  Document with valid `media_name`
3.  Document with blank `media_name`
4.  Document with null `media_name`
5.  Image with `media_name` (ignored as expected)
6.  Video with `media_name` (ignored as expected)
7.  Blank URL (returns nil appropriately)

**All tests passed** and confirmed **100% backward compatibility**.

## Technical Implementation

**Backend Changes:**
- `PopulateTemplateParametersService`: Added `media_name` parameter
support
- `TemplateProcessorService`: Pass `media_name` to parameter builder
- `WhatsappCloudService`: Updated documentation with `media_name`
example

**Frontend Changes:**
- `WhatsAppTemplateParser.vue`: Added UI field for document filename
input
- `templateHelper.js`: Include `media_name` for document templates
- `whatsappTemplates.json`: Added translation key for document name
placeholder

**Key Features:**
- 🔄 **100% Backward Compatible** - Existing templates continue working
- 📝 **Document Filename Support** - Users can specify custom filenames
- 🎯 **Document-Only Feature** - Only affects document media types
-  **Comprehensive Testing** - All edge cases covered

## Expected Behavior

**Before:**
```ruby
# All documents appear as "untitled"
{
  type: 'document',
  document: { link: 'https://example.com/document.pdf' }
}
```

**After:**
```ruby
# With media_name - displays custom filename
{
  type: 'document',
  document: {
    link: 'https://example.com/document.pdf',
    filename: 'Invoice_2025.pdf'
  }
}

# Without media_name - works as before
{
  type: 'document',
  document: { link: 'https://example.com/document.pdf' }
}
```

## 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
- [x] 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: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-18 15:25:31 +05:30
Vishnu Narayanan
9527ff6269
feat: Add support for labels in automations (#11658)
- Add support for using labels as an action event for automation
 - Fix duplicated conversation_updated event dispatch for labels
 

Fixes https://github.com/chatwoot/chatwoot/issues/8539 and multiple
issues around duplication related to label change events.
---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-18 14:17:54 +05:30
Shivam Mishra
300d68f3f7
feat: SAML UI [CW-2958] (#12345)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-15 19:33:54 +05:30
Sivin Varghese
55633ab063
feat: Agent assignment policy index page with CRUD actions (#12373)
# Pull Request Template

## Description

This PR incudes new Agent assignment policy index page with CRUD
actions.

Fixes
https://linear.app/chatwoot/issue/CW-5570/feat-assignment-policy-index-page-with-actions

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

### Loom

https://www.loom.com/share/17ab5ceca4854f179628a3b53f347e5a?sid=cb64e881-57fd-4ae1-921b-7648653cca33

### Screenshots

**Light mode**
<img width="1428" height="888" alt="image"
src="https://github.com/user-attachments/assets/fdbb83e9-1f4f-4432-9e8a-4a8f1b810d31"
/>


**Dark mode**
<img width="1428" height="888" alt="image"
src="https://github.com/user-attachments/assets/f1fb38b9-1150-482c-ba62-3fe63ee1c7d4"
/>
<img width="726" height="495" alt="image"
src="https://github.com/user-attachments/assets/90a6ad55-9ab6-4adb-93a7-2327f5f09c79"
/>


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

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-10 12:07:21 +05:30
Muhsin Keloth
99997a701a
feat: Add twilio content templates (#12277)
Implements comprehensive Twilio WhatsApp content template support (Phase
1) enabling text, media, and quick reply templates with proper parameter
conversion, sync capabilities, and feature flag protection.

###  Features Implemented

  **Template Types Supported**

  - Basic Text Templates: Simple text with variables ({{1}}, {{2}})
  - Media Templates: Image/Video/Document templates with text variables
  - Quick Reply Templates: Interactive button templates
- Phase 2 (Future): List Picker, Call-to-Action, Catalog, Carousel,
Authentication templates

  **Template Synchronization**

- API Endpoint: POST
/api/v1/accounts/{account_id}/inboxes/{inbox_id}/sync_templates
  - Background Job: Channels::Twilio::TemplatesSyncJob
  - Storage: JSONB format in channel_twilio_sms.content_templates
  - Auto-categorization: UTILITY, MARKETING, AUTHENTICATION categories

 ###  Template Examples Tested


  #### Text template
```
  { "name": "greet", "language": "en" }
```
  #### Template with variables
```
  { "name": "order_status", "parameters": [{"type": "body", "parameters": [{"text": "John"}]}] }
```

  #### Media template with image
```
  { "name": "product_showcase", "parameters": [
    {"type": "header", "parameters": [{"image": {"link": "image.jpg"}}]},
    {"type": "body", "parameters": [{"text": "iPhone"}, {"text": "$999"}]}
  ]}
```
#### Preview

<img width="1362" height="1058" alt="CleanShot 2025-08-26 at 10 01
51@2x"
src="https://github.com/user-attachments/assets/cb280cea-08c3-44ca-8025-58a96cb3a451"
/>

<img width="1308" height="1246" alt="CleanShot 2025-08-26 at 10 02
02@2x"
src="https://github.com/user-attachments/assets/9ea8537a-61e9-40f5-844f-eaad337e1ddd"
/>

#### User guide

https://www.chatwoot.com/hc/user-guide/articles/1756195741-twilio-content-templates

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2025-08-29 16:13:25 +05:30
Muhsin Keloth
068538e3d6
feat: Setup posthog analytics (#12291)
- Replace June.so analytics with PostHog integration
- Maintain existing analytics API interface for seamless migration
- Remove all the June references

_June.so is shutting down their service, requiring migration to an
alternative analytics provider. PostHog was chosen as the replacement
due to its robust feature set and similar API structure._
2025-08-27 09:10:06 +05:30
gabrieljablonski
801033bd5f Merge branch 'main' into chore/merge-upstream-4.5.0 2025-08-20 11:20:31 -03:00
Cayo P. R. Oliveira
c6f9e814c2
feat: add customizable signature position and separator options (#78)
* feat: add customizable signature position and separator options

* fix: correct default value note for signatureSeparator and ensure reactivity

* fix: correct watcher boolean conversion and add immediate ui_settings updates

- Fix watchers to convert string props to boolean values for reactive refs
- Add immediate event handlers for switch changes to update ui_settings in real-time
- Ensure proper synchronization between switch states and user.ui_settings

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: split signature content and ui_settings updates to resolve persistence bug

- Use updateUISettings store action for signature_position and signature_separator
- Keep updateProfile for message_signature content only
- Fixes FormData serialization issue that corrupted nested ui_settings object
- Add diagnostic logging to verify data flow

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* clean: remove diagnostic console logging from updateSignature method

- Remove temporary console.log statements added for verification
- Keep core implementation that splits signature content and ui_settings updates
- Keep console.error for proper error handling with eslint-disable comment
- Implementation now ready for production use

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: updateUISettings call in updateSignature method

* chore: move signature application to send-time and add button highlighting (#79)

* fix: move signature application from editor manipulation to send-time

- Remove addSignature/removeSignature/toggleSignatureInEditor from WootWriter
- Remove signature logic from draft handling and canned response insertion
- Apply signatures only in getMessagePayload during message sending
- Add button highlighting for signature toggle when activated
- Prevents signature duplication and persistence in editor content
- Fixes signature position toggle bug

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: escape signature separator to prevent markdown setext heading interpretation

- Escape '--' separator as '\--' in appendSignature to prevent H2 heading creation
- Update removeSignature to handle escaped separators correctly
- Fixes signature separator being rendered as markdown instead of plain text
- Refactor nested ternary to fix ESLint error

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: prevent signature separator markdown interpretation in message processing

- Add fix_signature_separator_markdown method to escape '--' separators
- Update ensure_processed_message_content to fix separators before saving
- Prevents signature separators from being interpreted as setext headings
- Ensures correct message display in channels and email notifications

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: update separator format to use \n--\n instead of escaping

- Change separator delimiter from '\--' to '\n--\n' format
- Update removeSignature function to handle new separator format correctly
- Simplify message processing since separators are already properly formatted
- Ensures consistent separator handling across frontend and backend

Co-Authored-By: cayo@fazer.ai <cayoproliveira@gmail.com>

* fix: update signature delimiter format to include extra new lines

* chore: remove comment about signature application logic

* refactor: remove unused method and comments related to signature separator markdown processing

* chore: simplify slash command detection by using updatedMessage directly

* refactor: remove signature logic from draft message handling

* refactor: simplify body empty check by removing signature manipulation logic

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cayo@fazer.ai <cayoproliveira@gmail.com>

* refactor: extract signature settings logic into a separate method

* fix: handle nil ui_settings in signature position and separator methods

* fix: update return value of findSignatureInBody to include position information

* fix: update signature handling in findSignatureInBody and related methods

* fix: adjust delimiter length handling in removeSignature function

* test: add cases for appending, removing, and replacing signatures with various separators

* test: add cases for signature position and separator handling

* test: add cases for updating signature position and separator in ui_settings

* fix: correct typo in comment for findSignatureInBody function

* refactor: simplify translation function calls in MessageSignature component

* chore: refactoring

* chore: refactor

* feat: switch -> select

* chore: refactor and undo changes

* chore: refactor and undo changes

* chore: refactor

* fix: remove old select component usage

* chore: remove useless style

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2025-08-17 23:01:41 -03:00
Clairton Rodrigo Heinzen
b711bfd2ca
feat: Add automation rule event conversation resolved (#9669)
# Description

add automation rule event conversation resolved

<img width="1552" alt="Captura de Tela 2024-06-22 às 21 25 39"
src="https://github.com/chatwoot/chatwoot/assets/471685/b3a64ebc-35c8-468c-a0e5-7974134a40f9">

---------

Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-08-13 12:57:14 +05:30
Muhsin Keloth
5c560c7628
feat: WhatsApp enhanced templates front end changes (#12117)
Part of the https://github.com/chatwoot/chatwoot/pull/11997

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
2025-08-12 18:53:19 +05:30
Sivin Varghese
d908c880d2
chore: Replace Thumbnail with Avatar (#12119) 2025-08-11 15:47:17 +05:30