diff --git a/app/controllers/api/v1/accounts/kbase/categories_controller.rb b/app/controllers/api/v1/accounts/categories_controller.rb
similarity index 72%
rename from app/controllers/api/v1/accounts/kbase/categories_controller.rb
rename to app/controllers/api/v1/accounts/categories_controller.rb
index a40053dd2..246eeb2a2 100644
--- a/app/controllers/api/v1/accounts/kbase/categories_controller.rb
+++ b/app/controllers/api/v1/accounts/categories_controller.rb
@@ -1,4 +1,5 @@
-class Api::V1::Accounts::Kbase::CategoriesController < Api::V1::Accounts::Kbase::BaseController
+class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseController
+ before_action :portal
before_action :fetch_category, except: [:index, :create]
def index
@@ -24,6 +25,10 @@ class Api::V1::Accounts::Kbase::CategoriesController < Api::V1::Accounts::Kbase:
@category = @portal.categories.find(params[:id])
end
+ def portal
+ @portal ||= Current.account.portals.find_by(slug: params[:portal_id])
+ end
+
def category_params
params.require(:category).permit(
:name, :description, :position
diff --git a/app/controllers/api/v1/accounts/csat_survey_responses_controller.rb b/app/controllers/api/v1/accounts/csat_survey_responses_controller.rb
index 347f028f7..7b5c51d6e 100644
--- a/app/controllers/api/v1/accounts/csat_survey_responses_controller.rb
+++ b/app/controllers/api/v1/accounts/csat_survey_responses_controller.rb
@@ -5,7 +5,7 @@ class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::Base
RESULTS_PER_PAGE = 25
before_action :check_authorization
- before_action :set_csat_survey_responses, only: [:index, :metrics]
+ before_action :set_csat_survey_responses, only: [:index, :metrics, :download]
before_action :set_current_page, only: [:index]
before_action :set_current_page_surveys, only: [:index]
before_action :set_total_sent_messages_count, only: [:metrics]
@@ -19,6 +19,12 @@ class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::Base
@ratings_count = @csat_survey_responses.group(:rating).count
end
+ def download
+ response.headers['Content-Type'] = 'text/csv'
+ response.headers['Content-Disposition'] = 'attachment; filename=csat_report.csv'
+ render layout: false, template: 'api/v1/accounts/csat_survey_responses/download.csv.erb', format: 'csv'
+ end
+
private
def set_total_sent_messages_count
diff --git a/app/controllers/api/v1/accounts/kbase/base_controller.rb b/app/controllers/api/v1/accounts/kbase/base_controller.rb
deleted file mode 100644
index 4f62cd858..000000000
--- a/app/controllers/api/v1/accounts/kbase/base_controller.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class Api::V1::Accounts::Kbase::BaseController < Api::V1::Accounts::BaseController
- before_action :portal
-
- private
-
- def portal
- @portal ||= Current.account.kbase_portals.find_by(slug: params[:portal_id])
- end
-end
diff --git a/app/controllers/api/v1/accounts/kbase/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb
similarity index 58%
rename from app/controllers/api/v1/accounts/kbase/portals_controller.rb
rename to app/controllers/api/v1/accounts/portals_controller.rb
index 5ec1b4a83..75ffc35e9 100644
--- a/app/controllers/api/v1/accounts/kbase/portals_controller.rb
+++ b/app/controllers/api/v1/accounts/portals_controller.rb
@@ -1,14 +1,14 @@
-class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::BaseController
+class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
before_action :fetch_portal, except: [:index, :create]
def index
- @portals = Current.account.kbase_portals
+ @portals = Current.account.portals
end
def show; end
def create
- @portal = Current.account.kbase_portals.create!(portal_params)
+ @portal = Current.account.portals.create!(portal_params)
end
def update
@@ -23,7 +23,7 @@ class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::BaseContr
private
def fetch_portal
- @portal = Current.account.kbase_portals.find_by(slug: permitted_params[:id])
+ @portal = Current.account.portals.find_by(slug: permitted_params[:id])
end
def permitted_params
@@ -32,7 +32,7 @@ class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::BaseContr
def portal_params
params.require(:portal).permit(
- :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug
+ :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived
)
end
end
diff --git a/app/javascript/dashboard/api/csatReports.js b/app/javascript/dashboard/api/csatReports.js
index cc0271d7d..4decc8de3 100644
--- a/app/javascript/dashboard/api/csatReports.js
+++ b/app/javascript/dashboard/api/csatReports.js
@@ -18,6 +18,17 @@ class CSATReportsAPI extends ApiClient {
});
}
+ download({ from, to, user_ids } = {}) {
+ return axios.get(`${this.url}/download`, {
+ params: {
+ since: from,
+ until: to,
+ sort: '-created_at',
+ user_ids,
+ },
+ });
+ }
+
getMetrics({ from, to, user_ids } = {}) {
return axios.get(`${this.url}/metrics`, {
params: { since: from, until: to, user_ids },
diff --git a/app/javascript/dashboard/api/specs/csatReports.spec.js b/app/javascript/dashboard/api/specs/csatReports.spec.js
index 0022a91ad..a1d6e50f2 100644
--- a/app/javascript/dashboard/api/specs/csatReports.spec.js
+++ b/app/javascript/dashboard/api/specs/csatReports.spec.js
@@ -33,5 +33,23 @@ describe('#Reports API', () => {
}
);
});
+ it('#download', () => {
+ csatReportsAPI.download({
+ from: 1622485800,
+ to: 1623695400,
+ user_ids: 1,
+ });
+ expect(context.axiosMock.get).toHaveBeenCalledWith(
+ '/api/v1/csat_survey_responses/download',
+ {
+ params: {
+ since: 1622485800,
+ until: 1623695400,
+ user_ids: 1,
+ sort: '-created_at',
+ },
+ }
+ );
+ });
});
});
diff --git a/app/javascript/dashboard/helper/downloadCsvFile.js b/app/javascript/dashboard/helper/downloadHelper.js
similarity index 51%
rename from app/javascript/dashboard/helper/downloadCsvFile.js
rename to app/javascript/dashboard/helper/downloadHelper.js
index f0a13a1fd..150ac1fd8 100644
--- a/app/javascript/dashboard/helper/downloadCsvFile.js
+++ b/app/javascript/dashboard/helper/downloadHelper.js
@@ -1,6 +1,12 @@
+import fromUnixTime from 'date-fns/fromUnixTime';
+import format from 'date-fns/format';
+
export const downloadCsvFile = (fileName, fileContent) => {
const link = document.createElement('a');
link.download = fileName;
link.href = `data:text/csv;charset=utf-8,` + encodeURI(fileContent);
link.click();
};
+
+export const generateFileName = ({ type, to }) =>
+ `${type}-report-${format(fromUnixTime(to), 'dd-MM-yyyy')}.csv`;
diff --git a/app/javascript/dashboard/helper/specs/downloadCsvFile.spec.js b/app/javascript/dashboard/helper/specs/downloadHelper.spec.js
similarity index 68%
rename from app/javascript/dashboard/helper/specs/downloadCsvFile.spec.js
rename to app/javascript/dashboard/helper/specs/downloadHelper.spec.js
index d05b0a841..b294dfe16 100644
--- a/app/javascript/dashboard/helper/specs/downloadCsvFile.spec.js
+++ b/app/javascript/dashboard/helper/specs/downloadHelper.spec.js
@@ -1,4 +1,4 @@
-import { downloadCsvFile } from '../downloadCsvFile';
+import { downloadCsvFile, generateFileName } from '../downloadHelper';
const fileName = 'test.csv';
const fileData = `Agent name,Conversations count,Avg first response time (Minutes),Avg resolution time (Minutes)
@@ -19,3 +19,11 @@ describe('#downloadCsvFile', () => {
expect(link.click).toHaveBeenCalledTimes(1);
});
});
+
+describe('#generateFileName', () => {
+ it('should generate the correct file name', () => {
+ expect(generateFileName({ type: 'csat', to: 1652812199 })).toEqual(
+ 'csat-report-17-05-2022.csv'
+ );
+ });
+});
diff --git a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
index da5e213e9..2b590b411 100644
--- a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
@@ -398,8 +398,8 @@
"MESSENGER_SUB_HEAD": "ضع هذا الكود داخل وسم الـ body في موقعك",
"INBOX_AGENTS": "موظف الدعم",
"INBOX_AGENTS_SUB_TEXT": "إضافة أو إزالة موظفين من قناة التواصل هذه",
- "AGENT_ASSIGNMENT": "Conversation Assignment",
- "AGENT_ASSIGNMENT_SUB_TEXT": "Update conversation assignment settings",
+ "AGENT_ASSIGNMENT": "إسناد المحادثات",
+ "AGENT_ASSIGNMENT_SUB_TEXT": "تحديث إعدادات إسناد المحادثات",
"UPDATE": "تحديث",
"ENABLE_EMAIL_COLLECT_BOX": "تفعيل صندوق جمع البريد الإلكتروني",
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "تمكين أو تعطيل مربع جمع البريد الإلكتروني في محادثة جديدة",
diff --git a/app/javascript/dashboard/i18n/locale/ar/settings.json b/app/javascript/dashboard/i18n/locale/ar/settings.json
index 45ea1db14..9b779f25c 100644
--- a/app/javascript/dashboard/i18n/locale/ar/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ar/settings.json
@@ -151,7 +151,7 @@
},
"SIDEBAR": {
"CURRENTLY_VIEWING_ACCOUNT": "مشاهدة حاليا:",
- "SWITCH": "Switch",
+ "SWITCH": "تبديل",
"CONVERSATIONS": "المحادثات",
"ALL_CONVERSATIONS": "كل المحادثات",
"MENTIONED_CONVERSATIONS": "الإشارات",
diff --git a/app/javascript/dashboard/i18n/locale/en/report.json b/app/javascript/dashboard/i18n/locale/en/report.json
index 35a349b56..5f2c6ae48 100644
--- a/app/javascript/dashboard/i18n/locale/en/report.json
+++ b/app/javascript/dashboard/i18n/locale/en/report.json
@@ -354,6 +354,7 @@
"CSAT_REPORTS": {
"HEADER": "CSAT Reports",
"NO_RECORDS": "There are no CSAT survey responses available.",
+ "DOWNLOAD": "Download CSAT Reports",
"FILTERS": {
"AGENTS": {
"PLACEHOLDER": "Choose Agents"
diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
index 985179d83..3a3076e9c 100644
--- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
+++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue
@@ -8,7 +8,6 @@
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue
index 24aeeff88..db20d7043 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue
@@ -65,7 +65,6 @@
+
{{ $t('REPORT.BUSINESS_HOURS') }}
@@ -105,6 +108,10 @@ export default {
type: Boolean,
default: false,
},
+ showBusinessHoursSwitch: {
+ type: Boolean,
+ default: true,
+ },
},
data() {
return {
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
index 6d8b49c62..34188a12a 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
@@ -61,6 +61,7 @@ import format from 'date-fns/format';
import { GROUP_BY_FILTER, METRIC_CHART } from '../constants';
import reportMixin from '../../../../../mixins/reportMixin';
import { formatTime } from '@chatwoot/utils';
+import { generateFileName } from '../../../../../helper/downloadHelper';
const REPORTS_KEYS = {
CONVERSATIONS: 'conversations_count',
@@ -250,26 +251,17 @@ export default {
});
},
downloadReports() {
- const { from, to } = this;
- const fileName = `${this.type}-report-${format(
- fromUnixTime(to),
- 'dd-MM-yyyy'
- )}.csv`;
- switch (this.type) {
- case 'agent':
- this.$store.dispatch('downloadAgentReports', { from, to, fileName });
- break;
- case 'label':
- this.$store.dispatch('downloadLabelReports', { from, to, fileName });
- break;
- case 'inbox':
- this.$store.dispatch('downloadInboxReports', { from, to, fileName });
- break;
- case 'team':
- this.$store.dispatch('downloadTeamReports', { from, to, fileName });
- break;
- default:
- break;
+ const { from, to, type } = this;
+ const dispatchMethods = {
+ agent: 'downloadAgentReports',
+ label: 'downloadLabelReports',
+ inbox: 'downloadInboxReports',
+ team: 'downloadTeamReports',
+ };
+ if (dispatchMethods[type]) {
+ const fileName = generateFileName({ type, to });
+ const params = { from, to, fileName };
+ this.$store.dispatch(dispatchMethods[type], params);
}
},
changeSelection(index) {
diff --git a/app/javascript/dashboard/store/modules/csat.js b/app/javascript/dashboard/store/modules/csat.js
index 7bc1dad6d..2557e2698 100644
--- a/app/javascript/dashboard/store/modules/csat.js
+++ b/app/javascript/dashboard/store/modules/csat.js
@@ -1,6 +1,7 @@
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
import types from '../mutation-types';
import CSATReports from '../../api/csatReports';
+import { downloadCsvFile } from '../../helper/downloadHelper';
const computeDistribution = (value, total) =>
((value * 100) / total).toFixed(2);
@@ -107,6 +108,11 @@ export const actions = {
commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetchingMetrics: false });
}
},
+ downloadCSATReports(_, params) {
+ return CSATReports.download(params).then(response => {
+ downloadCsvFile(params.fileName, response.data);
+ });
+ },
};
export const mutations = {
diff --git a/app/javascript/dashboard/store/modules/reports.js b/app/javascript/dashboard/store/modules/reports.js
index dc9a6c33b..9ae571803 100644
--- a/app/javascript/dashboard/store/modules/reports.js
+++ b/app/javascript/dashboard/store/modules/reports.js
@@ -5,7 +5,7 @@ import * as types from '../mutation-types';
import Report from '../../api/reports';
import Vue from 'vue';
-import { downloadCsvFile } from '../../helper/downloadCsvFile';
+import { downloadCsvFile } from '../../helper/downloadHelper';
const state = {
fetchingStatus: false,
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index b0e081f98..184c7c6df 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -39,6 +39,7 @@ import {
} from '../dashboard/helper/scriptHelpers';
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon';
import VueDOMPurifyHTML from 'vue-dompurify-html';
+import { domPurifyConfig } from '../shared/helpers/HTMLSanitizer';
Vue.config.env = process.env;
@@ -55,7 +56,8 @@ if (window.analyticsConfig) {
api_host: window.analyticsConfig.host,
});
}
-Vue.use(VueDOMPurifyHTML);
+
+Vue.use(VueDOMPurifyHTML, domPurifyConfig);
Vue.use(VueRouter);
Vue.use(VueI18n);
Vue.use(WootUiKit);
diff --git a/app/javascript/packs/widget.js b/app/javascript/packs/widget.js
index 3b9661bc7..6eab2db8b 100644
--- a/app/javascript/packs/widget.js
+++ b/app/javascript/packs/widget.js
@@ -9,9 +9,10 @@ import ActionCableConnector from '../widget/helpers/actionCable';
import i18n from '../widget/i18n';
import { isPhoneE164OrEmpty } from 'shared/helpers/Validators';
import router from '../widget/router';
+import { domPurifyConfig } from '../shared/helpers/HTMLSanitizer';
Vue.use(VueI18n);
Vue.use(Vuelidate);
-Vue.use(VueDOMPurifyHTML);
+Vue.use(VueDOMPurifyHTML, domPurifyConfig);
const i18nConfig = new VueI18n({
locale: 'en',
diff --git a/app/javascript/shared/helpers/HTMLSanitizer.js b/app/javascript/shared/helpers/HTMLSanitizer.js
index b119b3681..889948c0b 100644
--- a/app/javascript/shared/helpers/HTMLSanitizer.js
+++ b/app/javascript/shared/helpers/HTMLSanitizer.js
@@ -6,3 +6,15 @@ export const escapeHtml = (unsafe = '') => {
.replace(/"/g, '"')
.replace(/'/g, ''');
};
+
+export const afterSanitizeAttributes = currentNode => {
+ if ('target' in currentNode) {
+ currentNode.setAttribute('target', '_blank');
+ }
+};
+
+export const domPurifyConfig = {
+ hooks: {
+ afterSanitizeAttributes,
+ },
+};
diff --git a/app/javascript/shared/helpers/MessageFormatter.js b/app/javascript/shared/helpers/MessageFormatter.js
index 260436da4..c4d0bb62e 100644
--- a/app/javascript/shared/helpers/MessageFormatter.js
+++ b/app/javascript/shared/helpers/MessageFormatter.js
@@ -1,6 +1,6 @@
import { marked } from 'marked';
import DOMPurify from 'dompurify';
-import { escapeHtml } from './HTMLSanitizer';
+import { escapeHtml, afterSanitizeAttributes } from './HTMLSanitizer';
const TWITTER_USERNAME_REGEX = /(^|[^@\w])@(\w{1,15})\b/g;
const TWITTER_USERNAME_REPLACEMENT =
@@ -48,9 +48,7 @@ class MessageFormatter {
const markedDownOutput = marked(withHash);
return markedDownOutput;
}
- DOMPurify.addHook('afterSanitizeAttributes', node => {
- if ('target' in node) node.setAttribute('target', '_blank');
- });
+ DOMPurify.addHook('afterSanitizeAttributes', afterSanitizeAttributes);
return DOMPurify.sanitize(
marked(this.message, { breaks: true, gfm: true })
);
diff --git a/app/models/account.rb b/app/models/account.rb
index 22aeefcaf..b14887957 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -51,9 +51,9 @@ class Account < ApplicationRecord
has_many :facebook_pages, dependent: :destroy_async, class_name: '::Channel::FacebookPage'
has_many :hooks, dependent: :destroy_async, class_name: 'Integrations::Hook'
has_many :inboxes, dependent: :destroy_async
- has_many :kbase_articles, dependent: :destroy_async, class_name: '::Kbase::Article'
- has_many :kbase_categories, dependent: :destroy_async, class_name: '::Kbase::Category'
- has_many :kbase_portals, dependent: :destroy_async, class_name: '::Kbase::Portal'
+ has_many :articles, dependent: :destroy_async, class_name: '::Article'
+ has_many :categories, dependent: :destroy_async, class_name: '::Category'
+ has_many :portals, dependent: :destroy_async, class_name: '::Portal'
has_many :labels, dependent: :destroy_async
has_many :line_channels, dependent: :destroy_async, class_name: '::Channel::Line'
has_many :mentions, dependent: :destroy_async
diff --git a/app/models/kbase/article.rb b/app/models/article.rb
similarity index 92%
rename from app/models/kbase/article.rb
rename to app/models/article.rb
index 0f14f6709..750e3a4fd 100644
--- a/app/models/kbase/article.rb
+++ b/app/models/article.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: kbase_articles
+# Table name: articles
#
# id :bigint not null, primary key
# content :text
@@ -16,7 +16,7 @@
# folder_id :integer
# portal_id :integer not null
#
-class Kbase::Article < ApplicationRecord
+class Article < ApplicationRecord
belongs_to :account
belongs_to :category
belongs_to :portal
diff --git a/app/models/kbase/category.rb b/app/models/category.rb
similarity index 79%
rename from app/models/kbase/category.rb
rename to app/models/category.rb
index d2b270306..2fb5815f2 100644
--- a/app/models/kbase/category.rb
+++ b/app/models/category.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: kbase_categories
+# Table name: categories
#
# id :bigint not null, primary key
# description :text
@@ -14,9 +14,10 @@
#
# Indexes
#
-# index_kbase_categories_on_locale_and_account_id (locale,account_id)
+# index_categories_on_locale (locale)
+# index_categories_on_locale_and_account_id (locale,account_id)
#
-class Kbase::Category < ApplicationRecord
+class Category < ApplicationRecord
belongs_to :account
belongs_to :portal
has_many :folders, dependent: :destroy_async
diff --git a/app/models/kbase/folder.rb b/app/models/folder.rb
similarity index 87%
rename from app/models/kbase/folder.rb
rename to app/models/folder.rb
index 887522b5d..315d1f340 100644
--- a/app/models/kbase/folder.rb
+++ b/app/models/folder.rb
@@ -1,6 +1,6 @@
# == Schema Information
#
-# Table name: kbase_folders
+# Table name: folders
#
# id :bigint not null, primary key
# name :string
@@ -9,7 +9,7 @@
# account_id :integer not null
# category_id :integer not null
#
-class Kbase::Folder < ApplicationRecord
+class Folder < ApplicationRecord
belongs_to :account
belongs_to :category
has_many :articles, dependent: :nullify
diff --git a/app/models/kbase/portal.rb b/app/models/portal.rb
similarity index 80%
rename from app/models/kbase/portal.rb
rename to app/models/portal.rb
index c381e5c46..8774820be 100644
--- a/app/models/kbase/portal.rb
+++ b/app/models/portal.rb
@@ -1,8 +1,9 @@
# == Schema Information
#
-# Table name: kbase_portals
+# Table name: portals
#
# id :bigint not null, primary key
+# archived :boolean default(FALSE)
# color :string
# config :jsonb
# custom_domain :string
@@ -17,13 +18,14 @@
#
# Indexes
#
-# index_kbase_portals_on_slug (slug) UNIQUE
+# index_portals_on_slug (slug) UNIQUE
#
-class Kbase::Portal < ApplicationRecord
+class Portal < ApplicationRecord
belongs_to :account
has_many :categories, dependent: :destroy_async
has_many :folders, through: :categories
has_many :articles, dependent: :destroy_async
+ has_many :users, through: :portals_members
validates :account_id, presence: true
validates :name, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 3b7a74672..6c3349473 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -90,6 +90,7 @@ class User < ApplicationRecord
has_many :notifications, dependent: :destroy_async
has_many :team_members, dependent: :destroy_async
has_many :teams, through: :team_members
+ has_many :portals, through: :portals_members
before_validation :set_password_and_uid, on: :create
diff --git a/app/policies/csat_survey_response_policy.rb b/app/policies/csat_survey_response_policy.rb
index c0ce8821b..afcce00e9 100644
--- a/app/policies/csat_survey_response_policy.rb
+++ b/app/policies/csat_survey_response_policy.rb
@@ -6,4 +6,8 @@ class CsatSurveyResponsePolicy < ApplicationPolicy
def metrics?
@account_user.administrator?
end
+
+ def download?
+ @account_user.administrator?
+ end
end
diff --git a/app/views/api/v1/accounts/kbase/categories/_category.json.jbuilder b/app/views/api/v1/accounts/categories/_category.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/categories/_category.json.jbuilder
rename to app/views/api/v1/accounts/categories/_category.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/categories/create.json.jbuilder b/app/views/api/v1/accounts/categories/create.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/categories/create.json.jbuilder
rename to app/views/api/v1/accounts/categories/create.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/categories/index.json.jbuilder b/app/views/api/v1/accounts/categories/index.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/categories/index.json.jbuilder
rename to app/views/api/v1/accounts/categories/index.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/categories/update.json.jbuilder b/app/views/api/v1/accounts/categories/update.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/categories/update.json.jbuilder
rename to app/views/api/v1/accounts/categories/update.json.jbuilder
diff --git a/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb b/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb
new file mode 100644
index 000000000..8b694ed35
--- /dev/null
+++ b/app/views/api/v1/accounts/csat_survey_responses/download.csv.erb
@@ -0,0 +1,38 @@
+<%=
+ CSV.generate_line([
+ I18n.t('reports.csat.headers.agent_name'),
+ I18n.t('reports.csat.headers.rating'),
+ I18n.t('reports.csat.headers.feedback'),
+ I18n.t('reports.csat.headers.contact_name'),
+ I18n.t('reports.csat.headers.contact_email_address'),
+ I18n.t('reports.csat.headers.contact_phone_number'),
+ I18n.t('reports.csat.headers.link_to_the_conversation'),
+ I18n.t('reports.csat.headers.recorded_at')
+ ])
+-%>
+<% @csat_survey_responses.each do |csat_response| %>
+<% assigned_agent = csat_response.assigned_agent %>
+<% contact = csat_response.contact %>
+<% conversation = csat_response.conversation %>
+<%=
+ CSV.generate_line([
+ assigned_agent ? "#{assigned_agent.name} (#{assigned_agent.email})" : nil,
+ csat_response.rating,
+ csat_response.feedback_message.present? ? csat_response.feedback_message : nil,
+ contact&.name.present? ? contact&.name: nil,
+ contact&.email.present? ? contact&.email: nil,
+ contact&.phone_number.present? ? contact&.phone_number: nil,
+ conversation ? app_account_conversation_url(account_id: Current.account.id, id: conversation.display_id): nil,
+ csat_response.created_at,
+ ])
+-%>
+<% end %>
+<%=
+ CSV.generate_line([
+ I18n.t(
+ 'reports.period',
+ since: Date.strptime(params[:since], '%s'),
+ until: Date.strptime(params[:until], '%s')
+ )
+ ])
+-%>
diff --git a/app/views/public/api/v1/models/kbase/_portal.json.jbuilder b/app/views/api/v1/accounts/portals/_portal.json.jbuilder
similarity index 89%
rename from app/views/public/api/v1/models/kbase/_portal.json.jbuilder
rename to app/views/api/v1/accounts/portals/_portal.json.jbuilder
index d612f5265..1f828d4ad 100644
--- a/app/views/public/api/v1/models/kbase/_portal.json.jbuilder
+++ b/app/views/api/v1/accounts/portals/_portal.json.jbuilder
@@ -6,4 +6,5 @@ json.homepage_link portal.homepage_link
json.name portal.name
json.page_title portal.page_title
json.slug portal.slug
+json.archived portal.archived
json.config portal.config
diff --git a/app/views/api/v1/accounts/kbase/portals/create.json.jbuilder b/app/views/api/v1/accounts/portals/create.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/portals/create.json.jbuilder
rename to app/views/api/v1/accounts/portals/create.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/portals/index.json.jbuilder b/app/views/api/v1/accounts/portals/index.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/portals/index.json.jbuilder
rename to app/views/api/v1/accounts/portals/index.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/portals/show.json.jbuilder b/app/views/api/v1/accounts/portals/show.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/portals/show.json.jbuilder
rename to app/views/api/v1/accounts/portals/show.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/portals/update.json.jbuilder b/app/views/api/v1/accounts/portals/update.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/portals/update.json.jbuilder
rename to app/views/api/v1/accounts/portals/update.json.jbuilder
diff --git a/app/views/public/api/v1/models/kbase/_category.json.jbuilder b/app/views/public/api/v1/models/_category.json.jbuilder
similarity index 100%
rename from app/views/public/api/v1/models/kbase/_category.json.jbuilder
rename to app/views/public/api/v1/models/_category.json.jbuilder
diff --git a/app/views/api/v1/accounts/kbase/portals/_portal.json.jbuilder b/app/views/public/api/v1/models/_portal.json.jbuilder
similarity index 100%
rename from app/views/api/v1/accounts/kbase/portals/_portal.json.jbuilder
rename to app/views/public/api/v1/models/_portal.json.jbuilder
diff --git a/config/app.yml b/config/app.yml
index d370e1d74..1ea45235d 100644
--- a/config/app.yml
+++ b/config/app.yml
@@ -1,5 +1,5 @@
shared: &shared
- version: '2.4.1'
+ version: '2.5.0'
development:
<<: *shared
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 0c92c32eb..bd7829b53 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -60,6 +60,16 @@ en:
avg_first_response_time: Avg first response time (Minutes)
avg_resolution_time: Avg resolution time (Minutes)
default_group_by: day
+ csat:
+ headers:
+ contact_name: Contact Name
+ contact_email_address: Contact Email Address
+ contact_phone_number: Contact Phone Number
+ link_to_the_conversation: Link to the conversation
+ agent_name: Agent Name
+ rating: Rating
+ feedback: Feedback Comment
+ recorded_at: Recorded date
notifications:
notification_title:
diff --git a/config/routes.rb b/config/routes.rb
index 7ed667160..a80f6ca39 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -106,6 +106,7 @@ Rails.application.routes.draw do
resources :csat_survey_responses, only: [:index] do
collection do
get :metrics
+ get :download
end
end
resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]
@@ -154,14 +155,15 @@ Rails.application.routes.draw do
end
resources :working_hours, only: [:update]
- namespace :kbase do
- resources :portals do
- resources :categories do
- resources :folders
- end
- resources :articles
+ resources :portals do
+ member do
+ post :archive
+ end
+ resources :categories do
+ resources :folders
end
end
+ resources :articles
end
end
# end of account scoped api routes
diff --git a/db/migrate/20220506061540_change_kbase_portals_to_portals.rb b/db/migrate/20220506061540_change_kbase_portals_to_portals.rb
new file mode 100644
index 000000000..c9765abbb
--- /dev/null
+++ b/db/migrate/20220506061540_change_kbase_portals_to_portals.rb
@@ -0,0 +1,5 @@
+class ChangeKbasePortalsToPortals < ActiveRecord::Migration[6.1]
+ def change
+ rename_table :kbase_portals, :portals
+ end
+end
diff --git a/db/migrate/20220506064938_create_portals_members_join_table.rb b/db/migrate/20220506064938_create_portals_members_join_table.rb
new file mode 100644
index 000000000..a24bb7356
--- /dev/null
+++ b/db/migrate/20220506064938_create_portals_members_join_table.rb
@@ -0,0 +1,9 @@
+class CreatePortalsMembersJoinTable < ActiveRecord::Migration[6.1]
+ def change
+ create_join_table :portals, :users, table_name: :portals_members do |t|
+ t.index :portal_id
+ t.index :user_id
+ t.index [:portal_id, :user_id], unique: true
+ end
+ end
+end
diff --git a/db/migrate/20220506072007_change_kbase_categories_to_categories.rb b/db/migrate/20220506072007_change_kbase_categories_to_categories.rb
new file mode 100644
index 000000000..b527e3e80
--- /dev/null
+++ b/db/migrate/20220506072007_change_kbase_categories_to_categories.rb
@@ -0,0 +1,5 @@
+class ChangeKbaseCategoriesToCategories < ActiveRecord::Migration[6.1]
+ def change
+ rename_table :kbase_categories, :categories
+ end
+end
diff --git a/db/migrate/20220506080338_change_kbase_folders_to_folders.rb b/db/migrate/20220506080338_change_kbase_folders_to_folders.rb
new file mode 100644
index 000000000..8e0ba6642
--- /dev/null
+++ b/db/migrate/20220506080338_change_kbase_folders_to_folders.rb
@@ -0,0 +1,5 @@
+class ChangeKbaseFoldersToFolders < ActiveRecord::Migration[6.1]
+ def change
+ rename_table :kbase_folders, :folders
+ end
+end
diff --git a/db/migrate/20220506080429_change_kbase_articles_to_articles.rb b/db/migrate/20220506080429_change_kbase_articles_to_articles.rb
new file mode 100644
index 000000000..ba041ca84
--- /dev/null
+++ b/db/migrate/20220506080429_change_kbase_articles_to_articles.rb
@@ -0,0 +1,5 @@
+class ChangeKbaseArticlesToArticles < ActiveRecord::Migration[6.1]
+ def change
+ rename_table :kbase_articles, :articles
+ end
+end
diff --git a/db/migrate/20220511072655_add_archive_column_to_portal.rb b/db/migrate/20220511072655_add_archive_column_to_portal.rb
new file mode 100644
index 000000000..b3a504e97
--- /dev/null
+++ b/db/migrate/20220511072655_add_archive_column_to_portal.rb
@@ -0,0 +1,5 @@
+class AddArchiveColumnToPortal < ActiveRecord::Migration[6.1]
+ def change
+ add_column :portals, :archived, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 157e2ac2f..32f7dbefb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2022_05_06_163839) do
+ActiveRecord::Schema.define(version: 2022_05_11_072655) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
@@ -111,6 +111,21 @@ ActiveRecord::Schema.define(version: 2022_05_06_163839) do
t.index ["account_id"], name: "index_agent_bots_on_account_id"
end
+ create_table "articles", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.integer "portal_id", null: false
+ t.integer "category_id"
+ t.integer "folder_id"
+ t.integer "author_id"
+ t.string "title"
+ t.text "description"
+ t.text "content"
+ t.integer "status"
+ t.integer "views"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ end
+
create_table "attachments", id: :serial, force: :cascade do |t|
t.integer "file_type", default: 0
t.string "external_url"
@@ -169,6 +184,19 @@ ActiveRecord::Schema.define(version: 2022_05_06_163839) do
t.datetime "updated_at", null: false
end
+ create_table "categories", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.integer "portal_id", null: false
+ t.string "name"
+ t.text "description"
+ t.integer "position"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.string "locale", default: "en"
+ t.index ["locale", "account_id"], name: "index_categories_on_locale_and_account_id"
+ t.index ["locale"], name: "index_categories_on_locale"
+ end
+
create_table "channel_api", force: :cascade do |t|
t.integer "account_id", null: false
t.string "webhook_url"
@@ -434,6 +462,14 @@ ActiveRecord::Schema.define(version: 2022_05_06_163839) do
t.index ["name", "account_id"], name: "index_email_templates_on_name_and_account_id", unique: true
end
+ create_table "folders", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.integer "category_id", null: false
+ t.string "name"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ end
+
create_table "inbox_members", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "inbox_id", null: false
@@ -486,56 +522,6 @@ ActiveRecord::Schema.define(version: 2022_05_06_163839) do
t.jsonb "settings", default: {}
end
- create_table "kbase_articles", force: :cascade do |t|
- t.integer "account_id", null: false
- t.integer "portal_id", null: false
- t.integer "category_id"
- t.integer "folder_id"
- t.integer "author_id"
- t.string "title"
- t.text "description"
- t.text "content"
- t.integer "status"
- t.integer "views"
- t.datetime "created_at", precision: 6, null: false
- t.datetime "updated_at", precision: 6, null: false
- end
-
- create_table "kbase_categories", force: :cascade do |t|
- t.integer "account_id", null: false
- t.integer "portal_id", null: false
- t.string "name"
- t.text "description"
- t.integer "position"
- t.datetime "created_at", precision: 6, null: false
- t.datetime "updated_at", precision: 6, null: false
- t.string "locale", default: "en"
- t.index ["locale", "account_id"], name: "index_kbase_categories_on_locale_and_account_id"
- end
-
- create_table "kbase_folders", force: :cascade do |t|
- t.integer "account_id", null: false
- t.integer "category_id", null: false
- t.string "name"
- t.datetime "created_at", precision: 6, null: false
- t.datetime "updated_at", precision: 6, null: false
- end
-
- create_table "kbase_portals", force: :cascade do |t|
- t.integer "account_id", null: false
- t.string "name", null: false
- t.string "slug", null: false
- t.string "custom_domain"
- t.string "color"
- t.string "homepage_link"
- t.string "page_title"
- t.text "header_text"
- t.datetime "created_at", precision: 6, null: false
- t.datetime "updated_at", precision: 6, null: false
- t.jsonb "config", default: {"allowed_locales"=>["en"]}
- t.index ["slug"], name: "index_kbase_portals_on_slug", unique: true
- end
-
create_table "labels", force: :cascade do |t|
t.string "title"
t.text "description"
@@ -653,6 +639,30 @@ ActiveRecord::Schema.define(version: 2022_05_06_163839) do
t.datetime "updated_at", precision: 6, null: false
end
+ create_table "portals", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.string "name", null: false
+ t.string "slug", null: false
+ t.string "custom_domain"
+ t.string "color"
+ t.string "homepage_link"
+ t.string "page_title"
+ t.text "header_text"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.jsonb "config", default: {"allowed_locales"=>["en"]}
+ t.boolean "archived", default: false
+ t.index ["slug"], name: "index_portals_on_slug", unique: true
+ end
+
+ create_table "portals_members", id: false, force: :cascade do |t|
+ t.bigint "portal_id", null: false
+ t.bigint "user_id", null: false
+ t.index ["portal_id", "user_id"], name: "index_portals_members_on_portal_id_and_user_id", unique: true
+ t.index ["portal_id"], name: "index_portals_members_on_portal_id"
+ t.index ["user_id"], name: "index_portals_members_on_user_id"
+ end
+
create_table "reporting_events", force: :cascade do |t|
t.string "name"
t.float "value"
diff --git a/package.json b/package.json
index 8bfdaf74a..f862e0fbe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@chatwoot/chatwoot",
- "version": "2.4.1",
+ "version": "2.5.0",
"license": "MIT",
"scripts": {
"eslint": "eslint app/**/*.{js,vue} --fix",
@@ -63,7 +63,7 @@
"vue-chartjs": "3.5.1",
"vue-clickaway": "~2.1.0",
"vue-color": "2.8.1",
- "vue-dompurify-html": "^2.5.1",
+ "vue-dompurify-html": "^2.5.2",
"vue-easytable": "2.5.5",
"vue-i18n": "8.24.3",
"vue-loader": "15.9.6",
diff --git a/spec/controllers/api/v1/accounts/kbase/categories_controller_spec.rb b/spec/controllers/api/v1/accounts/categories_controller_spec.rb
similarity index 61%
rename from spec/controllers/api/v1/accounts/kbase/categories_controller_spec.rb
rename to spec/controllers/api/v1/accounts/categories_controller_spec.rb
index 4d98cd898..a1b767166 100644
--- a/spec/controllers/api/v1/accounts/kbase/categories_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/categories_controller_spec.rb
@@ -1,15 +1,15 @@
require 'rails_helper'
-RSpec.describe 'Api::V1::Accounts::Kbase::Categories', type: :request do
+RSpec.describe 'Api::V1::Accounts::Categories', type: :request do
let(:account) { create(:account) }
let(:agent) { create(:user, account: account, role: :agent) }
- let!(:portal) { create(:kbase_portal, name: 'test_portal', account_id: account.id) }
- let!(:category) { create(:kbase_category, name: 'category', portal: portal, account_id: account.id) }
+ let!(:portal) { create(:portal, name: 'test_portal', account_id: account.id) }
+ let!(:category) { create(:category, name: 'category', portal: portal, account_id: account.id) }
- describe 'POST /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}/categories' do
+ describe 'POST /api/v1/accounts/{account.id}/portals/{portal.slug}/categories' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- post "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories", params: {}
+ post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
@@ -23,7 +23,7 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Categories', type: :request do
position: 1
}
}
- post "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories",
+ post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories",
params: category_params,
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
@@ -33,10 +33,10 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Categories', type: :request do
end
end
- describe 'PUT /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}/categories/{category.id}' do
+ describe 'PUT /api/v1/accounts/{account.id}/portals/{portal.slug}/categories/{category.id}' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- put "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories/#{category.id}", params: {}
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories/#{category.id}", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
@@ -53,7 +53,7 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Categories', type: :request do
expect(category.name).not_to eql(category_params[:category][:name])
- put "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories/#{category.id}",
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories/#{category.id}",
params: category_params,
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
@@ -63,39 +63,39 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Categories', type: :request do
end
end
- describe 'DELETE /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}/categories/{category.id}' do
+ describe 'DELETE /api/v1/accounts/{account.id}/portals/{portal.slug}/categories/{category.id}' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- delete "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories/#{category.id}", params: {}
+ delete "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories/#{category.id}", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'deletes category' do
- delete "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories/#{category.id}",
+ delete "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories/#{category.id}",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
- deleted_category = Kbase::Category.find_by(id: category.id)
+ deleted_category = Category.find_by(id: category.id)
expect(deleted_category).to be nil
end
end
end
- describe 'GET /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}/categories' do
+ describe 'GET /api/v1/accounts/{account.id}/portals/{portal.slug}/categories' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- get "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories"
+ get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories"
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'get all portals' do
- category2 = create(:kbase_category, name: 'test_category_2', portal: portal)
+ category2 = create(:category, name: 'test_category_2', portal: portal)
expect(category2.id).not_to be nil
- get "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}/categories",
+ get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
diff --git a/spec/controllers/api/v1/accounts/csat_survey_responses_controller_spec.rb b/spec/controllers/api/v1/accounts/csat_survey_responses_controller_spec.rb
index abb4231d9..f431f0ed5 100644
--- a/spec/controllers/api/v1/accounts/csat_survey_responses_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/csat_survey_responses_controller_spec.rb
@@ -148,4 +148,38 @@ RSpec.describe 'CSAT Survey Responses API', type: :request do
end
end
end
+
+ describe 'GET /api/v1/accounts/{account.id}/csat_survey_responses/download' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/csat_survey_responses/download"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated user' do
+ let(:params) { { since: 5.days.ago.to_time.to_i.to_s, until: Time.zone.tomorrow.to_time.to_i.to_s } }
+
+ it 'returns unauthorized for agents' do
+ get "/api/v1/accounts/#{account.id}/csat_survey_responses/download",
+ params: params,
+ headers: agent.create_new_auth_token
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+
+ it 'returns summary' do
+ get "/api/v1/accounts/#{account.id}/csat_survey_responses/download",
+ params: params,
+ headers: administrator.create_new_auth_token
+
+ expect(response).to have_http_status(:success)
+
+ content = CSV.parse(response.body)
+ # Check rating from CSAT Row
+ expect(content[1][1]).to eq '1'
+ expect(content.length).to eq 3
+ end
+ end
+ end
end
diff --git a/spec/controllers/api/v1/accounts/kbase/portals_controller_spec.rb b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
similarity index 60%
rename from spec/controllers/api/v1/accounts/kbase/portals_controller_spec.rb
rename to spec/controllers/api/v1/accounts/portals_controller_spec.rb
index eae356930..d4c923d58 100644
--- a/spec/controllers/api/v1/accounts/kbase/portals_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
@@ -1,23 +1,23 @@
require 'rails_helper'
-RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
+RSpec.describe 'Api::V1::Accounts::Portals', type: :request do
let(:account) { create(:account) }
let(:agent) { create(:user, account: account, role: :agent) }
- let!(:portal) { create(:kbase_portal, slug: 'portal-1', name: 'test_portal', account_id: account.id) }
+ let!(:portal) { create(:portal, slug: 'portal-1', name: 'test_portal', account_id: account.id) }
- describe 'GET /api/v1/accounts/{account.id}/kbase/portals' do
+ describe 'GET /api/v1/accounts/{account.id}/portals' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- get "/api/v1/accounts/#{account.id}/kbase/portals"
+ get "/api/v1/accounts/#{account.id}/portals"
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'get all portals' do
- portal2 = create(:kbase_portal, name: 'test_portal_2', account_id: account.id, slug: 'portal-2')
+ portal2 = create(:portal, name: 'test_portal_2', account_id: account.id, slug: 'portal-2')
expect(portal2.id).not_to be nil
- get "/api/v1/accounts/#{account.id}/kbase/portals",
+ get "/api/v1/accounts/#{account.id}/portals",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
@@ -26,17 +26,17 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
end
end
- describe 'GET /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}' do
+ describe 'GET /api/v1/accounts/{account.id}/portals/{portal.slug}' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- get "/api/v1/accounts/#{account.id}/kbase/portals"
+ get "/api/v1/accounts/#{account.id}/portals"
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'get one portals' do
- get "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}",
+ get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
@@ -45,10 +45,10 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
end
end
- describe 'POST /api/v1/accounts/{account.id}/kbase/portals' do
+ describe 'POST /api/v1/accounts/{account.id}/portals' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- post "/api/v1/accounts/#{account.id}/kbase/portals", params: {}
+ post "/api/v1/accounts/#{account.id}/portals", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
@@ -61,7 +61,7 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
slug: 'test_kbase'
}
}
- post "/api/v1/accounts/#{account.id}/kbase/portals",
+ post "/api/v1/accounts/#{account.id}/portals",
params: portal_params,
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
@@ -71,10 +71,10 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
end
end
- describe 'PUT /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}' do
+ describe 'PUT /api/v1/accounts/{account.id}/portals/{portal.slug}' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- put "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}", params: {}
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
@@ -89,30 +89,50 @@ RSpec.describe 'Api::V1::Accounts::Kbase::Portals', type: :request do
expect(portal.name).to eql('test_portal')
- put "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}",
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
params: portal_params,
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
json_response = JSON.parse(response.body)
expect(json_response['name']).to eql(portal_params[:portal][:name])
end
+
+ it 'archive portal' do
+ portal_params = {
+ portal: {
+ archived: true
+ }
+ }
+
+ expect(portal.archived).to be_falsy
+
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
+ params: portal_params,
+ headers: agent.create_new_auth_token
+ expect(response).to have_http_status(:success)
+ json_response = JSON.parse(response.body)
+ expect(json_response['archived']).to eql(portal_params[:portal][:archived])
+
+ portal.reload
+ expect(portal.archived).to be_truthy
+ end
end
end
- describe 'DELETE /api/v1/accounts/{account.id}/kbase/portals/{portal.slug}' do
+ describe 'DELETE /api/v1/accounts/{account.id}/portals/{portal.slug}' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
- delete "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}", params: {}
+ delete "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", params: {}
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'deletes portal' do
- delete "/api/v1/accounts/#{account.id}/kbase/portals/#{portal.slug}",
+ delete "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:success)
- deleted_portal = Kbase::Portal.find_by(id: portal.slug)
+ deleted_portal = Portal.find_by(id: portal.slug)
expect(deleted_portal).to be nil
end
end
diff --git a/spec/factories/kbase/articles.rb b/spec/factories/articles.rb
similarity index 81%
rename from spec/factories/kbase/articles.rb
rename to spec/factories/articles.rb
index 9c19d7c9e..3f0c94c47 100644
--- a/spec/factories/kbase/articles.rb
+++ b/spec/factories/articles.rb
@@ -1,5 +1,5 @@
FactoryBot.define do
- factory :kbase_article, class: 'Kbase::Article' do
+ factory :article, class: 'Article' do
account_id { 1 }
category_id { 1 }
folder_id { 1 }
diff --git a/spec/factories/kbase/categories.rb b/spec/factories/categories.rb
similarity index 70%
rename from spec/factories/kbase/categories.rb
rename to spec/factories/categories.rb
index 6b4c47ce4..f7becf8fc 100644
--- a/spec/factories/kbase/categories.rb
+++ b/spec/factories/categories.rb
@@ -1,6 +1,6 @@
FactoryBot.define do
- factory :kbase_category, class: 'Kbase::Category' do
- portal { kbase_portal }
+ factory :category, class: 'Category' do
+ portal { portal }
name { 'MyString' }
description { 'MyText' }
position { 1 }
diff --git a/spec/factories/kbase/folders.rb b/spec/factories/folders.rb
similarity index 71%
rename from spec/factories/kbase/folders.rb
rename to spec/factories/folders.rb
index 00cbabedd..62be3d263 100644
--- a/spec/factories/kbase/folders.rb
+++ b/spec/factories/folders.rb
@@ -1,5 +1,5 @@
FactoryBot.define do
- factory :kbase_folder, class: 'Kbase::Folder' do
+ factory :folder, class: 'Folder' do
account_id { 1 }
name { 'MyString' }
description { 'MyText' }
diff --git a/spec/factories/kbase/portals.rb b/spec/factories/portals.rb
similarity index 66%
rename from spec/factories/kbase/portals.rb
rename to spec/factories/portals.rb
index fb37b8b56..d49de096a 100644
--- a/spec/factories/kbase/portals.rb
+++ b/spec/factories/portals.rb
@@ -1,5 +1,5 @@
FactoryBot.define do
- factory :kbase_portal, class: 'Kbase::Portal' do
+ factory :portal, class: 'Portal' do
account
name { Faker::Book.name }
slug { SecureRandom.hex }
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index ff5f9cca5..e9906f131 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -19,8 +19,8 @@ RSpec.describe Account do
it { is_expected.to have_many(:webhooks).dependent(:destroy_async) }
it { is_expected.to have_many(:notification_settings).dependent(:destroy_async) }
it { is_expected.to have_many(:reporting_events) }
- it { is_expected.to have_many(:kbase_portals).dependent(:destroy_async) }
- it { is_expected.to have_many(:kbase_categories).dependent(:destroy_async) }
+ it { is_expected.to have_many(:portals).dependent(:destroy_async) }
+ it { is_expected.to have_many(:categories).dependent(:destroy_async) }
it { is_expected.to have_many(:teams).dependent(:destroy_async) }
describe 'usage_limits' do
diff --git a/spec/models/kbase/article_spec.rb b/spec/models/article_spec.rb
similarity index 91%
rename from spec/models/kbase/article_spec.rb
rename to spec/models/article_spec.rb
index 14729b1ed..ee1e7f618 100644
--- a/spec/models/kbase/article_spec.rb
+++ b/spec/models/article_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Kbase::Article, type: :model do
+RSpec.describe Article, type: :model do
context 'with validations' do
it { is_expected.to validate_presence_of(:account_id) }
it { is_expected.to validate_presence_of(:category_id) }
diff --git a/spec/models/kbase/category_spec.rb b/spec/models/category_spec.rb
similarity index 89%
rename from spec/models/kbase/category_spec.rb
rename to spec/models/category_spec.rb
index 4b84cb352..55bad9a14 100644
--- a/spec/models/kbase/category_spec.rb
+++ b/spec/models/category_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Kbase::Category, type: :model do
+RSpec.describe Category, type: :model do
context 'with validations' do
it { is_expected.to validate_presence_of(:account_id) }
it { is_expected.to validate_presence_of(:name) }
diff --git a/spec/models/kbase/folder_spec.rb b/spec/models/folder_spec.rb
similarity index 90%
rename from spec/models/kbase/folder_spec.rb
rename to spec/models/folder_spec.rb
index 4af97a644..47c9adf06 100644
--- a/spec/models/kbase/folder_spec.rb
+++ b/spec/models/folder_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Kbase::Folder, type: :model do
+RSpec.describe Folder, type: :model do
context 'with validations' do
it { is_expected.to validate_presence_of(:account_id) }
it { is_expected.to validate_presence_of(:category_id) }
diff --git a/spec/models/kbase/portal_spec.rb b/spec/models/portal_spec.rb
similarity index 90%
rename from spec/models/kbase/portal_spec.rb
rename to spec/models/portal_spec.rb
index 67f79bf81..e7fe633bc 100644
--- a/spec/models/kbase/portal_spec.rb
+++ b/spec/models/portal_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Kbase::Portal, type: :model do
+RSpec.describe Portal, type: :model do
context 'with validations' do
it { is_expected.to validate_presence_of(:account_id) }
it { is_expected.to validate_presence_of(:slug) }
diff --git a/yarn.lock b/yarn.lock
index 82d266618..fa46ddb37 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15284,10 +15284,10 @@ vue-docgen-loader@^1.5.0:
loader-utils "^1.2.3"
querystring "^0.2.0"
-vue-dompurify-html@^2.5.1:
- version "2.5.1"
- resolved "https://registry.npmjs.org/vue-dompurify-html/-/vue-dompurify-html-2.5.1.tgz#a754f4ac7b18eb8fe41f461cb2bb1c4956a9bd2d"
- integrity sha512-B8rQj2jAPJJhtKHHa6jg5B3/RoKBmmUl/awP/GxWXGu75j4Y7+MHqv0DG52v0Uz0taEpHyZun34KEYMAfrPWnA==
+vue-dompurify-html@^2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/vue-dompurify-html/-/vue-dompurify-html-2.5.2.tgz#f547d4eacae4640f95eb0e9308e7ef8e223887c6"
+ integrity sha512-G6I135+BhlACJ9xftqK7fvhXyjNrgHCI594qHnUW5e2Bmp8BOTV1kz7cxwI37b4BJnHkj9IY10RwMPOtJqw+pw==
dependencies:
dompurify "^2.3.4"