diff --git a/.rubocop.yml b/.rubocop.yml
index d63f0418d..dafd9a620 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -184,3 +184,4 @@ AllCops:
- db/migrate/20200927135222_add_last_activity_at_to_conversation.rb
- db/migrate/20210306170117_add_last_activity_at_to_contacts.rb
- db/migrate/20220809104508_revert_cascading_indexes.rb
+
diff --git a/Gemfile b/Gemfile
index ba83ce964..9c3590017 100644
--- a/Gemfile
+++ b/Gemfile
@@ -158,6 +158,10 @@ group :test do
gem 'webmock'
end
+group :development, :test, :staging do
+ gem 'faker'
+end
+
group :development, :test do
gem 'active_record_query_trace'
##--- gems for debugging and error reporting ---##
@@ -167,7 +171,6 @@ group :development, :test do
gem 'byebug', platform: :mri
gem 'climate_control'
gem 'factory_bot_rails'
- gem 'faker'
gem 'listen'
gem 'mock_redis'
gem 'pry-rails'
diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb
index c3d8ad024..6ac000b58 100644
--- a/app/controllers/api/v1/accounts/portals_controller.rb
+++ b/app/controllers/api/v1/accounts/portals_controller.rb
@@ -58,7 +58,8 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
def portal_params
params.require(:portal).permit(
- :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived, config: { allowed_locales: [] }
+ :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived, { config: [:default_locale,
+ { allowed_locales: [] }] }
)
end
diff --git a/app/controllers/super_admin/accounts_controller.rb b/app/controllers/super_admin/accounts_controller.rb
index 28cae96b0..3d038281c 100644
--- a/app/controllers/super_admin/accounts_controller.rb
+++ b/app/controllers/super_admin/accounts_controller.rb
@@ -41,4 +41,9 @@ class SuperAdmin::AccountsController < SuperAdmin::ApplicationController
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
# for more information
+
+ def seed
+ Seeders::AccountSeeder.new(account: requested_resource).perform!
+ redirect_back(fallback_location: [namespace, requested_resource], notice: 'Account seeding triggered')
+ end
end
diff --git a/app/fields/avatar_field.rb b/app/fields/avatar_field.rb
index a9674eb94..c443b7a21 100644
--- a/app/fields/avatar_field.rb
+++ b/app/fields/avatar_field.rb
@@ -2,6 +2,8 @@ require 'administrate/field/base'
class AvatarField < Administrate::Field::Base
def avatar_url
- data.presence&.gsub('?d=404', '?d=mp')
+ return data.presence if data.presence
+
+ resource.is_a?(User) ? '/assets/administrate/user/avatar.png' : '/assets/administrate/bot/avatar.png'
end
end
diff --git a/app/javascript/dashboard/api/helpCenter/articles.js b/app/javascript/dashboard/api/helpCenter/articles.js
index 8bc960267..62328e8eb 100644
--- a/app/javascript/dashboard/api/helpCenter/articles.js
+++ b/app/javascript/dashboard/api/helpCenter/articles.js
@@ -42,6 +42,10 @@ class ArticlesAPI extends PortalsAPI {
category_id,
});
}
+
+ deleteArticle({ articleId, portalSlug }) {
+ return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`);
+ }
}
export default new ArticlesAPI();
diff --git a/app/javascript/dashboard/api/helpCenter/portals.js b/app/javascript/dashboard/api/helpCenter/portals.js
index ec9528f73..a71f18f5c 100644
--- a/app/javascript/dashboard/api/helpCenter/portals.js
+++ b/app/javascript/dashboard/api/helpCenter/portals.js
@@ -9,6 +9,10 @@ class PortalsAPI extends ApiClient {
updatePortal({ portalSlug, params }) {
return axios.patch(`${this.url}/${portalSlug}`, params);
}
+
+ deletePortal(portalSlug) {
+ return axios.delete(`${this.url}/${portalSlug}`);
+ }
}
export default PortalsAPI;
diff --git a/app/javascript/dashboard/api/specs/article.spec.js b/app/javascript/dashboard/api/specs/article.spec.js
index e20e2e222..51e90318f 100644
--- a/app/javascript/dashboard/api/specs/article.spec.js
+++ b/app/javascript/dashboard/api/specs/article.spec.js
@@ -52,4 +52,15 @@ describe('#PortalAPI', () => {
);
});
});
+ describeWithAPIMock('API calls', context => {
+ it('#deleteArticle', () => {
+ articlesAPI.deleteArticle({
+ articleId: 1,
+ portalSlug: 'room-rental',
+ });
+ expect(context.axiosMock.delete).toHaveBeenCalledWith(
+ '/api/v1/portals/room-rental/articles/1'
+ );
+ });
+ });
});
diff --git a/app/javascript/dashboard/assets/scss/widgets/_modal.scss b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
index 68ea09589..fc497f069 100644
--- a/app/javascript/dashboard/assets/scss/widgets/_modal.scss
+++ b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
@@ -85,7 +85,7 @@
.modal-footer {
@include flex;
- @include flex-align($x: flex-start, $y: middle);
+ @include flex-align($x: flex-end, $y: middle);
padding: $space-small $zero;
button {
diff --git a/app/javascript/dashboard/components/ModalHeader.vue b/app/javascript/dashboard/components/ModalHeader.vue
index 0f8d8931c..b60f5d851 100644
--- a/app/javascript/dashboard/components/ModalHeader.vue
+++ b/app/javascript/dashboard/components/ModalHeader.vue
@@ -6,6 +6,9 @@
{{ headerContent }}
+
+ {{ headerContentValue }}
+
@@ -22,6 +25,10 @@ export default {
type: String,
default: '',
},
+ headerContentValue: {
+ type: String,
+ default: '',
+ },
headerImage: {
type: String,
default: '',
@@ -32,5 +39,8 @@ export default {
diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
index 861805589..319aa5fc1 100644
--- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
+++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue
@@ -5,9 +5,10 @@
{{ $t(`SIDEBAR.${menuItem.label}`) }}
@@ -32,7 +32,7 @@
{{ $t('HELP_CENTER.PORTAL.POPOVER.CANCEL_BUTTON_LABEL') }}
-
+ {}">
{{ $t('HELP_CENTER.PORTAL.POPOVER.CHOOSE_LOCALE_BUTTON') }}
@@ -52,19 +52,26 @@ export default {
type: Array,
default: () => [],
},
- activePortal: {
- type: Object,
- default: () => ({}),
+ activePortalSlug: {
+ type: String,
+ default: '',
},
},
+
methods: {
closePortalPopover() {
this.$emit('close-popover');
},
- openPortalPage() {
+ openPortalArticles({ slug, locale }) {
this.$emit('close-popover');
+ const portal = this.portals.find(p => p.slug === slug);
+ this.$store.dispatch('portals/setPortalId', portal.id);
this.$router.push({
- name: 'list_all_portals',
+ name: 'list_all_locale_articles',
+ params: {
+ portalSlug: slug,
+ locale: locale,
+ },
});
},
},
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ArticleSettings.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ArticleSettings.vue
index 6817c175b..5dc08c5cb 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ArticleSettings.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ArticleSettings.vue
@@ -127,7 +127,7 @@ export default {
props: {
article: {
type: Object,
- required: true,
+ default: () => ({}),
},
},
data() {
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/EditArticle.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/EditArticle.vue
index 0aefc7902..2d6402e62 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/EditArticle.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/EditArticle.vue
@@ -27,6 +27,17 @@
v-if="showArticleSettings"
:article="article"
@save-article="saveArticle"
+ @delete-article="openDeletePopup"
+ @archive-article="archiveArticle"
+ />
+
@@ -52,13 +63,13 @@ export default {
isSaved: false,
showArticleSettings: false,
alertMessage: '',
+ showDeleteConfirmationPopup: false,
};
},
computed: {
...mapGetters({
isFetching: 'articles/isFetching',
articles: 'articles/articles',
- selectedPortal: 'portals/getSelectedPortal',
}),
article() {
return this.$store.getters['articles/articleById'](this.articleId);
@@ -67,7 +78,7 @@ export default {
return this.$route.params.articleSlug;
},
selectedPortalSlug() {
- return this.portalSlug || this.selectedPortal?.slug;
+ return this.$route.params.portalSlug;
},
},
mounted() {
@@ -83,6 +94,16 @@ export default {
portalSlug: this.selectedPortalSlug,
});
},
+ openDeletePopup() {
+ this.showDeleteConfirmationPopup = true;
+ },
+ closeDeletePopup() {
+ this.showDeleteConfirmationPopup = false;
+ },
+ confirmDeletion() {
+ this.closeDeletePopup();
+ this.deleteArticle();
+ },
async saveArticle({ ...values }) {
this.isUpdating = true;
try {
@@ -93,8 +114,7 @@ export default {
});
} catch (error) {
this.alertMessage =
- error?.message ||
- this.$t('HELP_CENTER.EDIT_ARTICLE.API.ERROR_MESSAGE');
+ error?.message || this.$t('HELP_CENTER.EDIT_ARTICLE.API.ERROR');
this.showAlert(this.alertMessage);
} finally {
setTimeout(() => {
@@ -103,6 +123,45 @@ export default {
}, 1500);
}
},
+ async deleteArticle() {
+ try {
+ await this.$store.dispatch('articles/delete', {
+ portalSlug: this.selectedPortalSlug,
+ articleId: this.articleId,
+ });
+ this.alertMessage = this.$t(
+ 'HELP_CENTER.DELETE_ARTICLE.API.SUCCESS_MESSAGE'
+ );
+ this.$router.push({
+ name: 'list_all_locale_articles',
+ params: {
+ portalSlug: this.selectedPortalSlug,
+ locale: this.locale,
+ },
+ });
+ } catch (error) {
+ this.alertMessage =
+ error?.message ||
+ this.$t('HELP_CENTER.DELETE_ARTICLE.API.ERROR_MESSAGE');
+ } finally {
+ this.showAlert(this.alertMessage);
+ }
+ },
+ async archiveArticle() {
+ try {
+ await this.$store.dispatch('articles/update', {
+ portalSlug: this.selectedPortalSlug,
+ articleId: this.articleId,
+ status: 2,
+ });
+ this.alertMessage = this.$t('HELP_CENTER.ARCHIVE_ARTICLE.API.SUCCESS');
+ } catch (error) {
+ this.alertMessage =
+ error?.message || this.$t('HELP_CENTER.ARCHIVE_ARTICLE.API.ERROR');
+ } finally {
+ this.showAlert(this.alertMessage);
+ }
+ },
openArticleSettings() {
this.showArticleSettings = true;
},
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue
index fcb25546b..73ccda403 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue
@@ -46,7 +46,6 @@ export default {
...mapGetters({
articles: 'articles/allArticles',
categories: 'categories/allCategories',
- selectedPortal: 'portals/getSelectedPortal',
uiFlags: 'articles/uiFlags',
meta: 'articles/getMeta',
isFetching: 'articles/isFetching',
@@ -64,7 +63,7 @@ export default {
return this.isFetching && !this.articles.length;
},
selectedPortalSlug() {
- return this.selectedPortal?.slug;
+ return this.$route.params.portalSlug;
},
selectedCategorySlug() {
const { categorySlug } = this.$route.params;
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/NewArticle.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/NewArticle.vue
index 778dc38cc..bc8ad4fab 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/NewArticle.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/NewArticle.vue
@@ -46,7 +46,6 @@ export default {
},
computed: {
...mapGetters({
- selectedPortal: 'portals/getSelectedPortal',
currentUserID: 'getCurrentUserID',
articles: 'articles/articles',
categories: 'categories/allCategories',
@@ -58,7 +57,7 @@ export default {
return { title: this.articleTitle, content: this.articleContent };
},
selectedPortalSlug() {
- return this.portalSlug || this.selectedPortal?.slug;
+ return this.$route.params.portalSlug;
},
categoryId() {
return this.categories.length ? this.categories[0].id : null;
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/portals/ListAllPortals.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/portals/ListAllPortals.vue
index 88501c9bc..9d7d09ae7 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/portals/ListAllPortals.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/portals/ListAllPortals.vue
@@ -33,6 +33,7 @@