feat: Add the option for changing the contact/conversation sidebar items order (#3362)

This commit is contained in:
Muhsin Keloth
2021-11-13 12:32:53 +05:30
committed by GitHub
parent f68a4b55bb
commit d1a62fe6ab
7 changed files with 450 additions and 243 deletions

View File

@@ -1,7 +1,9 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import uiSettingsMixin from '../uiSettings';
import uiSettingsMixin, {
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
} from '../uiSettings';
import Vuex from 'vuex';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -17,6 +19,8 @@ describe('uiSettingsMixin', () => {
display_rich_content_editor: false,
enter_to_send_enabled: false,
is_ct_labels_open: true,
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
}),
};
store = new Vuex.Store({ actions, getters });
@@ -33,6 +37,8 @@ describe('uiSettingsMixin', () => {
display_rich_content_editor: false,
enter_to_send_enabled: false,
is_ct_labels_open: true,
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
});
});
@@ -52,6 +58,8 @@ describe('uiSettingsMixin', () => {
display_rich_content_editor: false,
enter_to_send_enabled: true,
is_ct_labels_open: true,
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
},
},
undefined
@@ -75,6 +83,8 @@ describe('uiSettingsMixin', () => {
display_rich_content_editor: false,
enter_to_send_enabled: false,
is_ct_labels_open: false,
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
},
},
undefined
@@ -98,4 +108,36 @@ describe('uiSettingsMixin', () => {
).toEqual(false);
});
});
describe('#conversationSidebarItemsOrder', () => {
it('returns correct values', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.conversationSidebarItemsOrder).toEqual([
{ name: 'conversation_info' },
{ name: 'contact_attributes' },
{ name: 'previous_conversation' },
{ name: 'conversation_actions' },
]);
});
});
describe('#contactSidebarItemsOrder', () => {
it('returns correct values', () => {
const Component = {
render() {},
title: 'TestComponent',
mixins: [uiSettingsMixin],
};
const wrapper = shallowMount(Component, { store, localVue });
expect(wrapper.vm.contactSidebarItemsOrder).toEqual([
{ name: 'contact_attributes' },
{ name: 'contact_labels' },
{ name: 'previous_conversation' },
]);
});
});
});

View File

