feat: Create a new conversation from the contact panel (#2019)

* Chore: Improve button component styles
This commit is contained in:
Nithin David Thomas
2021-04-16 20:31:07 +05:30
committed by GitHub
parent c287ad08fb
commit 864471a21e
16 changed files with 469 additions and 9 deletions

View File

@@ -3,7 +3,7 @@
<span class="close-button" @click="onClose">
<i class="ion-android-close close-icon" />
</span>
<contact-info :contact="contact" />
<contact-info show-new-message :contact="contact" />
<contact-custom-attributes
v-if="hasContactAttributes"
:custom-attributes="contact.custom_attributes"

View File

@@ -50,19 +50,41 @@
</div>
</div>
<woot-button
v-if="!showNewMessage"
class="edit-contact"
variant="clear"
variant="clear link"
size="small"
@click="toggleEditModal"
>
{{ $t('EDIT_CONTACT.BUTTON_LABEL') }}
</woot-button>
<div v-else class="contact-actions">
<woot-button
class="new-message"
size="small expanded"
@click="toggleConversationModal"
>
{{ $t('CONTACT_PANEL.NEW_MESSAGE') }}
</woot-button>
<woot-button
variant="hollow"
size="small expanded"
@click="toggleEditModal"
>
{{ $t('EDIT_CONTACT.BUTTON_LABEL') }}
</woot-button>
</div>
<edit-contact
v-if="showEditModal"
:show="showEditModal"
:contact="contact"
@cancel="toggleEditModal"
/>
<new-conversation
:show="showConversationModal"
:contact="contact"
@cancel="toggleConversationModal"
/>
</div>
</div>
</template>
@@ -71,6 +93,7 @@ import ContactInfoRow from './ContactInfoRow';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import SocialIcons from './SocialIcons';
import EditContact from './EditContact';
import NewConversation from './NewConversation';
export default {
components: {
@@ -78,6 +101,7 @@ export default {
EditContact,
Thumbnail,
SocialIcons,
NewConversation,
},
props: {
contact: {
@@ -88,10 +112,15 @@ export default {
type: String,
default: '',
},
showNewMessage: {
type: Boolean,
default: false,
},
},
data() {
return {
showEditModal: false,
showConversationModal: false,
};
},
computed: {
@@ -111,6 +140,9 @@ export default {
toggleEditModal() {
this.showEditModal = !this.showEditModal;
},
toggleConversationModal() {
this.showConversationModal = !this.showConversationModal;
},
},
};
</script>
@@ -120,7 +152,7 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact--profile {
align-items: flex-start;
padding: var(--space-normal) var(--space-normal) var(--space-large);
padding: var(--space-normal) var(--space-normal);
.user-thumbnail-box {
margin-right: $space-normal;
@@ -164,8 +196,21 @@ export default {
font-size: $font-weight-normal;
}
}
.contact-actions {
margin: var(--space-small) 0;
}
.button.edit-contact {
margin-left: var(--space-two);
padding-left: var(--space-micro);
}
.edit-contact {
margin-left: var(--space-slab);
.button.new-message {
margin-right: var(--space-small);
}
.contact-actions {
display: flex;
align-items: center;
width: 100%;
}
</style>

View File

@@ -0,0 +1,210 @@
<template>
<form class="conversation--form" @submit.prevent="handleSubmit">
<div v-if="showNoInboxAlert" class="callout warning">
<p>
{{ $t('NEW_CONVERSATION.NO_INBOX') }}
</p>
</div>
<div v-else>
<div class="row">
<div class="columns">
<label :class="{ error: $v.targetInbox.$error }">
{{ $t('NEW_CONVERSATION.FORM.INBOX.LABEL') }}
<select v-model="targetInbox">
<option
v-for="contactableInbox in inboxes"
:key="contactableInbox.inbox.id"
:value="contactableInbox"
>
{{ contactableInbox.inbox.name }}
</option>
</select>
<span v-if="$v.targetInbox.$error" class="message">
{{ $t('NEW_CONVERSATION.FORM.INBOX.ERROR') }}
</span>
</label>
</div>
<div class="columns">
<label>
{{ $t('NEW_CONVERSATION.FORM.TO.LABEL') }}
<div class="contact-input">
<thumbnail
:src="contact.thumbnail"
size="24px"
:username="contact.name"
:status="contact.availability_status"
/>
<h4 class="text-block-title contact-name">
{{ contact.name }}
</h4>
</div>
</label>
</div>
</div>
<div class="row">
<div class="columns">
<label :class="{ error: $v.message.$error }">
{{ $t('NEW_CONVERSATION.FORM.MESSAGE.LABEL') }}
<textarea
v-model="message"
class="message-input"
type="text"
:placeholder="$t('NEW_CONVERSATION.FORM.MESSAGE.PLACEHOLDER')"
@input="$v.message.$touch"
/>
<span v-if="$v.message.$error" class="message">
{{ $t('NEW_CONVERSATION.FORM.MESSAGE.ERROR') }}
</span>
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button class="button clear" @click.prevent="onCancel">
{{ $t('NEW_CONVERSATION.FORM.CANCEL') }}
</button>
<woot-button type="submit" :is-loading="conversationsUiFlags.isCreating">
{{ $t('NEW_CONVERSATION.FORM.SUBMIT') }}
</woot-button>
</div>
</form>
</template>
<script>
import { mapGetters } from 'vuex';
import Thumbnail from 'dashboard/components/widgets/Thumbnail';
import alertMixin from 'shared/mixins/alertMixin';
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
import { required } from 'vuelidate/lib/validators';
export default {
components: {
Thumbnail,
},
mixins: [alertMixin],
props: {
contact: {
type: Object,
default: () => ({}),
},
onSubmit: {
type: Function,
default: () => {},
},
},
data() {
return {
name: '',
message: '',
selectedInbox: '',
};
},
validations: {
message: {
required,
},
targetInbox: {
required,
},
},
computed: {
...mapGetters({
uiFlags: 'contacts/getUIFlags',
conversationsUiFlags: 'contactConversations/getUIFlags',
}),
getNewConversation() {
return {
inboxId: this.targetInbox.inbox.id,
sourceId: this.targetInbox.source_id,
contactId: this.contact.id,
message: { content: this.message },
};
},
targetInbox: {
get() {
return this.selectedInbox || '';
},
set(value) {
this.selectedInbox = value;
},
},
showNoInboxAlert() {
if (!this.contact.contactableInboxes) {
return false;
}
return this.inboxes.length === 0 && !this.uiFlags.isFetchingInboxes;
},
inboxes() {
return this.contact.contactableInboxes || [];
},
},
methods: {
onCancel() {
this.$emit('cancel');
},
onSuccess() {
this.$emit('success');
},
async handleSubmit() {
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
try {
const payload = this.getNewConversation;
await this.onSubmit(payload);
this.onSuccess();
this.showAlert(this.$t('NEW_CONVERSATION.FORM.SUCCESS_MESSAGE'));
} catch (error) {
if (error instanceof ExceptionWithMessage) {
this.showAlert(error.data);
} else {
this.showAlert(this.$t('NEW_CONVERSATION.FORM.ERROR_MESSAGE'));
}
}
},
},
};
</script>
<style scoped lang="scss">
.conversation--form {
padding: var(--space-normal) var(--space-large) var(--space-large);
.columns {
padding: 0 var(--space-smaller);
}
}
.input-group-label {
font-size: var(--font-size-small);
}
.contact-input {
display: flex;
align-items: center;
height: 3.9rem;
background: var(--color-background-light);
border: 1px solid var(--color-border);
padding: var(--space-smaller) var(--space-small);
border-radius: var(--border-radius-small);
.contact-name {
margin: 0;
margin-left: var(--space-small);
}
}
.message-input {
min-height: 8rem;
}
.modal-footer {
display: flex;
justify-content: flex-end;
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<woot-modal :show.sync="show" :on-close="onCancel">
<div class="column content-box">
<woot-modal-header
:header-title="$t('NEW_CONVERSATION.TITLE')"
:header-content="$t('NEW_CONVERSATION.DESC')"
/>
<conversation-form
:contact="contact"
:on-submit="onSubmit"
@success="onSuccess"
@cancel="onCancel"
/>
</div>
</woot-modal>
</template>
<script>
import ConversationForm from './ConversationForm';
export default {
components: {
ConversationForm,
},
props: {
show: {
type: Boolean,
default: false,
},
contact: {
type: Object,
default: () => ({}),
},
},
mounted() {
const { id } = this.contact;
this.$store.dispatch('contacts/fetchContactableInbox', id);
},
methods: {
onCancel() {
this.$emit('cancel');
},
onSuccess() {
this.$emit('cancel');
},
async onSubmit(contactItem) {
await this.$store.dispatch('contactConversations/create', contactItem);
},
},
};
</script>