feat: Add a pre-chat form on widget (#1769)
This commit is contained in:
@@ -75,8 +75,6 @@ export default {
|
||||
padding: $space-two $space-medium;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
@include shadow-large;
|
||||
|
||||
.header-branding {
|
||||
display: flex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<header class="header-expanded py-8 px-6 bg-white relative box-border w-full">
|
||||
<header class="header-expanded bg-white py-8 px-6 relative box-border w-full">
|
||||
<div class="flex justify-between items-start">
|
||||
<img v-if="avatarUrl" class="logo" :src="avatarUrl" />
|
||||
<header-actions :show-popout-button="showPopoutButton" />
|
||||
@@ -50,8 +50,6 @@ export default {
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
|
||||
.header-expanded {
|
||||
@include shadow-large;
|
||||
|
||||
.logo {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
|
||||
59
app/javascript/widget/components/Form/Input.vue
Normal file
59
app/javascript/widget/components/Form/Input.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<label class="block">
|
||||
<div
|
||||
v-if="label"
|
||||
class="mb-2 text-xs font-medium"
|
||||
:class="{
|
||||
'text-black-800': !error,
|
||||
'text-red-400': error,
|
||||
}"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
<input
|
||||
:type="type"
|
||||
class="border rounded w-full py-2 px-3 text-slate-700 leading-tight outline-none"
|
||||
:class="{
|
||||
'border-black-200 hover:border-black-300 focus:border-black-300': !error,
|
||||
'border-red-200 hover:border-red-300 focus:border-red-300': error,
|
||||
}"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@change="onChange"
|
||||
/>
|
||||
<div v-if="error" class="text-red-400 mt-2 text-xs font-medium">
|
||||
{{ error }}
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event) {
|
||||
this.$emit('input', event.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
63
app/javascript/widget/components/Form/TextArea.vue
Normal file
63
app/javascript/widget/components/Form/TextArea.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<label class="block">
|
||||
<div
|
||||
v-if="label"
|
||||
class="mb-2 text-xs font-medium"
|
||||
:class="{
|
||||
'text-black-800': !error,
|
||||
'text-red-400': error,
|
||||
}"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
<textarea
|
||||
class="resize-none border rounded w-full py-2 px-3 text-slate-700 leading-tight outline-none"
|
||||
:class="{
|
||||
'border-black-200 hover:border-black-300 focus:border-black-300': !error,
|
||||
'border-red-200 hover:border-red-300 focus:border-red-300': error,
|
||||
}"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@change="onChange"
|
||||
/>
|
||||
<div v-if="error" class="text-red-400 mt-2 text-xs font-medium">
|
||||
{{ error }}
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event) {
|
||||
this.$emit('input', event.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
textarea {
|
||||
min-height: 8rem;
|
||||
}
|
||||
</style>
|
||||
117
app/javascript/widget/components/PreChat/Form.vue
Normal file
117
app/javascript/widget/components/PreChat/Form.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<form
|
||||
class="flex flex-1 flex-col p-6 overflow-y-scroll"
|
||||
@submit.prevent="onSubmit"
|
||||
>
|
||||
<div v-if="options.preChatMessage" class="text-black-800 text-sm leading-5">
|
||||
{{ options.preChatMessage }}
|
||||
</div>
|
||||
<form-input
|
||||
v-if="options.requireEmail"
|
||||
v-model="fullName"
|
||||
class="mt-5"
|
||||
:label="$t('PRE_CHAT_FORM.FIELDS.FULL_NAME.LABEL')"
|
||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.FULL_NAME.PLACEHOLDER')"
|
||||
type="text"
|
||||
:error="
|
||||
$v.fullName.$error ? $t('PRE_CHAT_FORM.FIELDS.FULL_NAME.ERROR') : ''
|
||||
"
|
||||
/>
|
||||
<form-input
|
||||
v-if="options.requireEmail"
|
||||
v-model="emailAddress"
|
||||
class="mt-5"
|
||||
:label="$t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.LABEL')"
|
||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.PLACEHOLDER')"
|
||||
type="email"
|
||||
:error="
|
||||
$v.emailAddress.$error
|
||||
? $t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.ERROR')
|
||||
: ''
|
||||
"
|
||||
/>
|
||||
<form-text-area
|
||||
v-model="message"
|
||||
class="my-5"
|
||||
:label="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.LABEL')"
|
||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.PLACEHOLDER')"
|
||||
:error="$v.message.$error ? $t('PRE_CHAT_FORM.FIELDS.MESSAGE.ERROR') : ''"
|
||||
/>
|
||||
<woot-button
|
||||
class="font-medium"
|
||||
block
|
||||
:bg-color="widgetColor"
|
||||
:text-color="textColor"
|
||||
:disabled="isCreating"
|
||||
>
|
||||
<spinner v-if="isCreating" class="p-0" />
|
||||
{{ $t('START_CONVERSATION') }}
|
||||
</woot-button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WootButton from 'shared/components/Button';
|
||||
import FormInput from '../Form/Input';
|
||||
import FormTextArea from '../Form/TextArea';
|
||||
import Spinner from 'shared/components/Spinner';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getContrastingTextColor } from 'shared/helpers/ColorHelper';
|
||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||
export default {
|
||||
components: {
|
||||
FormInput,
|
||||
FormTextArea,
|
||||
WootButton,
|
||||
Spinner,
|
||||
},
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
fullName: {
|
||||
required,
|
||||
},
|
||||
emailAddress: {
|
||||
required,
|
||||
email,
|
||||
},
|
||||
message: {
|
||||
required,
|
||||
minLength: minLength(10),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fullName: '',
|
||||
emailAddress: '',
|
||||
message: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
isCreating: 'conversation/getIsCreating',
|
||||
}),
|
||||
textColor() {
|
||||
return getContrastingTextColor(this.widgetColor);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('conversation/createConversation', {
|
||||
fullName: this.fullName,
|
||||
emailAddress: this.emailAddress,
|
||||
message: this.message,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user