Merge branch 'develop' into foss-gh-action-arm64
This commit is contained in:
commit
ce31454dc8
23 changed files with 144 additions and 98 deletions
|
@ -7,8 +7,8 @@ end_of_line = lf
|
|||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = spaces
|
||||
indent_style = space
|
||||
tab_width = 2
|
||||
|
||||
[{*.{rb,erb,js,coffee,json,yml,css,scss,sh,markdown,md,html}]
|
||||
[*.{rb,erb,js,coffee,json,yml,css,scss,sh,markdown,md,html}]
|
||||
indent_size = 2
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -39,9 +39,6 @@ public/packs*
|
|||
*.un~
|
||||
.jest-cache
|
||||
|
||||
#VS Code files
|
||||
.vscode
|
||||
|
||||
# ignore jetbrains IDE files
|
||||
.idea
|
||||
|
||||
|
@ -62,4 +59,4 @@ package-lock.json
|
|||
test/cypress/videos/*
|
||||
|
||||
/config/master.key
|
||||
/config/*.enc
|
||||
/config/*.enc
|
||||
|
|
32
.vscode/extensions.json
vendored
Normal file
32
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"recommendations": [
|
||||
// Spell check
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
// Better Comments
|
||||
"aaron-bond.better-comments",
|
||||
// Rails Test Runner
|
||||
"davidpallinder.rails-test-runner",
|
||||
// Eslint
|
||||
"dbaeumer.vscode-eslint",
|
||||
// Auto Close Tag
|
||||
"formulahendry.auto-close-tag",
|
||||
// Auto Rename Tag
|
||||
"formulahendry.auto-rename-tag",
|
||||
// Hight light colors
|
||||
"naumovs.color-highlight",
|
||||
// GitLens
|
||||
"eamodio.gitlens",
|
||||
// Ruby
|
||||
"rebornix.ruby",
|
||||
// Vue
|
||||
"octref.vetur",
|
||||
// Prettier
|
||||
"esbenp.prettier-vscode",
|
||||
// Dot Env
|
||||
"mikestead.dotenv",
|
||||
// HTML CSS Support
|
||||
"ecmel.vscode-html-css",
|
||||
// Tailwind CSS Intellisense
|
||||
"bradlc.vscode-tailwindcss",
|
||||
]
|
||||
}
|
|
@ -39,7 +39,7 @@ const primaryMenuItems = accountId => [
|
|||
label: 'HELP_CENTER.TITLE',
|
||||
featureFlag: 'help_center',
|
||||
toState: frontendURL(`accounts/${accountId}/portals`),
|
||||
toStateName: 'list_all_portals',
|
||||
toStateName: 'default_portal_articles',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -78,7 +78,7 @@ export default {
|
|||
if (initials.length > 2 && initials.search(/[A-Z]/) !== -1) {
|
||||
initials = initials.replace(/[a-z]+/g, '');
|
||||
}
|
||||
initials = initials.substr(0, 2).toUpperCase();
|
||||
initials = initials.substring(0, 2).toUpperCase();
|
||||
return initials;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -477,7 +477,7 @@ export default {
|
|||
const hasNextWord = updatedMessage.includes(' ');
|
||||
const isShortCodeActive = this.hasSlashCommand && !hasNextWord;
|
||||
if (isShortCodeActive) {
|
||||
this.mentionSearchKey = updatedMessage.substr(1, updatedMessage.length);
|
||||
this.mentionSearchKey = updatedMessage.substring(1);
|
||||
this.showMentions = true;
|
||||
} else {
|
||||
this.mentionSearchKey = '';
|
||||
|
|
|
@ -239,7 +239,7 @@ export default {
|
|||
const hasNextWord = value.includes(' ');
|
||||
const isShortCodeActive = this.hasSlashCommand && !hasNextWord;
|
||||
if (isShortCodeActive) {
|
||||
this.cannedResponseSearchKey = value.substr(1, value.length);
|
||||
this.cannedResponseSearchKey = value.substring(1);
|
||||
this.showCannedResponseMenu = true;
|
||||
} else {
|
||||
this.cannedResponseSearchKey = '';
|
||||
|
|
|
@ -59,6 +59,7 @@ import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
|
|||
import CommandBar from 'dashboard/routes/dashboard/commands/commandbar.vue';
|
||||
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal';
|
||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import portalMixin from '../mixins/portalMixin';
|
||||
import AddCategory from '../pages/categories/AddCategory';
|
||||
|
||||
|
@ -72,7 +73,7 @@ export default {
|
|||
PortalPopover,
|
||||
AddCategory,
|
||||
},
|
||||
mixins: [portalMixin],
|
||||
mixins: [portalMixin, uiSettingsMixin],
|
||||
data() {
|
||||
return {
|
||||
isSidebarOpen: false,
|
||||
|
@ -231,7 +232,13 @@ export default {
|
|||
},
|
||||
updated() {
|
||||
const slug = this.$route.params.portalSlug;
|
||||
if (slug) this.lastActivePortalSlug = slug;
|
||||
if (slug) {
|
||||
this.lastActivePortalSlug = slug;
|
||||
this.updateUISettings({
|
||||
last_active_portal_slug: slug,
|
||||
last_active_locale_code: this.selectedLocaleInPortal,
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleResize() {
|
||||
|
|
|
@ -188,13 +188,15 @@
|
|||
<script>
|
||||
import thumbnail from 'dashboard/components/widgets/Thumbnail';
|
||||
import LocaleItemTable from './PortalListItemTable';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
thumbnail,
|
||||
LocaleItemTable,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
mixins: [alertMixin, uiSettingsMixin],
|
||||
props: {
|
||||
portal: {
|
||||
type: Object,
|
||||
|
@ -274,6 +276,10 @@ export default {
|
|||
this.alertMessage = this.$t(
|
||||
'HELP_CENTER.PORTAL.PORTAL_SETTINGS.DELETE_PORTAL.API.DELETE_SUCCESS'
|
||||
);
|
||||
this.updateUISettings({
|
||||
last_active_portal_slug: undefined,
|
||||
last_active_locale_code: undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
this.alertMessage =
|
||||
error?.message ||
|
||||
|
|
|
@ -21,12 +21,20 @@ const EditCategory = () => import('./pages/categories/EditCategory');
|
|||
const ListCategoryArticles = () =>
|
||||
import('./pages/articles/ListCategoryArticles');
|
||||
const ListAllArticles = () => import('./pages/articles/ListAllArticles');
|
||||
const DefaultPortalArticles = () =>
|
||||
import('./pages/articles/DefaultPortalArticles');
|
||||
const NewArticle = () => import('./pages/articles/NewArticle');
|
||||
const EditArticle = () => import('./pages/articles/EditArticle');
|
||||
|
||||
const portalRoutes = [
|
||||
{
|
||||
path: getPortalRoute(''),
|
||||
name: 'default_portal_articles',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: DefaultPortalArticles,
|
||||
},
|
||||
{
|
||||
path: getPortalRoute('all'),
|
||||
name: 'list_all_portals',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: ListAllPortals,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<div>Loading...</div>
|
||||
</template>
|
||||
<script>
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
mixins: [uiSettingsMixin],
|
||||
mounted() {
|
||||
const {
|
||||
last_active_portal_slug: lastActivePortalSlug,
|
||||
last_active_locale_code: lastActiveLocaleCode,
|
||||
} = this.uiSettings || {};
|
||||
|
||||
if (lastActivePortalSlug)
|
||||
this.$router.push({
|
||||
name: 'list_all_locale_articles',
|
||||
params: {
|
||||
portalSlug: lastActivePortalSlug,
|
||||
locale: lastActiveLocaleCode,
|
||||
},
|
||||
replace: true,
|
||||
});
|
||||
else
|
||||
this.$router.push({
|
||||
name: 'list_all_portals',
|
||||
replace: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -19,15 +19,11 @@
|
|||
:script="currentInbox.callback_webhook_url"
|
||||
/>
|
||||
</div>
|
||||
<div class="medium-6 small-offset-3">
|
||||
<div v-if="isWhatsAppCloudInbox" class="medium-6 small-offset-3">
|
||||
<p class="config--label">
|
||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }}
|
||||
</p>
|
||||
<woot-code
|
||||
v-if="isWhatsAppCloudInbox"
|
||||
lang="html"
|
||||
:script="currentInbox.callback_webhook_url"
|
||||
/>
|
||||
<woot-code lang="html" :script="currentInbox.callback_webhook_url" />
|
||||
<p class="config--label">
|
||||
{{
|
||||
$t(
|
||||
|
@ -36,7 +32,6 @@
|
|||
}}
|
||||
</p>
|
||||
<woot-code
|
||||
v-if="isWhatsAppCloudInbox"
|
||||
lang="html"
|
||||
:script="currentInbox.provider_config.webhook_verify_token"
|
||||
/>
|
||||
|
|
|
@ -243,7 +243,7 @@ export default {
|
|||
this.$t('INBOX_MGMT.WIDGET_BUILDER.SCRIPT_SETTINGS', {
|
||||
options: JSON.stringify(options),
|
||||
}) +
|
||||
script.substring(13, script.length)
|
||||
script.substring(13)
|
||||
);
|
||||
},
|
||||
getWidgetViewOptions() {
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
getUserCookieName,
|
||||
hasUserKeys,
|
||||
} from '../sdk/cookieHelpers';
|
||||
import { addClass, removeClass } from '../sdk/DOMHelpers';
|
||||
import { addClasses, removeClasses } from '../sdk/DOMHelpers';
|
||||
import { SDK_SET_BUBBLE_VISIBILITY } from 'shared/constants/sharedFrameEvents';
|
||||
const runSDK = ({ baseUrl, websiteToken }) => {
|
||||
if (window.$chatwoot) {
|
||||
|
@ -41,12 +41,12 @@ const runSDK = ({ baseUrl, websiteToken }) => {
|
|||
let widgetElm = document.querySelector('.woot--bubble-holder');
|
||||
let widgetHolder = document.querySelector('.woot-widget-holder');
|
||||
if (visibility === 'hide') {
|
||||
addClass(widgetHolder, 'woot-widget--without-bubble');
|
||||
addClass(widgetElm, 'woot-hidden');
|
||||
addClasses(widgetHolder, 'woot-widget--without-bubble');
|
||||
addClasses(widgetElm, 'woot-hidden');
|
||||
window.$chatwoot.hideMessageBubble = true;
|
||||
} else if (visibility === 'show') {
|
||||
removeClass(widgetElm, 'woot-hidden');
|
||||
removeClass(widgetHolder, 'woot-widget--without-bubble');
|
||||
removeClasses(widgetElm, 'woot-hidden');
|
||||
removeClasses(widgetHolder, 'woot-widget--without-bubble');
|
||||
window.$chatwoot.hideMessageBubble = false;
|
||||
}
|
||||
IFrameHelper.sendMessage(SDK_SET_BUBBLE_VISIBILITY, {
|
||||
|
|
|
@ -3,68 +3,20 @@ import { IFrameHelper } from './IFrameHelper';
|
|||
|
||||
export const loadCSS = () => {
|
||||
const css = document.createElement('style');
|
||||
css.type = 'text/css';
|
||||
css.innerHTML = `${SDK_CSS}`;
|
||||
document.body.appendChild(css);
|
||||
};
|
||||
|
||||
export const wootOn = (elm, event, fn) => {
|
||||
if (document.addEventListener) {
|
||||
elm.addEventListener(event, fn, false);
|
||||
} else if (document.attachEvent) {
|
||||
// <= IE 8 loses scope so need to apply, we add this to object so we
|
||||
// can detach later (can't detach anonymous functions)
|
||||
// eslint-disable-next-line
|
||||
elm[event + fn] = function() {
|
||||
// eslint-disable-next-line
|
||||
return fn.apply(elm, arguments);
|
||||
};
|
||||
elm.attachEvent(`on${event}`, elm[event + fn]);
|
||||
}
|
||||
};
|
||||
|
||||
export const classHelper = (classes, action, elm) => {
|
||||
let search;
|
||||
let replace;
|
||||
let i;
|
||||
let has = false;
|
||||
if (classes) {
|
||||
// Trim any whitespace
|
||||
const classarray = classes.split(/\s+/);
|
||||
for (i = 0; i < classarray.length; i += 1) {
|
||||
search = new RegExp(`\\b${classarray[i]}\\b`, 'g');
|
||||
replace = new RegExp(` *${classarray[i]}\\b`, 'g');
|
||||
if (action === 'remove') {
|
||||
// eslint-disable-next-line
|
||||
elm.className = elm.className.replace(replace, '');
|
||||
} else if (action === 'toggle') {
|
||||
// eslint-disable-next-line
|
||||
elm.className = elm.className.match(search)
|
||||
? elm.className.replace(replace, '')
|
||||
: `${elm.className} ${classarray[i]}`;
|
||||
} else if (action === 'has') {
|
||||
if (elm.className.match(search)) {
|
||||
has = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return has;
|
||||
};
|
||||
|
||||
export const addClass = (elm, classes) => {
|
||||
if (classes) {
|
||||
elm.className += ` ${classes}`;
|
||||
}
|
||||
export const addClasses = (elm, classes) => {
|
||||
elm.classList.add(...classes.split(' '));
|
||||
};
|
||||
|
||||
export const toggleClass = (elm, classes) => {
|
||||
classHelper(classes, 'toggle', elm);
|
||||
elm.classList.toggle(classes);
|
||||
};
|
||||
|
||||
export const removeClass = (elm, classes) => {
|
||||
classHelper(classes, 'remove', elm);
|
||||
export const removeClasses = (elm, classes) => {
|
||||
elm.classList.remove(...classes.split(' '));
|
||||
};
|
||||
|
||||
export const onLocationChange = ({ referrerURL, referrerHost }) => {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import Cookies from 'js-cookie';
|
||||
import {
|
||||
wootOn,
|
||||
addClass,
|
||||
addClasses,
|
||||
loadCSS,
|
||||
removeClass,
|
||||
removeClasses,
|
||||
onLocationChangeListener,
|
||||
} from './DOMHelpers';
|
||||
import {
|
||||
|
@ -68,7 +67,7 @@ export const IFrameHelper = {
|
|||
holderClassName += ` woot-widget-holder--flat`;
|
||||
}
|
||||
|
||||
addClass(widgetHolder, holderClassName);
|
||||
addClasses(widgetHolder, holderClassName);
|
||||
widgetHolder.appendChild(iframe);
|
||||
body.appendChild(widgetHolder);
|
||||
IFrameHelper.initPostMessageCommunication();
|
||||
|
@ -99,7 +98,7 @@ export const IFrameHelper = {
|
|||
};
|
||||
},
|
||||
initWindowSizeListener: () => {
|
||||
wootOn(window, 'resize', () => IFrameHelper.toggleCloseButton());
|
||||
window.addEventListener('resize', () => IFrameHelper.toggleCloseButton());
|
||||
},
|
||||
preventDefaultScroll: () => {
|
||||
widgetHolder.addEventListener('wheel', event => {
|
||||
|
@ -241,9 +240,9 @@ export const IFrameHelper = {
|
|||
event.unreadMessageCount > 0 &&
|
||||
!bubbleElement.classList.contains('unread-notification')
|
||||
) {
|
||||
addClass(bubbleElement, 'unread-notification');
|
||||
addClasses(bubbleElement, 'unread-notification');
|
||||
} else if (event.unreadMessageCount === 0) {
|
||||
removeClass(bubbleElement, 'unread-notification');
|
||||
removeClasses(bubbleElement, 'unread-notification');
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -284,7 +283,7 @@ export const IFrameHelper = {
|
|||
target: chatBubble,
|
||||
});
|
||||
|
||||
addClass(closeBubble, closeBtnClassName);
|
||||
addClasses(closeBubble, closeBtnClassName);
|
||||
|
||||
chatIcon.style.background = widgetColor;
|
||||
closeBubble.style.background = widgetColor;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { addClass, removeClass, toggleClass, wootOn } from './DOMHelpers';
|
||||
import { addClasses, removeClasses, toggleClass } from './DOMHelpers';
|
||||
import { IFrameHelper } from './IFrameHelper';
|
||||
import { isExpandedView } from './settingsHelper';
|
||||
|
||||
|
@ -41,14 +41,14 @@ export const createBubbleIcon = ({ className, src, target }) => {
|
|||
|
||||
export const createBubbleHolder = hideMessageBubble => {
|
||||
if (hideMessageBubble) {
|
||||
addClass(bubbleHolder, 'woot-hidden');
|
||||
addClasses(bubbleHolder, 'woot-hidden');
|
||||
}
|
||||
addClass(bubbleHolder, 'woot--bubble-holder');
|
||||
addClasses(bubbleHolder, 'woot--bubble-holder');
|
||||
body.appendChild(bubbleHolder);
|
||||
};
|
||||
|
||||
export const createNotificationBubble = () => {
|
||||
addClass(notificationBubble, 'woot--notification');
|
||||
addClasses(notificationBubble, 'woot--notification');
|
||||
return notificationBubble;
|
||||
};
|
||||
|
||||
|
@ -71,15 +71,15 @@ export const onBubbleClick = (props = {}) => {
|
|||
};
|
||||
|
||||
export const onClickChatBubble = () => {
|
||||
wootOn(bubbleHolder, 'click', onBubbleClick);
|
||||
bubbleHolder.addEventListener('click', onBubbleClick);
|
||||
};
|
||||
|
||||
export const addUnreadClass = () => {
|
||||
const holderEl = document.querySelector('.woot-widget-holder');
|
||||
addClass(holderEl, 'has-unread-view');
|
||||
addClasses(holderEl, 'has-unread-view');
|
||||
};
|
||||
|
||||
export const removeUnreadClass = () => {
|
||||
const holderEl = document.querySelector('.woot-widget-holder');
|
||||
removeClass(holderEl, 'has-unread-view');
|
||||
removeClasses(holderEl, 'has-unread-view');
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { CAMPAIGN_TYPES } from '../constants/campaign';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
campaignType() {
|
||||
const pageURL = window.location.href;
|
||||
const type = pageURL.substr(pageURL.lastIndexOf('/') + 1);
|
||||
return type;
|
||||
return pageURL.substring(pageURL.lastIndexOf('/') + 1);
|
||||
},
|
||||
isOngoingType() {
|
||||
return this.campaignType === CAMPAIGN_TYPES.ONGOING;
|
||||
|
|
|
@ -93,7 +93,7 @@ export default {
|
|||
computed: {
|
||||
surveyId() {
|
||||
const pageURL = window.location.href;
|
||||
return pageURL.substr(pageURL.lastIndexOf('/') + 1);
|
||||
return pageURL.substring(pageURL.lastIndexOf('/') + 1);
|
||||
},
|
||||
isRatingSubmitted() {
|
||||
return this.surveyDetails && this.surveyDetails.rating;
|
||||
|
|
|
@ -40,7 +40,10 @@ class WorkingHour < ApplicationRecord
|
|||
validate :open_all_day_and_closed_all_day
|
||||
|
||||
def self.today
|
||||
find_by(day_of_week: Date.current.wday)
|
||||
# While getting the day of the week, consider the timezone as well. `first` would
|
||||
# return the first working hour from the list of working hours available per week.
|
||||
inbox = first.inbox
|
||||
find_by(day_of_week: Time.zone.now.in_time_zone(inbox.timezone).to_date.wday)
|
||||
end
|
||||
|
||||
def open_at?(time)
|
||||
|
|
|
@ -88,4 +88,18 @@ RSpec.describe WorkingHour do
|
|||
'Validation failed: open_all_day and closed_all_day cannot be true at the same time')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on monday 9am in Sydney timezone' do
|
||||
let(:inbox) { create(:inbox) }
|
||||
|
||||
before do
|
||||
Time.zone = 'Australia/Sydney'
|
||||
inbox.update(timezone: 'Australia/Sydney')
|
||||
travel_to '10.10.2022 9:00 AEDT'
|
||||
end
|
||||
|
||||
it 'is considered working hour' do
|
||||
expect(described_class.today.open_now?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -167,6 +167,7 @@ describe ::Contacts::FilterService do
|
|||
|
||||
context 'with x_days_before filter' do
|
||||
before do
|
||||
Time.zone = 'UTC'
|
||||
el_contact.update(last_activity_at: (Time.zone.today - 4.days))
|
||||
cs_contact.update(last_activity_at: (Time.zone.today - 5.days))
|
||||
en_contact.update(last_activity_at: (Time.zone.today - 2.days))
|
||||
|
|
|
@ -309,6 +309,7 @@ describe ::Conversations::FilterService do
|
|||
|
||||
context 'with x_days_before filter' do
|
||||
before do
|
||||
Time.zone = 'UTC'
|
||||
en_conversation_1.update!(last_activity_at: (Time.zone.today - 4.days))
|
||||
en_conversation_2.update!(last_activity_at: (Time.zone.today - 5.days))
|
||||
user_2_assigned_conversation.update!(last_activity_at: (Time.zone.today - 2.days))
|
||||
|
|
Loading…
Reference in a new issue