diff --git a/app/builders/messages/message_builder.rb b/app/builders/messages/message_builder.rb index e9bf0802b..69ed786ce 100644 --- a/app/builders/messages/message_builder.rb +++ b/app/builders/messages/message_builder.rb @@ -35,7 +35,13 @@ class Messages::MessageBuilder file: uploaded_attachment ) - attachment.file_type = file_type(uploaded_attachment&.content_type) if uploaded_attachment.is_a?(ActionDispatch::Http::UploadedFile) + attachment.file_type = if uploaded_attachment.is_a?(String) + file_type_by_signed_id( + uploaded_attachment + ) + else + file_type(uploaded_attachment&.content_type) + end end end diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index d650b19d9..a7891ffd4 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -5,9 +5,10 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController before_action :set_current_page, only: [:index] def index - @articles_count = @portal.articles.count - @articles = @portal.articles - @articles = @articles.search(list_params) if list_params.present? + @portal_articles = @portal.articles + @all_articles = @portal_articles.search(list_params) + @articles_count = @all_articles.count + @articles = @all_articles.page(@current_page) end def create @@ -37,7 +38,7 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController end def portal - @portal ||= Current.account.portals.find_by(slug: params[:portal_id]) + @portal ||= Current.account.portals.find_by!(slug: params[:portal_id]) end def article_params diff --git a/app/controllers/api/v1/accounts/categories_controller.rb b/app/controllers/api/v1/accounts/categories_controller.rb index bc18b61b8..e28e601d5 100644 --- a/app/controllers/api/v1/accounts/categories_controller.rb +++ b/app/controllers/api/v1/accounts/categories_controller.rb @@ -5,6 +5,7 @@ class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseControlle before_action :set_current_page, only: [:index] def index + @current_locale = params[:locale] @categories = @portal.categories.search(params) end diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index 6ac000b58..62a98a872 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -14,7 +14,10 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController @portal.members << agents end - def show; end + def show + @all_articles = @portal.articles + @articles = @all_articles.search(locale: params[:locale]) + end def create @portal = Current.account.portals.build(portal_params) diff --git a/app/controllers/public/api/v1/inboxes_controller.rb b/app/controllers/public/api/v1/inboxes_controller.rb index a57e72e40..65fad57b1 100644 --- a/app/controllers/public/api/v1/inboxes_controller.rb +++ b/app/controllers/public/api/v1/inboxes_controller.rb @@ -3,9 +3,15 @@ class Public::Api::V1::InboxesController < PublicController before_action :set_contact_inbox before_action :set_conversation + def show + @inbox_channel = ::Channel::Api.find_by!(identifier: params[:id]) + end + private def set_inbox_channel + return if params[:inbox_id].blank? + @inbox_channel = ::Channel::Api.find_by!(identifier: params[:inbox_id]) end diff --git a/app/helpers/file_type_helper.rb b/app/helpers/file_type_helper.rb index db3f6249d..03b807aad 100644 --- a/app/helpers/file_type_helper.rb +++ b/app/helpers/file_type_helper.rb @@ -8,6 +8,12 @@ module FileTypeHelper :file end + # Used in case of DIRECT_UPLOADS_ENABLED=true + def file_type_by_signed_id(signed_id) + blob = ActiveStorage::Blob.find_signed(signed_id) + file_type(blob&.content_type) + end + def image_file?(content_type) [ 'image/jpeg', diff --git a/app/javascript/dashboard/api/helpCenter/categories.js b/app/javascript/dashboard/api/helpCenter/categories.js index b6eae3f32..01658497e 100644 --- a/app/javascript/dashboard/api/helpCenter/categories.js +++ b/app/javascript/dashboard/api/helpCenter/categories.js @@ -7,8 +7,8 @@ class CategoriesAPI extends PortalsAPI { super('categories', { accountScoped: true }); } - get({ portalSlug }) { - return axios.get(`${this.url}/${portalSlug}/categories`); + get({ portalSlug, locale }) { + return axios.get(`${this.url}/${portalSlug}/categories?locale=${locale}`); } create({ portalSlug, categoryObj }) { diff --git a/app/javascript/dashboard/api/helpCenter/portals.js b/app/javascript/dashboard/api/helpCenter/portals.js index 520453540..28220b0b5 100644 --- a/app/javascript/dashboard/api/helpCenter/portals.js +++ b/app/javascript/dashboard/api/helpCenter/portals.js @@ -6,6 +6,10 @@ class PortalsAPI extends ApiClient { super('portals', { accountScoped: true }); } + getPortal({ portalSlug, locale }) { + return axios.get(`${this.url}/${portalSlug}?locale=${locale}`); + } + updatePortal({ portalSlug, portalObj }) { return axios.patch(`${this.url}/${portalSlug}`, portalObj); } diff --git a/app/javascript/dashboard/components/index.js b/app/javascript/dashboard/components/index.js index 5e58aa049..f5ce5d856 100644 --- a/app/javascript/dashboard/components/index.js +++ b/app/javascript/dashboard/components/index.js @@ -5,9 +5,12 @@ import Button from './ui/WootButton'; import Code from './Code'; import ColorPicker from './widgets/ColorPicker'; import ConfirmDeleteModal from './widgets/modal/ConfirmDeleteModal.vue'; +import ConfirmModal from './widgets/modal/ConfirmationModal.vue'; +import ContextMenu from './ui/ContextMenu.vue'; import DeleteModal from './widgets/modal/DeleteModal.vue'; import DropdownItem from 'shared/components/ui/dropdown/DropdownItem'; import DropdownMenu from 'shared/components/ui/dropdown/DropdownMenu'; +import FeatureToggle from './widgets/FeatureToggle'; import HorizontalBar from './widgets/chart/HorizontalBarChart'; import Input from './widgets/forms/Input.vue'; import Label from './ui/Label'; @@ -21,8 +24,6 @@ import SubmitButton from './buttons/FormSubmitButton'; import Tabs from './ui/Tabs/Tabs'; import TabsItem from './ui/Tabs/TabsItem'; import Thumbnail from './widgets/Thumbnail.vue'; -import ConfirmModal from './widgets/modal/ConfirmationModal.vue'; -import ContextMenu from './ui/ContextMenu.vue'; const WootUIKit = { AvatarUploader, @@ -31,9 +32,12 @@ const WootUIKit = { Code, ColorPicker, ConfirmDeleteModal, + ConfirmModal, + ContextMenu, DeleteModal, DropdownItem, DropdownMenu, + FeatureToggle, HorizontalBar, Input, Label, @@ -47,8 +51,6 @@ const WootUIKit = { Tabs, TabsItem, Thumbnail, - ConfirmModal, - ContextMenu, install(Vue) { const keys = Object.keys(this); keys.pop(); // remove 'install' from keys diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryChildNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryChildNavItem.vue index de10dda4a..2dd23544f 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryChildNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryChildNavItem.vue @@ -26,7 +26,7 @@ :class="{ 'text-truncate': shouldTruncate }" > {{ label }} - + {{ childItemCount }} @@ -76,7 +76,7 @@ export default { type: String, default: '', }, - isHelpCenterSidebar: { + showChildCount: { type: Boolean, default: false, }, @@ -127,11 +127,16 @@ $label-badge-size: var(--space-slab); color: var(--w-500); border-color: var(--w-25); } + &.is-active .count-view { + background: var(--w-75); + color: var(--w-500); + } } .menu-label { flex-grow: 1; - line-height: var(--space-two); + display: inline-flex; + align-items: center; } .inbox-icon { @@ -175,10 +180,6 @@ $label-badge-size: var(--space-slab); font-weight: var(--font-weight-bold); margin-left: var(--space-smaller); padding: var(--space-zero) var(--space-smaller); - - &.is-active { - background: var(--w-50); - color: var(--w-500); - } + line-height: var(--font-size-small); } diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue index 8661a488f..644258f50 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue @@ -4,16 +4,15 @@ -
- {{ $t('SIDEBAR.HELP_CENTER.CATEGORY_EMPTY_MESSAGE') }} -
@@ -104,14 +96,6 @@ export default { type: Object, default: () => ({}), }, - isHelpCenterSidebar: { - type: Boolean, - default: false, - }, - isCategoryEmpty: { - type: Boolean, - default: false, - }, }, computed: { ...mapGetters({ @@ -161,8 +145,8 @@ export default { this.menuItem.toStateName === 'settings_applications' ); }, - isArticlesView() { - return this.$store.state.route.name === this.menuItem.toStateName; + isCurrentRoute() { + return this.$store.state.route.name.includes(this.menuItem.toStateName); }, computedClass() { @@ -181,12 +165,11 @@ export default { } return ' '; } - if (this.isHelpCenterSidebar) { - if (this.isArticlesView) { - return 'is-active'; - } - return ' '; + + if (this.isCurrentRoute) { + return 'is-active'; } + return ''; }, }, @@ -222,6 +205,9 @@ export default { onClickOpen() { this.$emit('open'); }, + showChildCount(count) { + return Number.isInteger(count); + }, }, }; @@ -277,6 +263,11 @@ export default { color: var(--w-500); border-color: var(--w-25); } + + &.is-active .count-view { + background: var(--w-75); + color: var(--w-600); + } } .secondary-menu--icon { @@ -306,15 +297,12 @@ export default { top: -1px; } -.sidebar-item .button.menu-item--new { - display: inline-flex; - height: var(--space-medium); - margin: var(--space-smaller) 0; - padding: var(--space-smaller); - color: var(--s-500); +.sidebar-item .menu-item--new { + padding: var(--space-small) 0; - &:hover { - color: var(--w-500); + .button { + display: inline-flex; + color: var(--s-500); } } @@ -340,11 +328,6 @@ export default { font-weight: var(--font-weight-bold); margin-left: var(--space-smaller); padding: var(--space-zero) var(--space-smaller); - - &.is-active { - background: var(--w-50); - color: var(--w-500); - } } .submenu-icons { @@ -356,10 +339,4 @@ export default { margin-left: var(--space-small); } } - -.empty-text { - color: var(--s-500); - font-size: var(--font-size-small); - margin: var(--space-smaller); -} diff --git a/app/javascript/dashboard/components/ui/TimeAgo.vue b/app/javascript/dashboard/components/ui/TimeAgo.vue index c262a8aa0..c796ed0f6 100644 --- a/app/javascript/dashboard/components/ui/TimeAgo.vue +++ b/app/javascript/dashboard/components/ui/TimeAgo.vue @@ -1,17 +1,15 @@ - {{ timeAgo }} + {{ timeAgo }} diff --git a/app/javascript/dashboard/components/widgets/Thumbnail.spec.js b/app/javascript/dashboard/components/widgets/Thumbnail.spec.js index c10cd3d16..bcb881d09 100644 --- a/app/javascript/dashboard/components/widgets/Thumbnail.spec.js +++ b/app/javascript/dashboard/components/widgets/Thumbnail.spec.js @@ -2,8 +2,8 @@ import { mount } from '@vue/test-utils'; import Avatar from './Avatar.vue'; import Thumbnail from './Thumbnail.vue'; -describe(`when there are NO errors loading the thumbnail`, () => { - it(`should render the agent thumbnail`, () => { +describe('Thumbnail.vue', () => { + it('should render the agent thumbnail if valid image is passed', () => { const wrapper = mount(Thumbnail, { propsData: { src: 'https://some_valid_url.com', @@ -14,14 +14,12 @@ describe(`when there are NO errors loading the thumbnail`, () => { }; }, }); - expect(wrapper.find('#image').exists()).toBe(true); + expect(wrapper.find('.user-thumbnail').exists()).toBe(true); const avatarComponent = wrapper.findComponent(Avatar); expect(avatarComponent.exists()).toBe(false); }); -}); -describe(`when there ARE errors loading the thumbnail`, () => { - it(`should render the agent avatar`, () => { + it('should render the avatar component if invalid image is passed', () => { const wrapper = mount(Thumbnail, { propsData: { src: 'https://some_invalid_url.com', @@ -32,19 +30,17 @@ describe(`when there ARE errors loading the thumbnail`, () => { }; }, }); - expect(wrapper.find('#image').exists()).toBe(false); + expect(wrapper.find('.avatar-container').exists()).toBe(true); const avatarComponent = wrapper.findComponent(Avatar); expect(avatarComponent.exists()).toBe(true); }); -}); -describe(`when Avatar shows`, () => { - it(`initials shold correspond to username`, () => { + it('should the initial of the name if no image is passed', () => { const wrapper = mount(Avatar, { propsData: { username: 'Angie Rojas', }, }); - expect(wrapper.find('span').text()).toBe('AR'); + expect(wrapper.find('div').text()).toBe('AR'); }); }); diff --git a/app/javascript/dashboard/components/widgets/Thumbnail.vue b/app/javascript/dashboard/components/widgets/Thumbnail.vue index 4bfc729de..e0d9da24c 100644 --- a/app/javascript/dashboard/components/widgets/Thumbnail.vue +++ b/app/javascript/dashboard/components/widgets/Thumbnail.vue @@ -1,74 +1,23 @@{{ macro.name }}
+{{ action.actionName }}
+{{ action.actionValue }}
+{{ $t('MACROS.EDITOR.VISIBILITY.LABEL') }}