![Chatwoot logo]()
-
- {{ messageContent }}
-
+ v-dompurify-html="formattedMessageContent"
+ class="mb-8 text-lg leading-relaxed text-n-slate-12 prose prose-bubble"
+ />
diff --git a/app/javascript/v3/api/auth.js b/app/javascript/v3/api/auth.js
index c4ef40b1c..4c4ffb13e 100644
--- a/app/javascript/v3/api/auth.js
+++ b/app/javascript/v3/api/auth.js
@@ -2,6 +2,7 @@ import {
setAuthCredentials,
throwErrorMessage,
clearLocalStorageOnLogout,
+ parseAPIErrorResponse,
} from 'dashboard/store/utils/api';
import wootAPI from './apiClient';
import {
@@ -42,8 +43,9 @@ export const login = async ({
mfaToken: error.response.data.mfa_token,
};
}
- throwErrorMessage(error);
- return null;
+ const loginError = new Error(parseAPIErrorResponse(error));
+ loginError.errorCode = error.response?.data?.error_code;
+ throw loginError;
}
};
diff --git a/app/javascript/v3/components/Form/Input.vue b/app/javascript/v3/components/Form/Input.vue
index 8e719808f..48e842189 100644
--- a/app/javascript/v3/components/Form/Input.vue
+++ b/app/javascript/v3/components/Form/Input.vue
@@ -1,5 +1,5 @@
+<% end %>
+
+
diff --git a/app/views/layouts/_portal_scripts.html.erb b/app/views/layouts/_portal_scripts.html.erb
new file mode 100644
index 000000000..b1479cace
--- /dev/null
+++ b/app/views/layouts/_portal_scripts.html.erb
@@ -0,0 +1,97 @@
+<% if @article.present? %>
+
+<% end %>
+
+
+
+<% if @portal.channel_web_widget.present? && !@is_plain_layout_enabled %>
+
+<%= @portal.channel_web_widget.web_widget_script.html_safe %>
+<% end %>
diff --git a/app/views/layouts/portal.html+documentation.erb b/app/views/layouts/portal.html+documentation.erb
new file mode 100644
index 000000000..50374917f
--- /dev/null
+++ b/app/views/layouts/portal.html+documentation.erb
@@ -0,0 +1,26 @@
+
+
+
+ <%= render 'layouts/portal_head' %>
+
+
+
+
+ <%= render 'public/api/v1/portals/documentation_layout/topbar',
+ portal: @portal, locale: @locale, article: @article, category: @category %>
+
+
+ <%= render 'public/api/v1/portals/documentation_layout/sidebar',
+ portal: @portal, locale: @locale, article: @article, category: @category %>
+
+ <%= yield %>
+ <%= render 'public/api/v1/portals/documentation_layout/footer',
+ portal: @portal, global_config: @global_config %>
+
+
+
+
+
+ <%= render 'layouts/portal_scripts' %>
+
+
diff --git a/app/views/layouts/portal.html.erb b/app/views/layouts/portal.html.erb
index 78418881a..a2b9033bf 100644
--- a/app/views/layouts/portal.html.erb
+++ b/app/views/layouts/portal.html.erb
@@ -1,160 +1,16 @@
-<%#
-# Application Layout
-
-This view template is used as the layout
-for every page that Administrate generates.
-
-By default, it renders:
-- Navigation
-- Content for a search bar
- (if provided by a `content_for` block in a nested page)
-- Flashes
-- Links to stylesheets and JavaScripts
-- The appearance dropdown styles are added to the top to prevent FOUC
-%>
-
-
-
-
-
-
- <%= vite_client_tag %>
- <%= vite_javascript_tag 'portal' %>
-
- <%= csrf_meta_tags %>
- <% if content_for?(:head) %>
- <%= yield(:head) %>
- <% else %>
-
<%= @portal.display_title %>
- <% end %>
-
- <% if @portal.logo.present? %>
-
- <% end %>
-
- <% unless @theme_from_params.blank? %>
- <%# this adds the theme from params, ensuring that there a localstorage value set %>
- <%# this will further trigger the next script to ensure color mode is toggled without a FOUC %>
-
- <% end %>
-
-
+ <%= render 'layouts/portal_head' %>
-
+
- <% if !@is_plain_layout_enabled %>
- <%= render "public/api/v1/portals/header", portal: @portal %>
- <% end %>
+ <%= render 'public/api/v1/portals/header', portal: @portal unless @is_plain_layout_enabled %>
<%= yield %>
- <% if !(@is_plain_layout_enabled || @portal.account.feature_enabled?('disable_branding')) %>
- <%= render "public/api/v1/portals/footer" %>
- <% end %>
+ <%= render 'public/api/v1/portals/footer' unless @is_plain_layout_enabled || @portal.account.feature_enabled?('disable_branding') %>
- <% if @article.present? %>
-
- <% end %>
+ <%= render 'layouts/portal_scripts' %>
-
-
-
- <% if @portal.channel_web_widget.present? && !@is_plain_layout_enabled %>
- <%= @portal.channel_web_widget.web_widget_script.html_safe %>
- <% end %>
diff --git a/app/views/layouts/vueapp.html.erb b/app/views/layouts/vueapp.html.erb
index d97ece981..954be9c29 100644
--- a/app/views/layouts/vueapp.html.erb
+++ b/app/views/layouts/vueapp.html.erb
@@ -55,6 +55,7 @@
<% end %>
enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>,
helpUrls: <%= feature_help_urls.to_json.html_safe %>,
+ inboxEventsEnabled: '<%= ENV['ENABLE_INBOX_EVENTS'].present? %>',
selectedLocale: '<%= I18n.locale %>'
}
window.globalConfig = <%= raw @global_config.to_json %>
diff --git a/app/views/mailers/administrator_notifications/integrations_notification_mailer/openai_disconnect.liquid b/app/views/mailers/administrator_notifications/integrations_notification_mailer/openai_disconnect.liquid
new file mode 100644
index 000000000..2847a389d
--- /dev/null
+++ b/app/views/mailers/administrator_notifications/integrations_notification_mailer/openai_disconnect.liquid
@@ -0,0 +1,7 @@
+
Hello,
+
+
Your OpenAI integration was disconnected because the configured API key is invalid or revoked. To continue using OpenAI-powered features, reconnect the integration with a valid API key.
+
+
+Click here to reconnect.
+
diff --git a/app/views/public/api/v1/models/_search_article.json.jbuilder b/app/views/public/api/v1/models/_search_article.json.jbuilder
index 28fc9559e..b868bc6b2 100644
--- a/app/views/public/api/v1/models/_search_article.json.jbuilder
+++ b/app/views/public/api/v1/models/_search_article.json.jbuilder
@@ -1,4 +1,4 @@
-content = article.content.to_s.squish
+content = ChatwootMarkdownRenderer.new(article.content.to_s).render_markdown_to_plain_text.squish
snippet = excerpt(content, search_query, radius: 110, omission: '...') ||
truncate(content, length: 220, separator: ' ')
diff --git a/app/views/public/api/v1/portals/_authors.html.erb b/app/views/public/api/v1/portals/_authors.html.erb
index 07c5c2c3c..f7dc1eb3e 100644
--- a/app/views/public/api/v1/portals/_authors.html.erb
+++ b/app/views/public/api/v1/portals/_authors.html.erb
@@ -2,8 +2,11 @@
<% if author_count > 0 %>
- <% category.articles.published.order(position: :asc).map(&:author).uniq.take(3).each do |author| %>
- <%= render "public/api/v1/portals/thumbnail", author: author, size: 5 %>
+ <% z_classes = %w[z-30 z-20 z-10] %>
+ <% category.articles.published.order(position: :asc).map(&:author).uniq.take(3).each_with_index do |author, idx| %>
+
+ <%= render "public/api/v1/portals/thumbnail", author: author, size: 5 %>
+
<% end %>
diff --git a/app/views/public/api/v1/portals/_header.html.erb b/app/views/public/api/v1/portals/_header.html.erb
index 3db8efa59..189371261 100644
--- a/app/views/public/api/v1/portals/_header.html.erb
+++ b/app/views/public/api/v1/portals/_header.html.erb
@@ -5,7 +5,7 @@
<% if @portal.logo.present? %>
 %>)