@@ -1,10 +1,28 @@
import { mapGetters } from 'vuex';
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = [
{ name: 'conversation_info' },
{ name: 'contact_attributes' },
{ name: 'previous_conversation' },
{ name: 'conversation_actions' },
];
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = [
{ name: 'contact_attributes' },
{ name: 'contact_labels' },
{ name: 'previous_conversation' },
];
export default {
computed: {
...mapGetters({
uiSettings: 'getUISettings',
}),
conversationSidebarItemsOrder() {
const { conversation_sidebar_items_order: itemsOrder } = this.uiSettings;
return itemsOrder || DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
},
contactSidebarItemsOrder() {
const { contact_sidebar_items_order: itemsOrder } = this.uiSettings;
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
},
},
methods: {
updateUISettings(uiSettings = {}) {

View File

@@ -12,42 +12,71 @@
:contact="contact"
@panel-close="onClose"
/>
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
compact
@click="value => toggleSidebarUIState('is_ct_custom_attr_open', value)"
<draggable
:list="contactSidebarItems"
:disabled="!dragEnabled"
class="list-group"
ghost-class="ghost"
@start="dragging = true"
@end="onDragEnd"
>
<custom-attributes
:contact-id="contact.id"
attribute-type="contact_attribute"
attribute-class="conversation--attribute"
:custom-attributes="contact.custom_attributes"
class="even"
/>
<custom-attribute-selector
attribute-type="contact_attribute"
:contact-id="contact.id"
/>
</accordion-item>
<accordion-item
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CONTACT_LABELS')"
:is-open="isContactSidebarItemOpen('is_ct_labels_open')"
@click="value => toggleSidebarUIState('is_ct_labels_open', value)"
>
<contact-label :contact-id="contact.id" class="contact-labels" />
</accordion-item>
<accordion-item
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.PREVIOUS_CONVERSATIONS')"
:is-open="isContactSidebarItemOpen('is_ct_prev_conv_open')"
@click="value => toggleSidebarUIState('is_ct_prev_conv_open', value)"
>
<contact-conversations
v-if="contact.id"
:contact-id="contact.id"
conversation-id=""
/>
</accordion-item>
<transition-group>
<div
v-for="element in contactSidebarItems"
:key="element.name"
class="list-group-item"
>
<div v-if="element.name === 'contact_attributes'">
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
compact
@click="
value => toggleSidebarUIState('is_ct_custom_attr_open', value)
"
>
<custom-attributes
:contact-id="contact.id"
attribute-type="contact_attribute"
attribute-class="conversation--attribute"
:custom-attributes="contact.custom_attributes"
class="even"
/>
<custom-attribute-selector
attribute-type="contact_attribute"
:contact-id="contact.id"
/>
</accordion-item>
</div>
<div v-if="element.name === 'contact_labels'">
<accordion-item
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CONTACT_LABELS')"
:is-open="isContactSidebarItemOpen('is_ct_labels_open')"
@click="value => toggleSidebarUIState('is_ct_labels_open', value)"
>
<contact-label :contact-id="contact.id" class="contact-labels" />
</accordion-item>
</div>
<div v-if="element.name === 'previous_conversation'">
<accordion-item
:title="
$t('CONTACT_PANEL.SIDEBAR_SECTIONS.PREVIOUS_CONVERSATIONS')
"
:is-open="isContactSidebarItemOpen('is_ct_prev_conv_open')"
@click="
value => toggleSidebarUIState('is_ct_prev_conv_open', value)
"
>
<contact-conversations
v-if="contact.id"
:contact-id="contact.id"
conversation-id=""
/>
</accordion-item>
</div>
</div>
</transition-group>
</draggable>
</div>
</template>
@@ -58,7 +87,7 @@ import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/Contact
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
import CustomAttributeSelector from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributeSelector.vue';
import draggable from 'vuedraggable';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
@@ -69,6 +98,7 @@ export default {
ContactLabel,
CustomAttributes,
CustomAttributeSelector,
draggable,
},
mixins: [uiSettingsMixin],
props: {
@@ -85,12 +115,30 @@ export default {
default: true,
},
},
data() {
return {
dragEnabled: true,
contactSidebarItems: [],
dragging: false,
};
},
computed: {
hasContactAttributes() {
const { custom_attributes: customAttributes } = this.contact;
return customAttributes && Object.keys(customAttributes).length;
},
},
mounted() {
this.contactSidebarItems = this.contactSidebarItemsOrder;
},
methods: {
onDragEnd() {
this.dragging = false;
this.updateUISettings({
contact_sidebar_items_order: this.contactSidebarItems,
});
},
},
};
</script>

View File

@@ -4,118 +4,95 @@
<i class="ion-chevron-right" />
</span>
<contact-info :contact="contact" :channel-type="channelType" />
<div class="conversation--actions">
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
:is-open="isContactSidebarItemOpen('is_conv_actions_open')"
@click="value => toggleSidebarUIState('is_conv_actions_open', value)"
>
<div>
<div class="multiselect-wrap--small">
<contact-details-item
compact
:title="$t('CONVERSATION_SIDEBAR.ASSIGNEE_LABEL')"
<draggable
:list="conversationSidebarItems"
:disabled="!dragEnabled"
class="list-group"
ghost-class="ghost"
@start="dragging = true"
@end="onDragEnd"
>
<transition-group>
<div
v-for="element in conversationSidebarItems"
:key="element.name"
class="list-group-item"
>
<div
v-if="element.name === 'conversation_actions'"
class="conversation--actions"
>
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
:is-open="isContactSidebarItemOpen('is_conv_actions_open')"
@click="
value => toggleSidebarUIState('is_conv_actions_open', value)
"
>
<template v-slot:button>
<woot-button
v-if="showSelfAssign"
icon="ion-arrow-right-c"
variant="link"
size="small"
@click="onSelfAssign"
>
{{ $t('CONVERSATION_SIDEBAR.SELF_ASSIGN') }}
</woot-button>
</template>
</contact-details-item>
<multiselect-dropdown
:options="agentsList"
:selected-item="assignedAgent"
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
:multiselector-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
"
:no-search-result="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
"
:input-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
"
@click="onClickAssignAgent"
/>
<conversation-action
:conversation-id="conversationId"
:inbox-id="inboxId"
/>
</accordion-item>
</div>
<div class="multiselect-wrap--small">
<contact-details-item
<div v-else-if="element.name === 'conversation_info'">
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
:is-open="isContactSidebarItemOpen('is_conv_details_open')"
compact
:title="$t('CONVERSATION_SIDEBAR.TEAM_LABEL')"
/>
<multiselect-dropdown
:options="teamsList"
:selected-item="assignedTeam"
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
:multiselector-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
@click="
value => toggleSidebarUIState('is_conv_details_open', value)
"
:no-search-result="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
"
:input-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
"
@click="onClickAssignTeam"
/>
>
<conversation-info
:conversation-attributes="conversationAdditionalAttributes"
:contact-attributes="contactAdditionalAttributes"
>
</conversation-info>
</accordion-item>
</div>
<div v-else-if="element.name === 'contact_attributes'">
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
compact
@click="
value =>
toggleSidebarUIState('is_contact_attributes_open', value)
"
>
<custom-attributes
attribute-type="contact_attribute"
attribute-class="conversation--attribute"
class="even"
:contact-id="contact.id"
/>
<custom-attribute-selector
attribute-type="contact_attribute"
:contact-id="contact.id"
/>
</accordion-item>
</div>
<div v-else-if="element.name === 'previous_conversation'">
<accordion-item
v-if="contact.id"
:title="
$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')
"
:is-open="isContactSidebarItemOpen('is_previous_conv_open')"
@click="
value => toggleSidebarUIState('is_previous_conv_open', value)
"
>
<contact-conversations
:contact-id="contact.id"
:conversation-id="conversationId"
/>
</accordion-item>
</div>
<contact-details-item
compact
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
/>
<conversation-labels :conversation-id="conversationId" />
</div>
</accordion-item>
</div>
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
:is-open="isContactSidebarItemOpen('is_conv_details_open')"
compact
@click="value => toggleSidebarUIState('is_conv_details_open', value)"
>
<conversation-info
:conversation-attributes="conversationAdditionalAttributes"
:contact-attributes="contactAdditionalAttributes"
>
</conversation-info>
</accordion-item>
<accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
compact
@click="
value => toggleSidebarUIState('is_contact_attributes_open', value)
"
>
<custom-attributes
attribute-type="contact_attribute"
attribute-class="conversation--attribute"
class="even"
:contact-id="contact.id"
/>
<custom-attribute-selector
attribute-type="contact_attribute"
:contact-id="contact.id"
/>
</accordion-item>
<accordion-item
v-if="contact.id"
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')"
:is-open="isContactSidebarItemOpen('is_previous_conv_open')"
@click="value => toggleSidebarUIState('is_previous_conv_open', value)"
>
<contact-conversations
:contact-id="contact.id"
:conversation-id="conversationId"
/>
</accordion-item>
</transition-group>
</draggable>
</div>
</template>
@@ -126,27 +103,25 @@ import agentMixin from '../../../mixins/agentMixin';
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
import ContactConversations from './ContactConversations.vue';
import ContactDetailsItem from './ContactDetailsItem.vue';
import ConversationAction from './ConversationAction.vue';
import ContactInfo from './contact/ContactInfo';
import ConversationInfo from './ConversationInfo';
import ConversationLabels from './labels/LabelBox.vue';
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
import CustomAttributes from './customAttributes/CustomAttributes.vue';
import CustomAttributeSelector from './customAttributes/CustomAttributeSelector.vue';
import draggable from 'vuedraggable';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
components: {
AccordionItem,
ContactConversations,
ContactDetailsItem,
ContactInfo,
ConversationInfo,
ConversationLabels,
MultiselectDropdown,
CustomAttributes,
CustomAttributeSelector,
ConversationAction,
draggable,
},
mixins: [alertMixin, agentMixin, uiSettingsMixin],
props: {
@@ -163,6 +138,13 @@ export default {
default: () => {},
},
},
data() {
return {
dragEnabled: true,
conversationSidebarItems: [],
dragging: false,
};
},
computed: {
...mapGetters({
currentChat: 'getSelectedChat',
@@ -194,61 +176,6 @@ export default {
const { custom_attributes: customAttributes } = this.contact;
return customAttributes && Object.keys(customAttributes).length;
},
teamsList() {
if (this.assignedTeam) {
return [
{
id: 0,
name: 'None',
},
...this.teams,
];
}
return this.teams;
},
assignedAgent: {
get() {
return this.currentChat.meta.assignee;
},
set(agent) {
const agentId = agent ? agent.id : 0;
this.$store.dispatch('setCurrentChatAssignee', agent);
this.$store
.dispatch('assignAgent', {
conversationId: this.currentChat.id,
agentId,
})
.then(() => {
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
});
},
},
assignedTeam: {
get() {
return this.currentChat.meta.team;
},
set(team) {
const teamId = team ? team.id : 0;
this.$store.dispatch('setCurrentChatTeam', team);
this.$store
.dispatch('assignTeam', {
conversationId: this.currentChat.id,
teamId,
})
.then(() => {
this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM'));
});
},
},
showSelfAssign() {
if (!this.assignedAgent) {
return true;
}
if (this.assignedAgent.id !== this.currentUser.id) {
return true;
}
return false;
},
},
watch: {
conversationId(newConversationId, prevConversationId) {
@@ -261,6 +188,7 @@ export default {
},
},
mounted() {
this.conversationSidebarItems = this.conversationSidebarItemsOrder;
this.getContactDetails();
this.$store.dispatch('attributes/get', 0);
},
@@ -281,43 +209,12 @@ export default {
openTranscriptModal() {
this.showTranscriptModal = true;
},
onSelfAssign() {
const {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
avatar_url,
} = this.currentUser;
const selfAssign = {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
thumbnail: avatar_url,
};
this.assignedAgent = selfAssign;
},
onClickAssignAgent(selectedItem) {
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
this.assignedAgent = null;
} else {
this.assignedAgent = selectedItem;
}
},
onClickAssignTeam(selectedItemTeam) {
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
this.assignedTeam = null;
} else {
this.assignedTeam = selectedItemTeam;
}
onDragEnd() {
this.dragging = false;
this.updateUISettings({
conversation_sidebar_items_order: this.conversationSidebarItems,
});
},
},
};

View File

@@ -0,0 +1,189 @@
<template>
<div>
<div class="multiselect-wrap--small">
<contact-details-item
compact
:title="$t('CONVERSATION_SIDEBAR.ASSIGNEE_LABEL')"
>
<template v-slot:button>
<woot-button
v-if="showSelfAssign"
icon="ion-arrow-right-c"
variant="link"
size="small"
@click="onSelfAssign"
>
{{ $t('CONVERSATION_SIDEBAR.SELF_ASSIGN') }}
</woot-button>
</template>
</contact-details-item>
<multiselect-dropdown
:options="agentsList"
:selected-item="assignedAgent"
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
:multiselector-placeholder="$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')"
:no-search-result="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
"
:input-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
"
@click="onClickAssignAgent"
/>
</div>
<div class="multiselect-wrap--small">
<contact-details-item
compact
:title="$t('CONVERSATION_SIDEBAR.TEAM_LABEL')"
/>
<multiselect-dropdown
:options="teamsList"
:selected-item="assignedTeam"
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
:multiselector-placeholder="$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')"
:no-search-result="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
"
:input-placeholder="
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
"
@click="onClickAssignTeam"
/>
</div>
<contact-details-item
compact
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
/>
<conversation-labels :conversation-id="conversationId" />
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import ContactDetailsItem from './ContactDetailsItem.vue';
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
import ConversationLabels from './labels/LabelBox.vue';
import agentMixin from '../../../mixins/agentMixin';
export default {
components: {
ContactDetailsItem,
MultiselectDropdown,
ConversationLabels,
},
mixins: [agentMixin, alertMixin],
props: {
conversationId: {
type: [Number, String],
required: true,
},
inboxId: {
type: Number,
default: undefined,
},
},
computed: {
...mapGetters({
currentChat: 'getSelectedChat',
teams: 'teams/getTeams',
currentUser: 'getCurrentUser',
}),
teamsList() {
if (this.assignedTeam) {
return [
{
id: 0,
name: 'None',
},
...this.teams,
];
}
return this.teams;
},
assignedAgent: {
get() {
return this.currentChat.meta.assignee;
},
set(agent) {
const agentId = agent ? agent.id : 0;
this.$store.dispatch('setCurrentChatAssignee', agent);
this.$store
.dispatch('assignAgent', {
conversationId: this.currentChat.id,
agentId,
})
.then(() => {
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
});
},
},
assignedTeam: {
get() {
return this.currentChat.meta.team;
},
set(team) {
const teamId = team ? team.id : 0;
this.$store.dispatch('setCurrentChatTeam', team);
this.$store
.dispatch('assignTeam', {
conversationId: this.currentChat.id,
teamId,
})
.then(() => {
this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM'));
});
},
},
showSelfAssign() {
if (!this.assignedAgent) {
return true;
}
if (this.assignedAgent.id !== this.currentUser.id) {
return true;
}
return false;
},
},
methods: {
onSelfAssign() {
const {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
avatar_url,
} = this.currentUser;
const selfAssign = {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
thumbnail: avatar_url,
};
this.assignedAgent = selfAssign;
},
onClickAssignAgent(selectedItem) {
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
this.assignedAgent = null;
} else {
this.assignedAgent = selectedItem;
}
},
onClickAssignTeam(selectedItemTeam) {
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
this.assignedTeam = null;
} else {
this.assignedTeam = selectedItemTeam;
}
},
},
};
</script>

View File

@@ -65,6 +65,7 @@
"vue-template-compiler": "2.6.12",
"vue-upload-component": "2.8.22",
"vue2-datepicker": "^3.9.1",
"vuedraggable": "^2.24.3",
"vuelidate": "0.7.6",
"vuex": "~2.1.1",
"vuex-router-sync": "~4.1.2"

View File

@@ -13540,6 +13540,11 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
sortablejs@1.10.2:
version "1.10.2"
resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@@ -15112,6 +15117,13 @@ vue@2.6.12, vue@^2.6.12:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
vuedraggable@^2.24.3:
version "2.24.3"
resolved "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
dependencies:
sortablejs "1.10.2"
vuelidate@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.6.tgz#84100c13b943470660d0416642845cd2a1edf4b2"