Merge branch 'develop' into feat/shopify-app-store-install-flow
This commit is contained in:
@@ -4,6 +4,11 @@
|
||||
|
||||
- **Setup**: `bundle install && pnpm install`
|
||||
- **Run Dev**: `pnpm dev` or `overmind start -f ./Procfile.dev`
|
||||
- **Seed Local Test Data**: `bundle exec rails db:seed` (quickly populates minimal data for standard feature verification)
|
||||
- **Seed Search Test Data**: `bundle exec rails search:setup_test_data` (bulk fixture generation for search/performance/manual load scenarios)
|
||||
- **Seed Account Sample Data (richer test data)**: `Seeders::AccountSeeder` is available as an internal utility and is exposed through Super Admin `Accounts#seed`, but can be used directly in dev workflows too:
|
||||
- UI path: Super Admin → Accounts → Seed (enqueues `Internal::SeedAccountJob`).
|
||||
- CLI path: `bundle exec rails runner "Internal::SeedAccountJob.perform_now(Account.find(<id>))"` (or call `Seeders::AccountSeeder.new(account: Account.find(<id>)).perform!` directly).
|
||||
- **Lint JS/Vue**: `pnpm eslint` / `pnpm eslint:fix`
|
||||
- **Lint Ruby**: `bundle exec rubocop -a`
|
||||
- **Test JS**: `pnpm test` or `pnpm test:watch`
|
||||
@@ -93,3 +98,7 @@ Practical checklist for any change impacting core logic or public APIs
|
||||
- When renaming/moving shared code, mirror the change in `enterprise/` to prevent drift.
|
||||
- Tests: Add Enterprise-specific specs under `spec/enterprise`, mirroring OSS spec layout where applicable.
|
||||
- When modifying existing OSS features for Enterprise-only behavior, add an Enterprise module (via `prepend_mod_with`/`include_mod_with`) instead of editing OSS files directly—especially for policies, controllers, and services. For Enterprise-exclusive features, place code directly under `enterprise/`.
|
||||
|
||||
## Branding / White-labeling note
|
||||
|
||||
- For user-facing strings that currently contain "Chatwoot" but should adapt to branded/self-hosted installs, prefer applying `replaceInstallationName` from `shared/composables/useBranding` in the UI layer (for example tooltip and suggestion labels) instead of adding hardcoded brand-specific copy.
|
||||
|
||||
@@ -6,12 +6,8 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
|
||||
|
||||
def create
|
||||
@user = User.from_email(params[:email])
|
||||
if @user
|
||||
@user.send_reset_password_instructions
|
||||
build_response(I18n.t('messages.reset_password_success'), 200)
|
||||
else
|
||||
build_response(I18n.t('messages.reset_password_failure'), 404)
|
||||
end
|
||||
@user&.send_reset_password_instructions
|
||||
build_response(I18n.t('messages.reset_password'), 200)
|
||||
end
|
||||
|
||||
def update
|
||||
|
||||
@@ -43,6 +43,7 @@ import VoiceCallBubble from './bubbles/VoiceCall.vue';
|
||||
|
||||
import MessageError from './MessageError.vue';
|
||||
import ContextMenu from 'dashboard/modules/conversations/components/MessageContextMenu.vue';
|
||||
import { useBranding } from 'shared/composables/useBranding';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Attachment
|
||||
@@ -143,6 +144,7 @@ const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const inboxGetter = useMapGetter('inboxes/getInbox');
|
||||
const inbox = computed(() => inboxGetter.value(props.inboxId) || {});
|
||||
const { replaceInstallationName } = useBranding();
|
||||
|
||||
/**
|
||||
* Computes the message variant based on props
|
||||
@@ -472,7 +474,7 @@ const avatarInfo = computed(() => {
|
||||
|
||||
const avatarTooltip = computed(() => {
|
||||
if (props.contentAttributes?.externalEcho) {
|
||||
return t('CONVERSATION.NATIVE_APP_ADVISORY');
|
||||
return replaceInstallationName(t('CONVERSATION.NATIVE_APP_ADVISORY'));
|
||||
}
|
||||
if (avatarInfo.value.name === '') return '';
|
||||
return `${t('CONVERSATION.SENT_BY')} ${avatarInfo.value.name}`;
|
||||
|
||||
+6
-2
@@ -2,6 +2,7 @@
|
||||
// components
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
|
||||
import { useBranding } from 'shared/composables/useBranding';
|
||||
|
||||
// composables
|
||||
import { useCaptain } from 'dashboard/composables/useCaptain';
|
||||
@@ -34,8 +35,9 @@ export default {
|
||||
},
|
||||
setup() {
|
||||
const { captainTasksEnabled } = useCaptain();
|
||||
const { replaceInstallationName } = useBranding();
|
||||
|
||||
return { captainTasksEnabled };
|
||||
return { captainTasksEnabled, replaceInstallationName };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -228,7 +230,9 @@ export default {
|
||||
<div class="sender--info has-tooltip" data-original-title="null">
|
||||
<Avatar
|
||||
v-tooltip.top="{
|
||||
content: $t('LABEL_MGMT.SUGGESTIONS.POWERED_BY'),
|
||||
content: replaceInstallationName(
|
||||
$t('LABEL_MGMT.SUGGESTIONS.POWERED_BY')
|
||||
),
|
||||
delay: { show: 600, hide: 0 },
|
||||
hideOnClick: true,
|
||||
}"
|
||||
|
||||
@@ -47,8 +47,10 @@ class Twilio::DeliveryStatusService
|
||||
@twilio_channel ||= if params[:MessagingServiceSid].present?
|
||||
::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid])
|
||||
elsif params[:AccountSid].present? && params[:From].present?
|
||||
::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid], phone_number: params[:From])
|
||||
::Channel::TwilioSms.find_by(account_sid: params[:AccountSid], phone_number: params[:From])
|
||||
end
|
||||
log_channel_not_found if @twilio_channel.blank?
|
||||
@twilio_channel
|
||||
end
|
||||
|
||||
def message
|
||||
@@ -56,4 +58,14 @@ class Twilio::DeliveryStatusService
|
||||
|
||||
@message ||= twilio_channel.inbox.messages.find_by(source_id: params[:MessageSid])
|
||||
end
|
||||
|
||||
def log_channel_not_found
|
||||
Rails.logger.warn(
|
||||
'[TWILIO] Delivery status channel lookup failed ' \
|
||||
"account_sid=#{params[:AccountSid]} " \
|
||||
"from=#{params[:From]} " \
|
||||
"messaging_service_sid=#{params[:MessagingServiceSid]} " \
|
||||
"message_sid=#{params[:MessageSid]}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,12 +26,23 @@ class Twilio::IncomingMessageService
|
||||
def twilio_channel
|
||||
@twilio_channel ||= ::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid]) if params[:MessagingServiceSid].present?
|
||||
if params[:AccountSid].present? && params[:To].present?
|
||||
@twilio_channel ||= ::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid],
|
||||
phone_number: params[:To])
|
||||
@twilio_channel ||= ::Channel::TwilioSms.find_by(account_sid: params[:AccountSid],
|
||||
phone_number: params[:To])
|
||||
end
|
||||
log_channel_not_found if @twilio_channel.blank?
|
||||
@twilio_channel
|
||||
end
|
||||
|
||||
def log_channel_not_found
|
||||
Rails.logger.warn(
|
||||
'[TWILIO] Incoming message channel lookup failed ' \
|
||||
"account_sid=#{params[:AccountSid]} " \
|
||||
"to=#{params[:To]} " \
|
||||
"messaging_service_sid=#{params[:MessagingServiceSid]} " \
|
||||
"sms_sid=#{params[:SmsSid]}"
|
||||
)
|
||||
end
|
||||
|
||||
def inbox
|
||||
@inbox ||= twilio_channel.inbox
|
||||
end
|
||||
|
||||
@@ -41,8 +41,7 @@ en:
|
||||
invalid_email: 'Please enter a valid email address'
|
||||
authentication_failed: 'Authentication failed. Please check your credentials and try again.'
|
||||
messages:
|
||||
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
|
||||
reset_password_failure: Uh ho! We could not find any user with the specified email.
|
||||
reset_password: Request for password reset is successful. A email with instructions will be sent to your email if it exists.
|
||||
reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
|
||||
login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
|
||||
saml_not_available: SAML authentication is not available in this installation.
|
||||
|
||||
Reference in New Issue
Block a user