<% end %>
-
<%= @portal.name %>
+
<%= @portal.localized_value('name', @locale) %>
@@ -106,7 +106,7 @@
<% if @portal.logo.present? %>
 %>)
<% end %>
-
<%= @portal.name %>
+
<%= @portal.localized_value('name', @locale) %>
diff --git a/app/views/public/api/v1/portals/_hero.html.erb b/app/views/public/api/v1/portals/_hero.html.erb
index 8fcdb2c0d..1e2bf119c 100644
--- a/app/views/public/api/v1/portals/_hero.html.erb
+++ b/app/views/public/api/v1/portals/_hero.html.erb
@@ -1,7 +1,7 @@
<% if !@is_plain_layout_enabled %>
<% content_for :head do %>
-
<%= @portal.display_title %>
-
+
<%= @portal.display_title(@locale) %>
+
<% if @og_image_url.present? %>
@@ -13,12 +13,12 @@
-
<%= @portal.name %>
+
<%= @portal.localized_value('name', @locale) %>
- <%= portal.header_text %>
+ <%= portal.localized_value('header_text', @locale) %>
<%= I18n.t('public_portal.hero.sub_title') %>
-
+
diff --git a/app/views/public/api/v1/portals/_uncategorized-block.html.erb b/app/views/public/api/v1/portals/_uncategorized-block.html.erb
index 430be617b..fe28bca5f 100644
--- a/app/views/public/api/v1/portals/_uncategorized-block.html.erb
+++ b/app/views/public/api/v1/portals/_uncategorized-block.html.erb
@@ -6,7 +6,7 @@