Initial commit: Add logistics and order_detail message types
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled

- Add Logistics component with progress tracking
- Add OrderDetail component for order information
- Support data-driven steps and actions
- Add blue color scale to widget SCSS
- Fix node overflow and progress bar rendering issues
- Add English translations for dashboard components

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Liang XJ
2026-01-26 11:16:56 +08:00
commit 092fb2e083
7646 changed files with 975643 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
# Delete migration and spec after 2 consecutive releases.
class Migration::AddSearchIndexesJob < ApplicationJob
queue_as :scheduled_jobs
def perform
ActiveRecord::Migration[6.1].add_index(:messages, [:account_id, :inbox_id], algorithm: :concurrently)
ActiveRecord::Migration[6.1].add_index(:messages, :content, using: 'gin', opclass: :gin_trgm_ops, algorithm: :concurrently)
ActiveRecord::Migration[6.1].add_index(
:contacts,
[:name, :email, :phone_number, :identifier],
using: 'gin',
opclass: :gin_trgm_ops,
name: 'index_contacts_on_name_email_phone_number_identifier',
algorithm: :concurrently
)
end
end

View File

@@ -0,0 +1,13 @@
class Migration::BackfillCompaniesContactsCountJob < ApplicationJob
queue_as :async_database_migration
def perform
return unless ChatwootApp.enterprise?
Company.find_in_batches(batch_size: 100) do |company_batch|
company_batch.each do |company|
Company.reset_counters(company.id, :contacts)
end
end
end
end

View File

@@ -0,0 +1,14 @@
class Migration::ConversationBatchCacheLabelJob < ApplicationJob
queue_as :async_database_migration
# To cache the label, we simply access it from the object and save it. Anytime the object is
# saved in the future, ActsAsTaggable will automatically recompute it. This process is done
# initially when the user has not performed any action.
# Reference: https://github.com/mbleigh/acts-as-taggable-on/wiki/Caching
def perform(conversation_batch)
conversation_batch.each do |conversation|
conversation.label_list
conversation.save!
end
end
end

View File

@@ -0,0 +1,9 @@
class Migration::ConversationCacheLabelJob < ApplicationJob
queue_as :async_database_migration
def perform(account)
account.conversations.find_in_batches(batch_size: 100) do |conversation_batch|
Migration::ConversationBatchCacheLabelJob.perform_later(conversation_batch)
end
end
end

View File

@@ -0,0 +1,17 @@
# Delete migration and spec after 2 consecutive releases.
class Migration::ConversationsFirstReplySchedulerJob < ApplicationJob
queue_as :scheduled_jobs
def perform(account)
account.conversations.each do |conversation|
# rubocop:disable Rails/SkipsModelValidations
if conversation.messages.outgoing.where("(additional_attributes->'campaign_id') is null").count.positive?
conversation.update_columns(first_reply_created_at: conversation.messages.outgoing.where("(additional_attributes->'campaign_id') is null")
.first.created_at)
else
conversation.update_columns(first_reply_created_at: nil)
end
# rubocop:enable Rails/SkipsModelValidations
end
end
end

View File

@@ -0,0 +1,8 @@
# Delete migration and spec after 2 consecutive releases.
class Migration::RemoveMessageNotifications < ApplicationJob
queue_as :scheduled_jobs
def perform
Notification.where(primary_actor_type: 'Message').in_batches(of: 100).delete_all
end
end

View File

@@ -0,0 +1,19 @@
# Delete migration and spec after 2 consecutive releases.
class Migration::RemoveStaleNotificationsJob < ApplicationJob
queue_as :scheduled_jobs
def perform
remove_invalid_messages
end
private
def remove_invalid_messages
deleted_ids = []
Message.unscoped.distinct.pluck(:inbox_id).each_slice(1000) do |id_list|
deleted_ids = (id_list - Inbox.where(id: id_list).pluck(:id))
Message.where(inbox_id: deleted_ids.flatten).destroy_all
end
end
end

View File

@@ -0,0 +1,69 @@
# Delete migration and spec after 2 consecutive releases.
class Migration::UpdateFirstResponseTimeInReportingEventsJob < ApplicationJob
include ReportingEventHelper
queue_as :async_database_migration
def perform(account)
get_conversations_with_bot_handoffs(account)
account.reporting_events.where(name: 'first_response').find_each do |event|
conversation = event.conversation
# if the conversation has a bot handoff event, we don't need to update the response_time
next if conversation.nil? || @conversations_with_handoffs.include?(conversation.id)
update_event_data(event, conversation)
end
end
def get_conversations_with_bot_handoffs(account)
@conversations_with_handoffs = account.reporting_events.where(name: 'conversation_bot_handoff').pluck(:conversation_id)
end
def update_event_data(event, conversation)
last_bot_reply = conversation.messages.where(sender_type: 'AgentBot').order(created_at: :asc).last
return if last_bot_reply.blank?
first_human_reply = conversation.messages.where(sender_type: 'User').order(created_at: :asc).first
return if first_human_reply.blank?
# accomodate for campaign if required
# new_value = difference between the first_human_reply and the first_bot_reply if it exists or first_human_reply and created at
#
# conversation bot conversation
# start handoff resolved
# | | |
# |____|___|_________|____|_______|_____|________|
# bot reply ^ ^ human reply
# | |
# | |
# last_bot_reply first_human_reply
#
#
# bot handoff happens at the last_bot_reply created time
# the response time is the time between last bot reply created and the first human reply created
return if last_bot_reply.created_at.to_i >= first_human_reply.created_at.to_i
# this means a bot replied existed, so we need to update the event_start_time
update_event_details(event, last_bot_reply, first_human_reply, conversation.inbox)
end
def update_event_details(event, last_bot_reply, first_human_reply, inbox)
# rubocop:disable Rails/SkipsModelValidations
event.update_columns(event_start_time: last_bot_reply.created_at,
event_end_time: first_human_reply.created_at,
value: calculate_event_value(last_bot_reply, first_human_reply),
value_in_business_hours: calculate_event_value_in_business_hours(inbox, last_bot_reply,
first_human_reply),
user_id: event.user_id || first_human_reply.sender_id)
# rubocop:enable Rails/SkipsModelValidations
end
def calculate_event_value(last_bot_reply, first_human_reply)
first_human_reply.created_at.to_i - last_bot_reply.created_at.to_i
end
def calculate_event_value_in_business_hours(inbox, last_bot_reply, first_human_reply)
business_hours(inbox, last_bot_reply.created_at, first_human_reply.created_at)
end
end