Merge branch 'develop' into feat/new-auth-screens
This commit is contained in:
commit
cf719282aa
23 changed files with 698 additions and 34 deletions
5
Gemfile
5
Gemfile
|
@ -156,11 +156,6 @@ group :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# TODO: is this needed ?
|
|
||||||
# errors thrown by devise password gem
|
|
||||||
gem 'flay'
|
|
||||||
gem 'rspec'
|
|
||||||
# for error thrown by devise password gem
|
|
||||||
gem 'active_record_query_trace'
|
gem 'active_record_query_trace'
|
||||||
gem 'bundle-audit', require: false
|
gem 'bundle-audit', require: false
|
||||||
gem 'byebug', platform: :mri
|
gem 'byebug', platform: :mri
|
||||||
|
|
17
Gemfile.lock
17
Gemfile.lock
|
@ -1,6 +1,6 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/chatwoot/devise-secure_password
|
remote: https://github.com/chatwoot/devise-secure_password
|
||||||
revision: de11e8765654b8242d42101ee9c8ffc8126f7975
|
revision: d777b04f12652d576b1272b8f39857e3e0b3fc26
|
||||||
specs:
|
specs:
|
||||||
devise-secure_password (2.0.1)
|
devise-secure_password (2.0.1)
|
||||||
devise (>= 4.0.0, < 5.0.0)
|
devise (>= 4.0.0, < 5.0.0)
|
||||||
|
@ -182,7 +182,6 @@ GEM
|
||||||
regexp_parser (~> 2.2)
|
regexp_parser (~> 2.2)
|
||||||
email_reply_trimmer (0.1.13)
|
email_reply_trimmer (0.1.13)
|
||||||
erubi (1.10.0)
|
erubi (1.10.0)
|
||||||
erubis (2.7.0)
|
|
||||||
et-orbi (1.2.7)
|
et-orbi (1.2.7)
|
||||||
tzinfo
|
tzinfo
|
||||||
execjs (2.8.1)
|
execjs (2.8.1)
|
||||||
|
@ -204,11 +203,6 @@ GEM
|
||||||
faraday (~> 1)
|
faraday (~> 1)
|
||||||
ffi (1.15.5)
|
ffi (1.15.5)
|
||||||
flag_shih_tzu (0.3.23)
|
flag_shih_tzu (0.3.23)
|
||||||
flay (2.12.1)
|
|
||||||
erubis (~> 2.7.0)
|
|
||||||
path_expander (~> 1.0)
|
|
||||||
ruby_parser (~> 3.0)
|
|
||||||
sexp_processor (~> 4.0)
|
|
||||||
foreman (0.87.2)
|
foreman (0.87.2)
|
||||||
fugit (1.5.3)
|
fugit (1.5.3)
|
||||||
et-orbi (~> 1, >= 1.2.7)
|
et-orbi (~> 1, >= 1.2.7)
|
||||||
|
@ -393,7 +387,6 @@ GEM
|
||||||
parallel (1.21.0)
|
parallel (1.21.0)
|
||||||
parser (3.1.1.0)
|
parser (3.1.1.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
path_expander (1.1.0)
|
|
||||||
pg (1.3.2)
|
pg (1.3.2)
|
||||||
procore-sift (0.16.0)
|
procore-sift (0.16.0)
|
||||||
rails (> 4.2.0)
|
rails (> 4.2.0)
|
||||||
|
@ -468,10 +461,6 @@ GEM
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
rspec (3.11.0)
|
|
||||||
rspec-core (~> 3.11.0)
|
|
||||||
rspec-expectations (~> 3.11.0)
|
|
||||||
rspec-mocks (~> 3.11.0)
|
|
||||||
rspec-core (3.11.0)
|
rspec-core (3.11.0)
|
||||||
rspec-support (~> 3.11.0)
|
rspec-support (~> 3.11.0)
|
||||||
rspec-expectations (3.11.0)
|
rspec-expectations (3.11.0)
|
||||||
|
@ -681,7 +670,6 @@ DEPENDENCIES
|
||||||
faker
|
faker
|
||||||
fcm
|
fcm
|
||||||
flag_shih_tzu
|
flag_shih_tzu
|
||||||
flay
|
|
||||||
foreman
|
foreman
|
||||||
geocoder
|
geocoder
|
||||||
google-cloud-dialogflow
|
google-cloud-dialogflow
|
||||||
|
@ -718,7 +706,6 @@ DEPENDENCIES
|
||||||
redis-namespace
|
redis-namespace
|
||||||
responders
|
responders
|
||||||
rest-client
|
rest-client
|
||||||
rspec
|
|
||||||
rspec-rails (~> 5.0.0)
|
rspec-rails (~> 5.0.0)
|
||||||
rubocop
|
rubocop
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
|
@ -755,4 +742,4 @@ RUBY VERSION
|
||||||
ruby 3.0.4p208
|
ruby 3.0.4p208
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.10
|
2.3.14
|
||||||
|
|
9
app/javascript/dashboard/api/bulkActions.js
Normal file
9
app/javascript/dashboard/api/bulkActions.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class BulkActionsAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('bulk_actions', { accountScoped: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BulkActionsAPI();
|
9
app/javascript/dashboard/api/specs/bulkAction.spec.js
Normal file
9
app/javascript/dashboard/api/specs/bulkAction.spec.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import bulkActions from '../bulkActions';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
describe('#BulkActionsAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(bulkActions).toBeInstanceOf(ApiClient);
|
||||||
|
expect(bulkActions).toHaveProperty('create');
|
||||||
|
});
|
||||||
|
});
|
|
@ -96,6 +96,9 @@
|
||||||
:chat="chat"
|
:chat="chat"
|
||||||
:conversation-type="conversationType"
|
:conversation-type="conversationType"
|
||||||
:show-assignee="showAssigneeInConversationCard"
|
:show-assignee="showAssigneeInConversationCard"
|
||||||
|
:selected="isConversationSelected(chat.id)"
|
||||||
|
@select-conversation="selectConversation"
|
||||||
|
@de-select-conversation="deSelectConversation"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="chatListLoading" class="text-center">
|
<div v-if="chatListLoading" class="text-center">
|
||||||
|
@ -134,6 +137,16 @@
|
||||||
@applyFilter="onApplyFilter"
|
@applyFilter="onApplyFilter"
|
||||||
/>
|
/>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
|
<conversation-bulk-actions
|
||||||
|
v-if="selectedConversations.length"
|
||||||
|
:conversations="selectedConversations"
|
||||||
|
:all-conversations-selected="allConversationsSelected"
|
||||||
|
:selected-inboxes="uniqueInboxes"
|
||||||
|
@select-all-conversations="selectAllConversations"
|
||||||
|
@assign-agent="onAssignAgent"
|
||||||
|
@resolve-conversations="onResolveConversations"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -152,6 +165,8 @@ import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
||||||
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
||||||
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
|
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
|
||||||
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
||||||
|
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Actions.vue';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndJKey,
|
hasPressedAltAndJKey,
|
||||||
|
@ -166,8 +181,9 @@ export default {
|
||||||
ChatFilter,
|
ChatFilter,
|
||||||
ConversationAdvancedFilter,
|
ConversationAdvancedFilter,
|
||||||
DeleteCustomViews,
|
DeleteCustomViews,
|
||||||
|
ConversationBulkActions,
|
||||||
},
|
},
|
||||||
mixins: [timeMixin, conversationMixin, eventListenerMixins],
|
mixins: [timeMixin, conversationMixin, eventListenerMixins, alertMixin],
|
||||||
props: {
|
props: {
|
||||||
conversationInbox: {
|
conversationInbox: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
|
@ -202,6 +218,8 @@ export default {
|
||||||
foldersQuery: {},
|
foldersQuery: {},
|
||||||
showAddFoldersModal: false,
|
showAddFoldersModal: false,
|
||||||
showDeleteFoldersModal: false,
|
showDeleteFoldersModal: false,
|
||||||
|
selectedConversations: [],
|
||||||
|
selectedInboxes: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -217,6 +235,7 @@ export default {
|
||||||
conversationStats: 'conversationStats/getStats',
|
conversationStats: 'conversationStats/getStats',
|
||||||
appliedFilters: 'getAppliedConversationFilters',
|
appliedFilters: 'getAppliedConversationFilters',
|
||||||
folders: 'customViews/getCustomViews',
|
folders: 'customViews/getCustomViews',
|
||||||
|
inboxes: 'inboxes/getInboxes',
|
||||||
}),
|
}),
|
||||||
hasAppliedFilters() {
|
hasAppliedFilters() {
|
||||||
return this.appliedFilters.length !== 0;
|
return this.appliedFilters.length !== 0;
|
||||||
|
@ -343,6 +362,15 @@ export default {
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
allConversationsSelected() {
|
||||||
|
return (
|
||||||
|
JSON.stringify(this.selectedConversations) ===
|
||||||
|
JSON.stringify(this.conversationList.map(item => item.id))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
uniqueInboxes() {
|
||||||
|
return [...new Set(this.selectedInboxes)];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
activeTeam() {
|
activeTeam() {
|
||||||
|
@ -376,6 +404,7 @@ export default {
|
||||||
if (this.$route.name !== 'home') {
|
if (this.$route.name !== 'home') {
|
||||||
this.$router.push({ name: 'home' });
|
this.$router.push({ name: 'home' });
|
||||||
}
|
}
|
||||||
|
this.resetBulkActions();
|
||||||
this.foldersQuery = filterQueryGenerator(payload);
|
this.foldersQuery = filterQueryGenerator(payload);
|
||||||
this.$store.dispatch('conversationPage/reset');
|
this.$store.dispatch('conversationPage/reset');
|
||||||
this.$store.dispatch('emptyAllConversations');
|
this.$store.dispatch('emptyAllConversations');
|
||||||
|
@ -441,6 +470,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetAndFetchData() {
|
resetAndFetchData() {
|
||||||
|
this.resetBulkActions();
|
||||||
this.$store.dispatch('conversationPage/reset');
|
this.$store.dispatch('conversationPage/reset');
|
||||||
this.$store.dispatch('emptyAllConversations');
|
this.$store.dispatch('emptyAllConversations');
|
||||||
this.$store.dispatch('clearConversationFilters');
|
this.$store.dispatch('clearConversationFilters');
|
||||||
|
@ -491,6 +521,7 @@ export default {
|
||||||
},
|
},
|
||||||
updateAssigneeTab(selectedTab) {
|
updateAssigneeTab(selectedTab) {
|
||||||
if (this.activeAssigneeTab !== selectedTab) {
|
if (this.activeAssigneeTab !== selectedTab) {
|
||||||
|
this.resetBulkActions();
|
||||||
bus.$emit('clearSearchInput');
|
bus.$emit('clearSearchInput');
|
||||||
this.activeAssigneeTab = selectedTab;
|
this.activeAssigneeTab = selectedTab;
|
||||||
if (!this.currentPage) {
|
if (!this.currentPage) {
|
||||||
|
@ -498,6 +529,10 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetBulkActions() {
|
||||||
|
this.selectedConversations = [];
|
||||||
|
this.selectedInboxes = [];
|
||||||
|
},
|
||||||
updateStatusType(index) {
|
updateStatusType(index) {
|
||||||
if (this.activeStatus !== index) {
|
if (this.activeStatus !== index) {
|
||||||
this.activeStatus = index;
|
this.activeStatus = index;
|
||||||
|
@ -520,6 +555,59 @@ export default {
|
||||||
this.fetchConversations();
|
this.fetchConversations();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isConversationSelected(id) {
|
||||||
|
return this.selectedConversations.includes(id);
|
||||||
|
},
|
||||||
|
selectConversation(conversationId, inboxId) {
|
||||||
|
this.selectedConversations.push(conversationId);
|
||||||
|
this.selectedInboxes.push(inboxId);
|
||||||
|
},
|
||||||
|
deSelectConversation(conversationId, inboxId) {
|
||||||
|
this.selectedConversations = this.selectedConversations.filter(
|
||||||
|
item => item !== conversationId
|
||||||
|
);
|
||||||
|
this.selectedInboxes = this.selectedInboxes.filter(
|
||||||
|
item => item !== inboxId
|
||||||
|
);
|
||||||
|
},
|
||||||
|
selectAllConversations(check) {
|
||||||
|
if (check) {
|
||||||
|
this.selectedConversations = this.conversationList.map(item => item.id);
|
||||||
|
this.selectedInboxes = this.conversationList.map(item => item.inbox_id);
|
||||||
|
} else {
|
||||||
|
this.resetBulkActions();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onAssignAgent(agent) {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('bulkActions/process', {
|
||||||
|
type: 'Conversation',
|
||||||
|
ids: this.selectedConversations,
|
||||||
|
fields: {
|
||||||
|
assignee_id: agent.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.selectedConversations = [];
|
||||||
|
this.showAlert(this.$t('BULK_ACTION.ASSIGN_SUCCESFUL'));
|
||||||
|
} catch (err) {
|
||||||
|
this.showAlert(this.$t('BULK_ACTION.ASSIGN_FAILED'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onResolveConversations() {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('bulkActions/process', {
|
||||||
|
type: 'Conversation',
|
||||||
|
ids: this.selectedConversations,
|
||||||
|
fields: {
|
||||||
|
status: 'resolved',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.selectedConversations = [];
|
||||||
|
this.showAlert(this.$t('BULK_ACTION.RESOLVE_SUCCESFUL'));
|
||||||
|
} catch (error) {
|
||||||
|
this.showAlert(this.$t('BULK_ACTION.RESOLVE_FAILED'));
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -535,7 +623,7 @@ export default {
|
||||||
.conversations-list-wrap {
|
.conversations-list-wrap {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 34rem;
|
width: 34rem;
|
||||||
|
overflow: hidden;
|
||||||
@include breakpoint(large up) {
|
@include breakpoint(large up) {
|
||||||
width: 36rem;
|
width: 36rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
'currentChat.inbox_id'(inboxId) {
|
'currentChat.inbox_id'(inboxId) {
|
||||||
if (inboxId) {
|
if (inboxId) {
|
||||||
this.$store.dispatch('inboxAssignableAgents/fetch', { inboxId });
|
this.$store.dispatch('inboxAssignableAgents/fetch', [inboxId]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'currentChat.id'() {
|
'currentChat.id'() {
|
||||||
|
|
|
@ -5,11 +5,24 @@
|
||||||
active: isActiveChat,
|
active: isActiveChat,
|
||||||
'unread-chat': hasUnread,
|
'unread-chat': hasUnread,
|
||||||
'has-inbox-name': showInboxName,
|
'has-inbox-name': showInboxName,
|
||||||
|
'conversation-selected': selected,
|
||||||
}"
|
}"
|
||||||
|
@mouseenter="onCardHover"
|
||||||
|
@mouseleave="onCardLeave"
|
||||||
@click="cardClick(chat)"
|
@click="cardClick(chat)"
|
||||||
>
|
>
|
||||||
|
<label v-if="hovered || selected" class="checkbox-wrapper">
|
||||||
|
<input
|
||||||
|
:value="selected"
|
||||||
|
:checked="selected"
|
||||||
|
class="checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
@change="onSelectConversation($event.target.checked)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<thumbnail
|
<thumbnail
|
||||||
v-if="!hideThumbnail"
|
v-if="bulkActionCheck"
|
||||||
:src="currentContact.thumbnail"
|
:src="currentContact.thumbnail"
|
||||||
:badge="inboxBadge"
|
:badge="inboxBadge"
|
||||||
class="columns"
|
class="columns"
|
||||||
|
@ -142,8 +155,16 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
selected: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hovered: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
|
@ -152,7 +173,9 @@ export default {
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
}),
|
}),
|
||||||
|
bulkActionCheck() {
|
||||||
|
return !this.hideThumbnail && !this.hovered && !this.selected;
|
||||||
|
},
|
||||||
chatMetadata() {
|
chatMetadata() {
|
||||||
return this.chat.meta || {};
|
return this.chat.meta || {};
|
||||||
},
|
},
|
||||||
|
@ -260,6 +283,16 @@ export default {
|
||||||
}
|
}
|
||||||
router.push({ path: frontendURL(path) });
|
router.push({ path: frontendURL(path) });
|
||||||
},
|
},
|
||||||
|
onCardHover() {
|
||||||
|
this.hovered = !this.hideThumbnail;
|
||||||
|
},
|
||||||
|
onCardLeave() {
|
||||||
|
this.hovered = false;
|
||||||
|
},
|
||||||
|
onSelectConversation(checked) {
|
||||||
|
const action = checked ? 'select-conversation' : 'de-select-conversation';
|
||||||
|
this.$emit(action, this.chat.id, this.inbox.id);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -272,6 +305,10 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversation-selected {
|
||||||
|
background: var(--color-background-light);
|
||||||
|
}
|
||||||
|
|
||||||
.has-inbox-name {
|
.has-inbox-name {
|
||||||
&::v-deep .user-thumbnail-box {
|
&::v-deep .user-thumbnail-box {
|
||||||
margin-top: var(--space-normal);
|
margin-top: var(--space-normal);
|
||||||
|
@ -320,4 +357,22 @@ export default {
|
||||||
margin-top: var(--space-minus-micro);
|
margin-top: var(--space-minus-micro);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
.checkbox-wrapper {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-top: var(--space-normal);
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--w-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
margin: var(--space-zero);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
<template>
|
||||||
|
<div class="bulk-action__container">
|
||||||
|
<div class="flex-between">
|
||||||
|
<label class="bulk-action__panel flex-between">
|
||||||
|
<input
|
||||||
|
ref="selectAllCheck"
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox"
|
||||||
|
:checked="allConversationsSelected"
|
||||||
|
@change="selectAll($event)"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
$t('BULK_ACTION.CONVERSATIONS_SELECTED', {
|
||||||
|
conversationCount: conversations.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div class="bulk-action__actions flex-between">
|
||||||
|
<woot-button
|
||||||
|
v-tooltip="$t('BULK_ACTION.RESOLVE_TOOLTIP')"
|
||||||
|
size="tiny"
|
||||||
|
variant="flat"
|
||||||
|
color-scheme="success"
|
||||||
|
icon="checkmark"
|
||||||
|
class="margin-right-smaller"
|
||||||
|
@click="resolveConversations"
|
||||||
|
/>
|
||||||
|
<woot-button
|
||||||
|
v-tooltip="$t('BULK_ACTION.ASSIGN_AGENT_TOOLTIP')"
|
||||||
|
size="tiny"
|
||||||
|
variant="flat"
|
||||||
|
color-scheme="secondary"
|
||||||
|
icon="person-assign"
|
||||||
|
@click="showAgentsList = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<transition name="menu-slide">
|
||||||
|
<agent-selector
|
||||||
|
v-if="showAgentsList"
|
||||||
|
:selected-inboxes="selectedInboxes"
|
||||||
|
:conversation-count="conversations.length"
|
||||||
|
@select="submit"
|
||||||
|
@close="showAgentsList = false"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<div v-if="allConversationsSelected" class="bulk-action__alert">
|
||||||
|
{{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AgentSelector from './AgentSelector.vue';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
AgentSelector,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
conversations: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
allConversationsSelected: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
selectedInboxes: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAgentsList: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$refs.selectAllCheck.indeterminate = true;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectAll(e) {
|
||||||
|
this.$emit('select-all-conversations', e.target.checked);
|
||||||
|
},
|
||||||
|
submit(agent) {
|
||||||
|
this.$emit('assign-agent', agent);
|
||||||
|
},
|
||||||
|
resolveConversations() {
|
||||||
|
this.$emit('resolve-conversations');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.flex-between {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action__container {
|
||||||
|
background-color: var(--s-50);
|
||||||
|
border-top: 1px solid var(--s-100);
|
||||||
|
box-shadow: var(--shadow-bulk-action-container);
|
||||||
|
padding: var(--space-normal) var(--space-one);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action__panel {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-left: var(--space-smaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
cursor: pointer;
|
||||||
|
margin: var(--space-zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action__alert {
|
||||||
|
background-color: var(--y-50);
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
border: 1px solid var(--y-300);
|
||||||
|
color: var(--y-700);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-top: var(--space-small);
|
||||||
|
padding: var(--space-half) var(--space-one);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,249 @@
|
||||||
|
<template>
|
||||||
|
<div class="bulk-action__agents">
|
||||||
|
<div class="header flex-between">
|
||||||
|
<span>{{ $t('BULK_ACTION.AGENT_SELECT_LABEL') }}</span>
|
||||||
|
<woot-button
|
||||||
|
size="tiny"
|
||||||
|
variant="clear"
|
||||||
|
color-scheme="secondary"
|
||||||
|
icon="dismiss"
|
||||||
|
@click="onClose"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div v-if="uiFlags.isUpdating" class="agent__list-loading">
|
||||||
|
<spinner />
|
||||||
|
<p>{{ $t('BULK_ACTION.AGENT_LIST_LOADING') }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="agent__list-container">
|
||||||
|
<ul v-if="!selectedAgent">
|
||||||
|
<li class="search-container">
|
||||||
|
<div class="agent-list-search flex-between">
|
||||||
|
<fluent-icon icon="search" class="search-icon" size="16" />
|
||||||
|
<input
|
||||||
|
ref="search"
|
||||||
|
v-model="query"
|
||||||
|
type="search"
|
||||||
|
placeholder="Search"
|
||||||
|
class="agent--search_input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li v-for="agent in filteredAgents" :key="agent.id">
|
||||||
|
<div class="agent-list-item" @click="assignAgent(agent)">
|
||||||
|
<thumbnail
|
||||||
|
src="agent.thumbnail"
|
||||||
|
:username="agent.name"
|
||||||
|
size="22px"
|
||||||
|
class="margin-right-small"
|
||||||
|
/>
|
||||||
|
<span class="reports-option__title">{{ agent.name }}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else class="agent-confirmation-container">
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
$t('BULK_ACTION.ASSIGN_CONFIRMATION_LABEL', {
|
||||||
|
conversationCount,
|
||||||
|
conversationLabel,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<strong>
|
||||||
|
{{ selectedAgent.name }}
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
<div class="agent-confirmation-actions">
|
||||||
|
<woot-button
|
||||||
|
color-scheme="primary"
|
||||||
|
variant="smooth"
|
||||||
|
@click="goBack"
|
||||||
|
>
|
||||||
|
{{ $t('BULK_ACTION.GO_BACK_LABEL') }}
|
||||||
|
</woot-button>
|
||||||
|
<woot-button
|
||||||
|
color-scheme="primary"
|
||||||
|
variant="flat"
|
||||||
|
:is-loading="uiFlags.isUpdating"
|
||||||
|
@click="submit"
|
||||||
|
>
|
||||||
|
{{ $t('BULK_ACTION.ASSIGN_LABEL') }}
|
||||||
|
</woot-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
|
import Spinner from 'shared/components/Spinner';
|
||||||
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Thumbnail,
|
||||||
|
Spinner,
|
||||||
|
},
|
||||||
|
mixins: [clickaway],
|
||||||
|
props: {
|
||||||
|
selectedInboxes: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
conversationCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
query: '',
|
||||||
|
selectedAgent: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
uiFlags: 'bulkActions/getUIFlags',
|
||||||
|
inboxes: 'inboxes/getInboxes',
|
||||||
|
assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags',
|
||||||
|
}),
|
||||||
|
filteredAgents() {
|
||||||
|
if (this.query) {
|
||||||
|
return this.assignableAgents.filter(agent =>
|
||||||
|
agent.name.toLowerCase().includes(this.query.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.assignableAgents;
|
||||||
|
},
|
||||||
|
assignableAgents() {
|
||||||
|
return this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
||||||
|
this.selectedInboxes.join(',')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
conversationLabel() {
|
||||||
|
return this.conversationCount > 1 ? 'conversations' : 'conversation';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('inboxAssignableAgents/fetch', this.selectedInboxes);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.$emit('select', this.selectedAgent);
|
||||||
|
},
|
||||||
|
goBack() {
|
||||||
|
this.selectedAgent = null;
|
||||||
|
},
|
||||||
|
assignAgent(agent) {
|
||||||
|
this.selectedAgent = agent;
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.flex-between {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.bulk-action__agents {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 40px;
|
||||||
|
right: var(--space-small);
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: var(--shadow-dropdown-pane);
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
border: 1px solid var(--s-50);
|
||||||
|
background-color: var(--white);
|
||||||
|
width: 75%;
|
||||||
|
.header {
|
||||||
|
padding: var(--space-one);
|
||||||
|
span {
|
||||||
|
font-size: var(--font-size-default);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
.agent__list-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.agent-list-search {
|
||||||
|
padding: 0 var(--space-one);
|
||||||
|
border: 1px solid var(--s-100);
|
||||||
|
border-radius: var(--border-radius-medium);
|
||||||
|
background-color: var(--s-50);
|
||||||
|
.search-icon {
|
||||||
|
color: var(--s-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent--search_input {
|
||||||
|
border: 0;
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--space-one);
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--s-50);
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-confirmation-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: var(--space-one);
|
||||||
|
p {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.agent-confirmation-actions {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: var(--space-one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.search-container {
|
||||||
|
padding: 0 var(--space-one);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: var(--z-index-twenty);
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent__list-loading {
|
||||||
|
height: calc(95% - var(--space-one));
|
||||||
|
margin: var(--space-one);
|
||||||
|
border-radius: var(--border-radius-medium);
|
||||||
|
background-color: var(--s-50);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
17
app/javascript/dashboard/i18n/locale/en/bulkActions.json
Normal file
17
app/javascript/dashboard/i18n/locale/en/bulkActions.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"BULK_ACTION": {
|
||||||
|
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
||||||
|
"AGENT_SELECT_LABEL": "Select Agent",
|
||||||
|
"ASSIGN_CONFIRMATION_LABEL": "Are you sure you want to assign %{conversationCount} %{conversationLabel} to",
|
||||||
|
"GO_BACK_LABEL": "Go back",
|
||||||
|
"ASSIGN_LABEL": "Assign",
|
||||||
|
"ASSIGN_AGENT_TOOLTIP": "Assign Agent",
|
||||||
|
"RESOLVE_TOOLTIP": "Resolve",
|
||||||
|
"ASSIGN_SUCCESFUL": "Conversations assigned successfully",
|
||||||
|
"ASSIGN_FAILED": "Failed to assign conversations, please try again",
|
||||||
|
"RESOLVE_SUCCESFUL": "Conversations resolved successfully",
|
||||||
|
"RESOLVE_FAILED": "Failed to resolve conversations, please try again",
|
||||||
|
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||||
|
"AGENT_LIST_LOADING": "Loading Agents"
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import { default as _setNewPassword } from './setNewPassword.json';
|
||||||
import { default as _settings } from './settings.json';
|
import { default as _settings } from './settings.json';
|
||||||
import { default as _signup } from './signup.json';
|
import { default as _signup } from './signup.json';
|
||||||
import { default as _teamsSettings } from './teamsSettings.json';
|
import { default as _teamsSettings } from './teamsSettings.json';
|
||||||
|
import { default as _bulkActions } from './bulkActions.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
..._advancedFilters,
|
..._advancedFilters,
|
||||||
|
@ -46,4 +47,5 @@ export default {
|
||||||
..._settings,
|
..._settings,
|
||||||
..._signup,
|
..._signup,
|
||||||
..._teamsSettings,
|
..._teamsSettings,
|
||||||
|
..._bulkActions,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ import agents from './modules/agents';
|
||||||
import attributes from './modules/attributes';
|
import attributes from './modules/attributes';
|
||||||
import auth from './modules/auth';
|
import auth from './modules/auth';
|
||||||
import automations from './modules/automations';
|
import automations from './modules/automations';
|
||||||
|
import bulkActions from './modules/bulkActions';
|
||||||
import campaigns from './modules/campaigns';
|
import campaigns from './modules/campaigns';
|
||||||
import cannedResponse from './modules/cannedResponse';
|
import cannedResponse from './modules/cannedResponse';
|
||||||
import contactConversations from './modules/contactConversations';
|
import contactConversations from './modules/contactConversations';
|
||||||
|
@ -43,6 +44,7 @@ export default new Vuex.Store({
|
||||||
attributes,
|
attributes,
|
||||||
auth,
|
auth,
|
||||||
automations,
|
automations,
|
||||||
|
bulkActions,
|
||||||
campaigns,
|
campaigns,
|
||||||
cannedResponse,
|
cannedResponse,
|
||||||
contactConversations,
|
contactConversations,
|
||||||
|
|
44
app/javascript/dashboard/store/modules/bulkActions.js
Normal file
44
app/javascript/dashboard/store/modules/bulkActions.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import types from '../mutation-types';
|
||||||
|
import BulkActionsAPI from '../../api/bulkActions';
|
||||||
|
|
||||||
|
export const state = {
|
||||||
|
uiFlags: {
|
||||||
|
isUpdating: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getters = {
|
||||||
|
getUIFlags(_state) {
|
||||||
|
return _state.uiFlags;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
process: async function processAction({ commit }, payload) {
|
||||||
|
commit(types.SET_BULK_ACTIONS_FLAG, { isUpdating: true });
|
||||||
|
try {
|
||||||
|
await BulkActionsAPI.create(payload);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
} finally {
|
||||||
|
commit(types.SET_BULK_ACTIONS_FLAG, { isUpdating: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
[types.SET_BULK_ACTIONS_FLAG](_state, data) {
|
||||||
|
_state.uiFlags = {
|
||||||
|
..._state.uiFlags,
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
actions,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
};
|
|
@ -26,13 +26,16 @@ export const getters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
async fetch({ commit }, { inboxId }) {
|
async fetch({ commit }, inboxIds) {
|
||||||
commit(types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true });
|
commit(types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true });
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { payload },
|
data: { payload },
|
||||||
} = await AssignableAgentsAPI.get([inboxId]);
|
} = await AssignableAgentsAPI.get(inboxIds);
|
||||||
commit(types.SET_INBOX_ASSIGNABLE_AGENTS, { inboxId, members: payload });
|
commit(types.SET_INBOX_ASSIGNABLE_AGENTS, {
|
||||||
|
inboxId: inboxIds.join(','),
|
||||||
|
members: payload,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { actions } from '../../bulkActions';
|
||||||
|
import * as types from '../../../mutation-types';
|
||||||
|
import payload from './fixtures';
|
||||||
|
const commit = jest.fn();
|
||||||
|
global.axios = axios;
|
||||||
|
jest.mock('axios');
|
||||||
|
|
||||||
|
describe('#actions', () => {
|
||||||
|
describe('#create', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({ data: payload });
|
||||||
|
await actions.process({ commit }, payload);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_BULK_ACTIONS_FLAG, { isUpdating: true }],
|
||||||
|
[types.default.SET_BULK_ACTIONS_FLAG, { isUpdating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.process({ commit })).rejects.toThrow(Error);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_BULK_ACTIONS_FLAG, { isUpdating: true }],
|
||||||
|
[types.default.SET_BULK_ACTIONS_FLAG, { isUpdating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
type: 'Conversation',
|
||||||
|
ids: [64, 39],
|
||||||
|
fields: { assignee_id: 6 },
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { getters } from '../../bulkActions';
|
||||||
|
|
||||||
|
describe('#getters', () => {
|
||||||
|
it('getUIFlags', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: {
|
||||||
|
isUpdating: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getUIFlags(state)).toEqual({
|
||||||
|
isUpdating: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import types from '../../../mutation-types';
|
||||||
|
import { mutations } from '../../bulkActions';
|
||||||
|
|
||||||
|
describe('#mutations', () => {
|
||||||
|
describe('#toggleUiFlag', () => {
|
||||||
|
it('set update flags', () => {
|
||||||
|
const state = { uiFlags: { isUpdating: false } };
|
||||||
|
mutations[types.SET_BULK_ACTIONS_FLAG](state, { isUpdating: true });
|
||||||
|
expect(state.uiFlags.isUpdating).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -12,12 +12,12 @@ describe('#actions', () => {
|
||||||
axios.get.mockResolvedValue({
|
axios.get.mockResolvedValue({
|
||||||
data: { payload: agentsData },
|
data: { payload: agentsData },
|
||||||
});
|
});
|
||||||
await actions.fetch({ commit }, { inboxId: 1 });
|
await actions.fetch({ commit }, [1]);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true }],
|
[types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true }],
|
||||||
[
|
[
|
||||||
types.SET_INBOX_ASSIGNABLE_AGENTS,
|
types.SET_INBOX_ASSIGNABLE_AGENTS,
|
||||||
{ inboxId: 1, members: agentsData },
|
{ inboxId: '1', members: agentsData },
|
||||||
],
|
],
|
||||||
[types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: false }],
|
[types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: false }],
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -211,6 +211,9 @@ export default {
|
||||||
ADD_CUSTOM_VIEW: 'ADD_CUSTOM_VIEW',
|
ADD_CUSTOM_VIEW: 'ADD_CUSTOM_VIEW',
|
||||||
DELETE_CUSTOM_VIEW: 'DELETE_CUSTOM_VIEW',
|
DELETE_CUSTOM_VIEW: 'DELETE_CUSTOM_VIEW',
|
||||||
|
|
||||||
|
// Bulk Actions
|
||||||
|
SET_BULK_ACTIONS_FLAG: 'SET_BULK_ACTIONS_FLAG',
|
||||||
|
|
||||||
// Dashboard Apps
|
// Dashboard Apps
|
||||||
SET_DASHBOARD_APPS_UI_FLAG: 'SET_DASHBOARD_APPS_UI_FLAG',
|
SET_DASHBOARD_APPS_UI_FLAG: 'SET_DASHBOARD_APPS_UI_FLAG',
|
||||||
SET_DASHBOARD_APPS: 'SET_DASHBOARD_APPS',
|
SET_DASHBOARD_APPS: 'SET_DASHBOARD_APPS',
|
||||||
|
|
|
@ -8,4 +8,9 @@
|
||||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
--shadow-larger: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
--shadow-larger: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||||
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
--shadow-dropdown-pane:
|
||||||
|
0 0.8rem 1.6rem rgb(50 50 93 / 8%),
|
||||||
|
0 0.4rem 1.2rem rgb(0 0 0 / 7%);
|
||||||
|
--shadow-bulk-action-container:
|
||||||
|
6px 3px 22px 9px rgb(181 181 181 / 25%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
"people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z",
|
"people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z",
|
||||||
"people-team-outline": "M14.754 10c.966 0 1.75.784 1.75 1.75v4.749a4.501 4.501 0 0 1-9.002 0V11.75c0-.966.783-1.75 1.75-1.75h5.502Zm0 1.5H9.252a.25.25 0 0 0-.25.25v4.749a3.001 3.001 0 0 0 6.002 0V11.75a.25.25 0 0 0-.25-.25ZM3.75 10h3.381a2.738 2.738 0 0 0-.618 1.5H3.75a.25.25 0 0 0-.25.25v3.249a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V11.75c0-.966.784-1.75 1.75-1.75Zm13.125 0h3.375c.966 0 1.75.784 1.75 1.75V15a4 4 0 0 1-5.03 3.866c.214-.448.369-.929.455-1.433A2.5 2.5 0 0 0 20.5 15v-3.25a.25.25 0 0 0-.25-.25h-2.757a2.738 2.738 0 0 0-.618-1.5ZM12 3a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm6.5 1a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-13 0a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm6.5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm6.5 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-13 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
|
"people-team-outline": "M14.754 10c.966 0 1.75.784 1.75 1.75v4.749a4.501 4.501 0 0 1-9.002 0V11.75c0-.966.783-1.75 1.75-1.75h5.502Zm0 1.5H9.252a.25.25 0 0 0-.25.25v4.749a3.001 3.001 0 0 0 6.002 0V11.75a.25.25 0 0 0-.25-.25ZM3.75 10h3.381a2.738 2.738 0 0 0-.618 1.5H3.75a.25.25 0 0 0-.25.25v3.249a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V11.75c0-.966.784-1.75 1.75-1.75Zm13.125 0h3.375c.966 0 1.75.784 1.75 1.75V15a4 4 0 0 1-5.03 3.866c.214-.448.369-.929.455-1.433A2.5 2.5 0 0 0 20.5 15v-3.25a.25.25 0 0 0-.25-.25h-2.757a2.738 2.738 0 0 0-.618-1.5ZM12 3a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm6.5 1a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-13 0a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm6.5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm6.5 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-13 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z",
|
||||||
"person-add-outline": "M17.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11zm-5.477 2a6.47 6.47 0 0 0-.709 1.5H4.253a.749.749 0 0 0-.75.75v.577c0 .535.192 1.053.54 1.46c1.253 1.469 3.22 2.214 5.957 2.214c.597 0 1.157-.035 1.68-.106c.246.495.553.954.912 1.367c-.795.16-1.66.24-2.592.24c-3.146 0-5.532-.906-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.578A2.249 2.249 0 0 1 4.253 14h7.77zm5.477 0l-.09.008a.5.5 0 0 0-.402.402L17 14.5V17h-2.496l-.09.008a.5.5 0 0 0-.402.402l-.008.09l.008.09a.5.5 0 0 0 .402.402l.09.008H17L17 20.5l.008.09a.5.5 0 0 0 .402.402l.09.008l.09-.008a.5.5 0 0 0 .402-.402L18 20.5V18h2.504l.09-.008a.5.5 0 0 0 .402-.402l.008-.09l-.008-.09a.5.5 0 0 0-.402-.402l-.09-.008H18L18 14.5l-.008-.09a.5.5 0 0 0-.402-.402L17.5 14zM10 2.005a5 5 0 1 1 0 10a5 5 0 0 1 0-10zm0 1.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7z",
|
"person-add-outline": "M17.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11zm-5.477 2a6.47 6.47 0 0 0-.709 1.5H4.253a.749.749 0 0 0-.75.75v.577c0 .535.192 1.053.54 1.46c1.253 1.469 3.22 2.214 5.957 2.214c.597 0 1.157-.035 1.68-.106c.246.495.553.954.912 1.367c-.795.16-1.66.24-2.592.24c-3.146 0-5.532-.906-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.578A2.249 2.249 0 0 1 4.253 14h7.77zm5.477 0l-.09.008a.5.5 0 0 0-.402.402L17 14.5V17h-2.496l-.09.008a.5.5 0 0 0-.402.402l-.008.09l.008.09a.5.5 0 0 0 .402.402l.09.008H17L17 20.5l.008.09a.5.5 0 0 0 .402.402l.09.008l.09-.008a.5.5 0 0 0 .402-.402L18 20.5V18h2.504l.09-.008a.5.5 0 0 0 .402-.402l.008-.09l-.008-.09a.5.5 0 0 0-.402-.402l-.09-.008H18L18 14.5l-.008-.09a.5.5 0 0 0-.402-.402L17.5 14zM10 2.005a5 5 0 1 1 0 10a5 5 0 0 1 0-10zm0 1.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7z",
|
||||||
|
"person-assign-outline": "M11.313 15.5a6.471 6.471 0 0 1 .709-1.5h-7.77a2.249 2.249 0 0 0-2.249 2.25v.577c0 .892.319 1.756.899 2.435c1.566 1.834 3.952 2.74 7.098 2.74c.931 0 1.796-.08 2.592-.24a6.51 6.51 0 0 1-.913-1.366c-.524.07-1.083.105-1.68.105c-2.737 0-4.703-.745-5.957-2.213a2.25 2.25 0 0 1-.539-1.461v-.578a.75.75 0 0 1 .75-.749h7.06ZM10 2.005a5 5 0 1 1 0 10a5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7ZM23 17.5a5.5 5.5 0 1 1-11 0a5.5 5.5 0 0 1 11 0Zm-4.647-2.853a.5.5 0 0 0-.707.707L19.293 17H15a.5.5 0 1 0 0 1h4.293l-1.647 1.647a.5.5 0 0 0 .707.707l2.5-2.5a.497.497 0 0 0 .147-.345V17.5a.498.498 0 0 0-.15-.357l-2.497-2.496Z",
|
||||||
"person-outline": "M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.575c0 .894-.32 1.76-.902 2.438-1.57 1.834-3.957 2.739-7.102 2.739-3.146 0-5.532-.905-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.577a2.249 2.249 0 0 1 2.249-2.25h11.501Zm0 1.5H6.253a.749.749 0 0 0-.75.749v.577c0 .536.192 1.054.54 1.461 1.253 1.468 3.219 2.214 5.957 2.214s4.706-.746 5.962-2.214a2.25 2.25 0 0 0 .541-1.463v-.575a.749.749 0 0 0-.749-.75ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z",
|
"person-outline": "M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.575c0 .894-.32 1.76-.902 2.438-1.57 1.834-3.957 2.739-7.102 2.739-3.146 0-5.532-.905-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.577a2.249 2.249 0 0 1 2.249-2.25h11.501Zm0 1.5H6.253a.749.749 0 0 0-.75.749v.577c0 .536.192 1.054.54 1.461 1.253 1.468 3.219 2.214 5.957 2.214s4.706-.746 5.962-2.214a2.25 2.25 0 0 0 .541-1.463v-.575a.749.749 0 0 0-.749-.75ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Z",
|
||||||
"person-account-outline": "M11 15c0-.35.06-.687.17-1H4.253a2.249 2.249 0 0 0-2.249 2.249v.578c0 .892.319 1.756.899 2.435 1.566 1.834 3.952 2.74 7.098 2.74.397 0 .783-.015 1.156-.044A2.998 2.998 0 0 1 11 21v-.535c-.321.024-.655.036-1 .036-2.738 0-4.704-.746-5.958-2.213a2.25 2.25 0 0 1-.539-1.462v-.577c0-.414.336-.75.75-.75H11V15ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7ZM12 15a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2v-6Zm2.5 1a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Zm0 3a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Z",
|
"person-account-outline": "M11 15c0-.35.06-.687.17-1H4.253a2.249 2.249 0 0 0-2.249 2.249v.578c0 .892.319 1.756.899 2.435 1.566 1.834 3.952 2.74 7.098 2.74.397 0 .783-.015 1.156-.044A2.998 2.998 0 0 1 11 21v-.535c-.321.024-.655.036-1 .036-2.738 0-4.704-.746-5.958-2.213a2.25 2.25 0 0 1-.539-1.462v-.577c0-.414.336-.75.75-.75H11V15ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7ZM12 15a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2v-6Zm2.5 1a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Zm0 3a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Z",
|
||||||
"power-outline": "M8.204 4.82a.75.75 0 0 1 .634 1.36A7.51 7.51 0 0 0 4.5 12.991c0 4.148 3.358 7.51 7.499 7.51s7.499-3.362 7.499-7.51a7.51 7.51 0 0 0-4.323-6.804.75.75 0 1 1 .637-1.358 9.01 9.01 0 0 1 5.186 8.162c0 4.976-4.029 9.01-9 9.01C7.029 22 3 17.966 3 12.99a9.01 9.01 0 0 1 5.204-8.17ZM12 2.496a.75.75 0 0 1 .743.648l.007.102v7.5a.75.75 0 0 1-1.493.102l-.007-.102v-7.5a.75.75 0 0 1 .75-.75Z",
|
"power-outline": "M8.204 4.82a.75.75 0 0 1 .634 1.36A7.51 7.51 0 0 0 4.5 12.991c0 4.148 3.358 7.51 7.499 7.51s7.499-3.362 7.499-7.51a7.51 7.51 0 0 0-4.323-6.804.75.75 0 1 1 .637-1.358 9.01 9.01 0 0 1 5.186 8.162c0 4.976-4.029 9.01-9 9.01C7.029 22 3 17.966 3 12.99a9.01 9.01 0 0 1 5.204-8.17ZM12 2.496a.75.75 0 0 1 .743.648l.007.102v7.5a.75.75 0 0 1-1.493.102l-.007-.102v-7.5a.75.75 0 0 1 .75-.75Z",
|
||||||
|
|
|
@ -179,7 +179,8 @@ function main() {
|
||||||
Chatwoot Installation (latest)
|
Chatwoot Installation (latest)
|
||||||
***************************************************************************
|
***************************************************************************
|
||||||
|
|
||||||
For more verbose logs, open up a second terminal and follow along using, `tail -f /var/log/chatwoot`.
|
For more verbose logs, open up a second terminal and follow along using,
|
||||||
|
'tail -f /var/log/chatwoot'.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -199,7 +200,7 @@ EOF
|
||||||
echo "***** Skipping Postgres and Redis installation. ****"
|
echo "***** Skipping Postgres and Redis installation. ****"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -en "\n➥ 1/9 Installing dependencies. This takes a while."
|
echo -en "\n➥ 1/9 Installing dependencies. This takes a while.\n"
|
||||||
install_dependencies &>> "${LOG_FILE}"
|
install_dependencies &>> "${LOG_FILE}"
|
||||||
|
|
||||||
if [ "$install_pg_redis" != "no" ]
|
if [ "$install_pg_redis" != "no" ]
|
||||||
|
@ -273,7 +274,7 @@ The database migrations had not run as Postgres and Redis were not installed
|
||||||
as part of the installation process. After modifying the environment
|
as part of the installation process. After modifying the environment
|
||||||
variables (in the .env file) with your external database credentials, run
|
variables (in the .env file) with your external database credentials, run
|
||||||
the database migrations using the below command.
|
the database migrations using the below command.
|
||||||
`RAILS_ENV=production bundle exec rails db:chatwoot_prepare`.
|
'RAILS_ENV=production bundle exec rails db:chatwoot_prepare'.
|
||||||
***************************************************************************
|
***************************************************************************
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Reference in a new issue