Commit Graph

3060 Commits

Author SHA1 Message Date
Cayo P. R. Oliveira
8ffdc16faf
fix(schedule): enhance attachment handling in scheduled message modal (#239)
* fix(schedule): enhance attachment handling in scheduled message modal

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The 'Custom' option with full DatePicker is preserved.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(schedule): improve PT natural language preprocessing

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

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

Tests: 50 passing (was 40)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(schedule): hide seconds column in datepicker

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

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

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

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

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

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

* chore: remove leftover planning files

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix: prevent scroll overshoot when navigating to message

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

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

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 22:35:04 -03:00
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
Gabriel Jablonski
05d47931be
feat: add kanban feature promotion with upgrade banner (#235)
* feat: add kanban feature promotion with upgrade banner

Add a Kanban sidebar item visible to all users that shows a locked
feature promotion page. Super admins see an upgrade button linking to
fazer.ai, while non-admins see a message to contact their administrator.

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

* fix: allow custom_role users to access kanban promotion page

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:31:53 -03:00
gabrieljablonski
477e0ad445 chore: fix fazer.ai render 2026-03-11 19:12:04 -03:00
gabrieljablonski
a4ff73d496 feat: add validation and error messages for custom HTML fields in portal settings 2026-03-09 11:48:53 -03:00
Gabriel Jablonski
eaac65c973
feat: add custom HTML fields for portals (#233) 2026-03-09 11:47:41 -03:00
Gabriel Jablonski
195713cbfe
chore: general improvements (#232) 2026-03-03 14:08:56 -03:00
Gabriel Jablonski
56c5609ca0
feat: add per-inbox signature management (#226)
* feat: add per-inbox signature management

- Introduced `InboxSignature` model to manage signatures specific to each inbox.
- Added API endpoints for fetching, creating, updating, and deleting inbox signatures.
- Updated UI components to support inbox-specific signatures, including overrides for signature position and separator.
- Implemented a new composable `useInboxSignatures` for managing inbox signatures in the frontend.
- Enhanced existing components to utilize inbox signatures, including the reply box and message signature settings.
- Added tests for the new inbox signatures functionality, ensuring proper behavior of the API and model validations.
- Updated translations for new UI elements related to inbox signatures.

* feat: implement inbox access validation and add related tests

* feat: enhance inbox signatures fetching and management logic
2026-02-26 19:53:03 -03:00
Gabriel Jablonski
21007bd20b
feat: add show author option to portal settings and update related views (#225)
* feat: add show author option to portal settings and update related views

* fix: update portal reference to use local variable for show author condition

* feat: enhance show_author handling in portal config and add related tests
2026-02-26 14:32:52 -03:00
Shivam Mishra
c026ee2fc8 fix: url endpoint
fix: spec
2026-02-25 12:28:30 -03:00
Gabriel Jablonski
ce39e54308
feat: add audio transcoding support for WhatsApp Cloud API (#220)
* feat: add audio transcoding support for WhatsApp Cloud API

- Introduced `Audio::TranscodeService` to handle audio transcoding to OGG/Opus format.
- Updated `Messages::MessageBuilder` to transcode audio attachments based on `transcode_audio` parameter.
- Enhanced `WhatsappCloudService` to normalize audio content types and send voice flag for recorded audio in OGG format.
- Added utility functions for audio conversion in JavaScript.
- Updated Dockerfile to include FFmpeg for audio processing.
- Added tests for audio transcoding and WhatsApp Cloud service interactions.

* feat: enhance audio handling with transcoding support and error management

* feat: improve audio transcoding error handling and enhance audio recording features

* feat: enhance audio transcoding process and error handling for better reliability

* feat: update recorded audio handling to support boolean and array formats
2026-02-22 16:21:50 -03:00
Gabriel Jablonski
3b8a38b153
feat: Implement existing template linking for CSAT surveys (#218)
* feat: Implement existing template linking for CSAT surveys

- Added functionality to link existing CSAT templates for WhatsApp channels.
- Introduced a new component for selecting existing templates.
- Updated the dashboard settings page to support template mode switching between creating new and using existing templates.
- Enhanced the CSAT template management service to handle linking existing templates and fetching available templates.
- Updated API routes to include linking and fetching available templates.
- Added tests for the new linking functionality and template availability checks.

* feat: Enhance CSAT template handling and validation across services and components

* feat: Refactor body variable extraction for CSAT templates and update related validations

* feat: Add linked_at field to CSAT template responses and update related handling

* feat: Add tests for ConversationDrop date formatting and CSAT template body variable handling
2026-02-18 18:00:29 -03:00
gabrieljablonski
360ad59732 feat: add enableCopilot prop to Editor and update ScheduledMessageModal to disable copilot 2026-02-18 10:47:25 -03:00
gabrieljablonski
85ec13a273 chore: update DropdownBody styles for better overflow handling 2026-02-18 10:23:14 -03:00
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
Sivin Varghese
c5f6844877
fix: Disable reply editor outside WhatsApp reply window (#13454) 2026-02-17 14:07:36 +05:30
Sivin Varghese
fb2f5e1d42
fix: Persist compose form state on accidental outside click (#13529) 2026-02-17 13:57:44 +05:30
Sivin Varghese
cfe3061b5d
feat: Allow removing labels via conversation context menu (#13525)
# Pull Request Template

## Description

This PR adds support for removing labels from the conversation card
context menu. Assigned labels now show a checkmark, and clicking an
already-selected label will remove it.

Fixes
https://linear.app/chatwoot/issue/CW-6400/allow-removing-labels-directly-from-the-right-click-menu
https://github.com/chatwoot/chatwoot/issues/13367
## Type of change

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

## How Has This Been Tested?

**Screencast**


https://github.com/user-attachments/assets/4e3a6080-a67d-4851-9d10-d8dbf3ceeb04




## 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-17 13:30:55 +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
Tanmay Deep Sharma
9cd7c4ef89
fix: Enhance notification emails with message details and handle failed messages (#13273)
## Description

Handle messages with null content properly in UI and email notifications

## Type of change

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

## Relevant Screenshots:
<img width="688" height="765" alt="Screenshot 2026-01-21 at 4 43 00 PM"
src="https://github.com/user-attachments/assets/6a27c22e-2ae6-4377-a05d-cfa44bf181fe"
/>


## 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]
> **Medium Risk**
> Touches notification email templates and message rendering conditions;
mistakes could lead to missing content/attachments in emails or
incorrect UI visibility, but changes are localized and non-auth/security
related.
> 
> **Overview**
> Agent notification emails for *assigned* and *participating* new
messages now include the actual message details (sender name, rendered
text when present, and attachment links) and gracefully fall back when
content is unavailable.
> 
> To support this, the mailer now passes `@message` into Liquid via
`MessageDrop` (adding `attachments` URLs), and the dashboard message UI
now renders failed/external-error messages even when `content` is `null`
while tightening retry eligibility to require content or attachments
(and still within 1 day).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
475c8cedda54eb5e806990f977faf8098d0b27d8. 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>
2026-02-16 14:47:33 +05:30
Sojan Jose
fd5ac2a8a3
fix: apply installation branding replacement in tooltip copy (#13538)
## Summary
Fix hardcoded `Chatwoot` branding in two UI tooltips using the existing
`useBranding` flow so self-hosted/white-label deployments no longer show
the wrong brand text.

## Changes
- LabelSuggestion tooltip now uses:
  - `replaceInstallationName($t('LABEL_MGMT.SUGGESTIONS.POWERED_BY'))`
- Message avatar tooltip (native app/external echo) now uses:
  - `replaceInstallationName(t('CONVERSATION.NATIVE_APP_ADVISORY'))`

## Why
This follows the existing branding pattern already used in the product
and keeps behavior consistent across deployments.

## Notes
- No change to message logic or API behavior.
- `AGENTS.md` updated with a branding guidance note.

## Fixes
- Fixes https://github.com/chatwoot/chatwoot/issues/13306
- Fixes https://github.com/chatwoot/chatwoot/issues/13466

## Testing

<img width="195" height="155" alt="Screenshot 2026-02-13 at 3 55 39 PM"
src="https://github.com/user-attachments/assets/5b295cdd-6e5d-42c0-bbd7-23ba7052e1c3"
/>
<img width="721" height="152" alt="Screenshot 2026-02-13 at 3 55 48 PM"
src="https://github.com/user-attachments/assets/19cec2a0-451f-4fb3-bd61-7c2e591fc3c7"
/>
2026-02-13 16:47:25 -08:00
Tanmay Deep Sharma
7b512bd00e
fix: V2 Assignment service enhancements (#13036)
## Linear Ticket:
https://linear.app/chatwoot/issue/CW-6081/review-feedback

## Description

Assignment V2 Service Enhancements

- Enable Assignment V2 on plan upgrade
- Fix UI issue with fair distribution policy display
- Add advanced assignment feature flag and enhance Assignment V2
capabilities

## Type of change

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

## How Has This Been Tested?

This has been tested using the UI.

## 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]
> **Medium Risk**
> Changes auto-assignment execution paths, rate limiting defaults, and
feature-flag gating (including premium plan behavior), which could
affect which conversations get assigned and when. UI rewires inbox
settings and policy flows, so regressions are possible around
navigation/linking and feature visibility.
> 
> **Overview**
> **Adds a new premium `advanced_assignment` feature flag** and uses it
to gate capacity/balanced assignment features in the UI (sidebar entry,
settings routes, assignment-policy landing cards) and backend
(Enterprise balanced selector + capacity filtering).
`advanced_assignment` is marked premium, included in Business plan
entitlements, and auto-synced in Enterprise accounts when
`assignment_v2` is toggled.
> 
> **Improves Assignment V2 policy UX** by adding an inbox-level
“Conversation Assignment” section (behind `assignment_v2`) that can
link/unlink an assignment policy, navigate to create/edit policy flows
with `inboxId` query context, and show an inbox-link prompt after
creating a policy. The policy form now defaults to enabled, disables the
`balanced` option with a premium badge/message when unavailable, and
inbox lists support click-to-navigate.
> 
> **Tightens/adjusts auto-assignment behavior**: bulk assignment now
requires `inbox.enable_auto_assignment?`, conversation ordering uses the
attached `assignment_policy` priority, and rate limiting uses
`assignment_policy` config with an infinite default limit while still
tracking assignments. Tests and i18n strings are updated accordingly.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
23bc03bf75ee4376071e4d7fc7cd564c601d33d7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2026-02-11 12:24:45 +05:30
Sivin Varghese
e65ea24360
fix: Wrong assignee displayed after switching conversations (#13501) 2026-02-10 15:23:55 +05:30
Sivin Varghese
b252656984
fix: Prevent race condition in conversation dataFetched flag (#13492)
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2026-02-10 15:23:14 +05:30
Shivam Mishra
0e30e3c00a
fix: add loading and silent retry to summary reports (#13455)
For large accounts, summary report queries can take several seconds to
complete, often times hitting the 15-second production request timeout.
The existing implementation silently swallows these failures and
provides no feedback during loading. Users see stale data with no
indication that a fetch is in progress, and if they interact with
filters while a request is in flight, they trigger race conditions that
can result in mismatched data being displayed.

This is a UX-level fix for what is fundamentally a performance problem.
While the underlying query performance is addressed separately, users
need proper feedback either way

## Approach

The PR adds three things: 

1. A loading overlay on the table, to provide feedback on loading state
2. Disabled filter inputs during loading so that the user does not
request new information that can cause race conditions in updating the
store
3. Silent retry before showing an error.

The retry exists because these queries often succeed on the second
attempt—likely due to database query caching. Rather than immediately
showing an error and forcing the user to manually retry, we do it
automatically. If the second attempt also fails, we show a toast so the
user knows something went wrong.

The store previously caught and discarded errors entirely. It now
rethrows them after resetting the loading flag, allowing components to
handle failures as they see fit.

### Previews

#### Double Retry and Error


https://github.com/user-attachments/assets/c189b173-8017-44b7-9493-417d65582c95

#### Loading State


https://github.com/user-attachments/assets/9f899c20-fbad-469b-93cc-f0d05d0853b0

---------

Co-authored-by: iamsivin <iamsivin@gmail.com>
2026-02-06 19:53:46 +05:30
Sivin Varghese
0d3b59fd9c
feat: Refactor reports filters (#13443) 2026-02-06 18:22:30 +05:30
Gabriel Jablonski
42b2530a53
fix: update default delay for scheduled messages in automation (#209)
* fix: update default delay for scheduled messages in automation

* fix: set default delay for scheduled messages to 24 hours in automation
2026-02-04 18:23:25 -03:00
Tanmay Deep Sharma
7ade9061a8
feat: display total FAQ count in Related FAQs dialog (#13433)
## Description

Display the total count of generated FAQs in the Related FAQs dialog
title to give users immediate visibility into how many FAQs were
generated from a document.

## Type of change

Please delete options that are not relevant.

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

## Snapshots?

<img width="717" height="268" alt="Screenshot 2026-02-04 at 1 47 36 AM"
src="https://github.com/user-attachments/assets/c3e927ce-6d09-499d-8d02-8a44e0c353e2"
/>


## 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]
> **Low Risk**
> Small UI-only change using existing store metadata; risk is limited to
incorrect/blank counts if `meta.totalCount` is missing or stale.
> 
> **Overview**
> Updates the `RelatedResponses` dialog to display the total related
response count in the title by reading
`captainResponses/getMeta.totalCount` (defaulting to 0) and appending it
as `(<count>)`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7cd67c9991faceeff33d33c319e324b1c6cf73f4. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-02-04 11:27:51 +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
Gabriel Jablonski
3dd8735054
fix: update whatsapp available template languages for CSAT (#207) 2026-02-03 10:14:56 -03:00
Gabriel Jablonski
e5b5746946
feat: add localized date format for scheduled messages (#206) 2026-02-03 09:22:25 -03:00
Gabriel Jablonski
da466c88a6
feat(scheduled-messages): add WhatsApp templates (#205)
* feat(scheduled-messages): add WhatsApp templates

* fix: update inboxId prop type to accept both Number and String; localize template labels in conversation.json
2026-02-02 19:50:17 -03:00
Muhsin Keloth
b686d14044
feat: Handle external echo messages from native apps (#13371)
When businesses use WhatsApp Business App (co-existence mode) or
Instagram App or TikTok alongside Chatwoot, messages sent from the
native apps were not synced properly back to Chatwoot. This left agents
with an incomplete conversation history and no visibility into responses
sent outside the dashboard. Additionally, if these echo messages did
arrive, they appeared as "Sent by: Bot" in the UI since they had no
sender, making it confusing for agents.

This PR subscribes to WhatsApp `smb_message_echoes` webhook events and
routes them through the existing service with an `outgoing_echo` flag,
mirroring how Instagram already handles echoes. On the Instagram side,
echo messages now also carry the `external_echo` content attribute and
`delivered` status.

On the frontend, messages with `externalEcho` are distinguished from bot
messages showing a "Native app" avatar and an advisory note encouraging
agents to reply from Chatwoot to maintain the service window.

<img width="1518" height="524" alt="CleanShot 2026-01-29 at 13 37 57@2x"
src="https://github.com/user-attachments/assets/5aa0b552-6382-441f-96aa-9a62ca716e4a"
/>


Fixes
https://linear.app/chatwoot/issue/CW-4204/display-messages-not-sent-from-chatwoot-in-case-of-outgoing-echo
Fixes
https://linear.app/chatwoot/issue/PLA-33/incoming-from-me-messages-from-whatsapp-business-app-are-not-falling
2026-02-02 15:52:53 +05:30
Shivam Mishra
133fb1bcf6
feat: add mark pending action to automation (#13378) 2026-02-02 11:59:51 +05:30
Gabriel Jablonski
fb6fec167b
chore: general improvements (#204)
* chore: update scheduled messages author association to nullable and adjust related specs

* chore: update sender handling for WhatsApp messages and add external sender name
2026-02-01 14:25:06 -03:00
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
Sivin Varghese
85324c82fa
fix: Formatting issue with reply preview content (#13399) 2026-01-30 16:35:32 +05:30
Gabriel Jablonski
e1a5e4339d
chore: remove PromoBanner component and related logic from WhatsApp channels (#202)
* chore: remove PromoBanner component and related logic from WhatsApp channels

* chore: remove Z-API promotional banner from inbox management localization files
2026-01-29 16:30:05 -03:00
Muhsin Keloth
acd0f1457e
feat: Add TikTok social profile display support (#13384)
Fixes
https://linear.app/chatwoot/issue/PLA-77/store-tiktok-user-id-in-contact-attributes
Adds support for displaying TikTok profile links in the contact sidebar,
matching the behavior of other social channels (Instagram, Facebook,
Twitter, etc.).

<img width="400" height="600" alt="CleanShot 2026-01-28 at 15 48 14@2x"
src="https://github.com/user-attachments/assets/c861e6af-6a45-40ff-a4bf-7d46b8937691"
/>
2026-01-29 09:26:10 +04:00
Sivin Varghese
2a69b37958
chore: Update theme colors and add new Inter variable fonts (#13347)
# Pull Request Template

## Description

This PR includes the following updates:

1. Updated the design system color tokens by introducing new tokens for
surfaces, overlays, buttons, labels, and cards, along with refinements
to existing shades.
2. Refreshed both light and dark themes with adjusted background,
border, and solid colors.
3. Replaced static Inter font files with the Inter variable font
(including italic), supporting weights from 100–900.
4. Added custom font weights (420, 440, 460, 520) along with custom
typography classes to enable more fine-grained and consistent typography
control.


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

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2026-01-28 14:36:04 -08:00
Muhsin Keloth
3b612e2b20
chore: Add unsupported message for Tiktok (#13380)
This PR adds the unsupported messages for tiktok.
Fixes
https://linear.app/chatwoot/issue/CW-6407/add-support-for-unsupported-message
2026-01-28 19:34:11 +04:00
Muhsin Keloth
aaeea6c9bf
feat: Display story replies with attachment and context label (#13356)
Fixes https://github.com/chatwoot/chatwoot/issues/13354
Fixes
https://linear.app/chatwoot/issue/CW-6394/story-responses-are-not-being-shown-in-the-ui
When someone replies to your Instagram story, agents in Chatwoot only
see the reply text with no story image and no indication that it was a
story reply. This makes it impossible to understand what the customer is
responding to the message looks like a random text with no context. For
example, if a customer replies "Love this!" to your story, the agent
just sees "Love this!" with no way to know which story triggered the
conversation. This PR fixes the issue by storing the story attachment
and adding a context label.

<img width="1408" height="2052" alt="CleanShot 2026-01-27 at 19 19
38@2x"
src="https://github.com/user-attachments/assets/341afea9-98e3-4e47-b2fa-ef77fe32851f"
/>
2026-01-28 16:47:04 +04:00
Pranav
7cddba2b08
feat: Add infinite scroll to contacts search page (#13376)
## Summary
- Add `has_more` to contacts search API response to enable infinite
scroll without expensive count queries
- Set `count` to the number of items in the current page instead of
total count
- Implement "Load more" button for contacts search results
- Keep existing contacts visible while loading additional pages

## Changes

### Backend
- Add `fetch_contacts_with_has_more` method that fetches N+1 records to
determine if more pages exist
- Return `has_more` in search endpoint meta response
- Set `count` to current page size instead of total count

### Frontend
- Add `APPEND_CONTACTS` mutation for appending contacts without clearing
existing ones
- Update search action to support `append` parameter
- Add `ContactsLoadMore` component with loading state
- Update `ContactsListLayout` to support infinite scroll mode
- Update `ContactsIndex` to use infinite scroll for search view
2026-01-27 18:55:19 -08:00
Gabriel Jablonski
4ca0ef22c0
feat: edit contact modal confirm discard (#199)
* feat(contact): add confirmation dialog for discarding unsaved changes in edit contact modal

* chore: add GitHub Copilot instructions for project guidelines

* feat(contact): update confirmation dialog logic for discarding unsaved changes in edit contact modal
2026-01-27 20:03:42 -03:00
Muhsin Keloth
04b2901e1f
feat: Conversation workflows(EE) (#13040)
We are expanding Chatwoot’s automation capabilities by
introducing **Conversation Workflows**, a dedicated section in settings
where teams can configure rules that govern how conversations are closed
and what information agents must fill before resolving. This feature
helps teams enforce data consistency, collect structured resolution
information, and ensure downstream reporting is accurate.

Instead of having auto‑resolution buried inside Account Settings, we
introduced a new sidebar item:
- Auto‑resolve conversations (existing behaviour)
- Required attributes on resolution (new)

This groups all conversation‑closing logic into a single place.

#### Required Attributes on Resolve

Admins can now pick which custom conversation attributes must be filled
before an agent can resolve a conversation.

**How it works**

- Admin selects one or more attributes from the list of existing
conversation level custom attributes.
- These selected attributes become mandatory during resolution.
- List all the attributes configured via Required Attributes (Text,
Number, Link, Date, List, Checkbox)
- When an agent clicks Resolve Conversation:
If attributes already have values → the conversation resolves normally.
If attributes are missing → a modal appears prompting the agent to fill
them.

<img width="1554" height="1282" alt="CleanShot 2025-12-10 at 11 42
23@2x"
src="https://github.com/user-attachments/assets/4cd5d6e1-abe8-4999-accd-d4a08913b373"
/>


#### Custom Attributes Integration

On the Custom Attributes page, we will surfaced indicators showing how
each attribute is being used.

Each attribute will show badges such as:

- Resolution → used in the required‑on‑resolve workflow

- Pre‑chat form → already existing

<img width="2390" height="1822" alt="CleanShot 2025-12-10 at 11 43
42@2x"
src="https://github.com/user-attachments/assets/b92a6eb7-7f6c-40e6-bf23-6a5310f2d9c5"
/>


#### Admin Flow

- Navigate to Settings → Conversation Workflows.
- Under Required attributes on resolve, click Add Required Attribute.
- Pick from the dropdown list of conversation attributes.
- Save changes.

Agents will now be prompted automatically whenever they resolve.

<img width="2434" height="872" alt="CleanShot 2025-12-10 at 11 44 42@2x"
src="https://github.com/user-attachments/assets/632fc0e5-767c-4a1c-8cf4-ffe3d058d319"
/>



#### NOTES
- The Required Attributes on Resolve modal should only appear when
values are missing.
- Required attributes must block the resolution action until satisfied.
- Bulk‑resolve actions should follow the same rules — any conversation
missing attributes cannot be bulk‑resolved, rest will be resolved, show
a notification that the resolution cannot be done.
- API resolution does not respect the attributes.

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
2026-01-27 11:36:20 +04:00
Gabriel Jablonski
0de6001b97
feat: add message editing functionality with UI support (#195)
* feat: add message editing functionality with UI support

* feat: enhance message editing with content length validation and context menu adjustments
2026-01-24 23:25:11 -03:00
Pranav
747d451387
chore: Improve signup flow, reduce the number of inputs (#13350)
- Improved design for the Chatwoot sign up page
2026-01-22 18:47:42 -08:00