Commit Graph

8 Commits

Author SHA1 Message Date
Cayo P. R. Oliveira
c6bfd1eed3
feat: schedule messages recurrence (#240)
* feat(scheduled-messages): add recurring scheduled messages

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Delete the now-unused RecurringScheduledMessageItem.vue.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2026-03-19 22:51:14 -03:00
Cayo P. R. Oliveira
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
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
Cayo P. R. Oliveira
f9d1146cb0
feat: mensagens agendadas (#198)
* feat:  Adds model for scheduling messages

* feat: Implement scheduled message handling and processing jobs

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

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

* feat: Add ScheduledMessagePolicy for managing access to scheduled messages

* feat: Add routes for managing scheduled messages

* feat: Add scheduled message event handling and broadcasting

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

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

* feat: Ensure scheduled message updates trigger dispatch event

* feat: Add mutation types for managing scheduled messages

* feat: Add additionalAttributes prop to Message component and provider

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

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

* feat: implement scheduled messages functionality

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

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

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

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

* feat: add create_scheduled_message action to automation rule attributes

* feat: implement create_scheduled_message action and enhance attachment handling

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

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

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

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

* chore: prepend_mod_with to ScheduledMessagesController for better module handling

* fix: attachment visibility in ScheduledMessageItem component

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

* refactor: simplify ScheduledMessagesAPI methods by removing unnecessary instance variable

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

* refactor: update status configuration to use label keys

* chore: update date formatting in ScheduledMessageItem component

* refactor: collapse logic to checkOverflow and update related functionality

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

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

* fix: send message shortcut

* chore: handle errors in scheduled message submission

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

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

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

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

* refactor: rename build_message method for send_message

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

* chore: ignore unnecessary check

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

* refactor: use scheduled message factorie creation in specs

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

* fix: change scheduled_messages association to destroy dependent records

* refactor: remove unused attributes from scheduled message payload builder

* chore: update scheduled message retrieval to use conversation association

* chore: correct cron format for scheduled messages job

* chore: remove migration for author_type in scheduled_messages

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

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

* feat: include additional_attributes in message JSON response

* feat: enhance scheduled message validation and localization support

* chore: update scheduled message display

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

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

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

* feat: update scheduled messages localization and enhance content validation

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

* fix: reorder condition for Facebook channel message length computation

* fix:  change detection for attachments in scheduled messages

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

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

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

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

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

* docs: add scheduled messages management APIs and payload definitions

---------

Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
2026-01-30 22:08:16 -03:00
Shivam Mishra
59bae616cf
feat: add UI for contact notes (#11358)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2025-04-29 16:08:20 +05:30
Pranav
d070743383
feat(ee): Add Captain features (#10665)
Migration Guide: https://chwt.app/v4/migration

This PR imports all the work related to Captain into the EE codebase. Captain represents the AI-based features in Chatwoot and includes the following key components:

- Assistant: An assistant has a persona, the product it would be trained on. At the moment, the data at which it is trained is from websites. Future integrations on Notion documents, PDF etc. This PR enables connecting an assistant to an inbox. The assistant would run the conversation every time before transferring it to an agent.
- Copilot for Agents: When an agent is supporting a customer, we will be able to offer additional help to lookup some data or fetch information from integrations etc via copilot.
- Conversation FAQ generator: When a conversation is resolved, the Captain integration would identify questions which were not in the knowledge base.
- CRM memory: Learns from the conversations and identifies important information about the contact.

---------

Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
2025-01-14 16:15:47 -08:00
Sivin Varghese
41106bccb7
chore: Fix issues with Contact pages (#10544) 2024-12-05 14:24:10 -08:00
Sivin Varghese
79daf56c31
feat: Add contact note item component (#10479) 2024-11-21 13:47:57 -08:00