mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 11:37:58 +00:00
feat: Add regex validation on form message inputs (#2317)
Co-authored-by: Nithin David Thomas <webofnithin@gmail.com> Co-authored-by: Sojan <sojan@pepalo.com>
This commit is contained in:
@@ -44,6 +44,9 @@ Metrics/BlockLength:
|
|||||||
- '**/routes.rb'
|
- '**/routes.rb'
|
||||||
- 'config/environments/*'
|
- 'config/environments/*'
|
||||||
- db/schema.rb
|
- db/schema.rb
|
||||||
|
Metrics/ModuleLength:
|
||||||
|
Exclude:
|
||||||
|
- lib/woot_message_seeder.rb
|
||||||
Rails/ApplicationController:
|
Rails/ApplicationController:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/api/v1/widget/messages_controller.rb'
|
- 'app/controllers/api/v1/widget/messages_controller.rb'
|
||||||
|
|||||||
@@ -1,12 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="form chat-bubble agent">
|
<div class="form chat-bubble agent">
|
||||||
<form @submit.prevent="onSubmit">
|
<form @submit.prevent="onSubmit">
|
||||||
<div v-for="item in items" :key="item.key" class="form-block">
|
<div
|
||||||
|
v-for="item in items"
|
||||||
|
:key="item.key"
|
||||||
|
class="form-block"
|
||||||
|
:class="{
|
||||||
|
'has-submitted': hasSubmitted,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<label>{{ item.label }}</label>
|
<label>{{ item.label }}</label>
|
||||||
<input
|
<input
|
||||||
v-if="item.type === 'email' || item.type === 'text'"
|
v-if="item.type === 'email'"
|
||||||
v-model="formValues[item.name]"
|
v-model="formValues[item.name]"
|
||||||
:type="item.type"
|
:type="item.type"
|
||||||
|
:pattern="item.regex"
|
||||||
|
:title="item.title"
|
||||||
|
:required="item.required && 'required'"
|
||||||
|
:name="item.name"
|
||||||
|
:placeholder="item.placeholder"
|
||||||
|
:disabled="!!submittedValues.length"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-else-if="item.type === 'text'"
|
||||||
|
v-model="formValues[item.name]"
|
||||||
|
:required="item.required && 'required'"
|
||||||
|
:pattern="item.pattern"
|
||||||
|
:title="item.title"
|
||||||
|
:type="item.type"
|
||||||
:name="item.name"
|
:name="item.name"
|
||||||
:placeholder="item.placeholder"
|
:placeholder="item.placeholder"
|
||||||
:disabled="!!submittedValues.length"
|
:disabled="!!submittedValues.length"
|
||||||
@@ -14,6 +35,8 @@
|
|||||||
<textarea
|
<textarea
|
||||||
v-else-if="item.type === 'text_area'"
|
v-else-if="item.type === 'text_area'"
|
||||||
v-model="formValues[item.name]"
|
v-model="formValues[item.name]"
|
||||||
|
:required="item.required && 'required'"
|
||||||
|
:title="item.title"
|
||||||
:name="item.name"
|
:name="item.name"
|
||||||
:placeholder="item.placeholder"
|
:placeholder="item.placeholder"
|
||||||
:disabled="!!submittedValues.length"
|
:disabled="!!submittedValues.length"
|
||||||
@@ -21,6 +44,7 @@
|
|||||||
<select
|
<select
|
||||||
v-else-if="item.type === 'select'"
|
v-else-if="item.type === 'select'"
|
||||||
v-model="formValues[item.name]"
|
v-model="formValues[item.name]"
|
||||||
|
:required="item.required && 'required'"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in item.options"
|
v-for="option in item.options"
|
||||||
@@ -30,13 +54,16 @@
|
|||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
<span class="error-message">
|
||||||
|
{{ item.pattern_error || $t('CHAT_FORM.INVALID.FIELD') }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="!submittedValues.length"
|
v-if="!submittedValues.length"
|
||||||
class="button block"
|
class="button block"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="!isFormValid"
|
|
||||||
:style="{ background: widgetColor, borderColor: widgetColor }"
|
:style="{ background: widgetColor, borderColor: widgetColor }"
|
||||||
|
@click="onSubmitClick"
|
||||||
>
|
>
|
||||||
{{ buttonLabel || $t('COMPONENTS.FORM_BUBBLE.SUBMIT') }}
|
{{ buttonLabel || $t('COMPONENTS.FORM_BUBBLE.SUBMIT') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -64,6 +91,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
formValues: {},
|
formValues: {},
|
||||||
|
hasSubmitted: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -84,6 +112,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onSubmitClick() {
|
||||||
|
this.hasSubmitted = true;
|
||||||
|
},
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
if (!this.isFormValid) {
|
if (!this.isFormValid) {
|
||||||
return;
|
return;
|
||||||
@@ -171,5 +202,29 @@ export default {
|
|||||||
.button {
|
.button {
|
||||||
font-size: $font-size-default;
|
font-size: $font-size-default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
display: none;
|
||||||
|
margin-top: $space-smaller;
|
||||||
|
color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-submitted {
|
||||||
|
input:invalid {
|
||||||
|
border: 1px solid $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:invalid + .error-message {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:invalid {
|
||||||
|
border: 1px solid $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:invalid + .error-message {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -51,5 +51,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit"
|
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
|
||||||
|
"CHAT_FORM": {
|
||||||
|
"INVALID": {
|
||||||
|
"FIELD": "Invalid field"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ class ContentAttributeValidator < ActiveModel::Validator
|
|||||||
ALLOWED_SELECT_ITEM_KEYS = [:title, :value].freeze
|
ALLOWED_SELECT_ITEM_KEYS = [:title, :value].freeze
|
||||||
ALLOWED_CARD_ITEM_KEYS = [:title, :description, :media_url, :actions].freeze
|
ALLOWED_CARD_ITEM_KEYS = [:title, :description, :media_url, :actions].freeze
|
||||||
ALLOWED_CARD_ITEM_ACTION_KEYS = [:text, :type, :payload, :uri].freeze
|
ALLOWED_CARD_ITEM_ACTION_KEYS = [:text, :type, :payload, :uri].freeze
|
||||||
ALLOWED_FORM_ITEM_KEYS = [:type, :placeholder, :label, :name, :options, :default].freeze
|
ALLOWED_FORM_ITEM_KEYS = [:type, :placeholder, :label, :name, :options, :default, :required, :pattern, :title, :pattern_error].freeze
|
||||||
ALLOWED_ARTICLE_KEYS = [:title, :description, :link].freeze
|
ALLOWED_ARTICLE_KEYS = [:title, :description, :link].freeze
|
||||||
|
|
||||||
def validate(record)
|
def validate(record)
|
||||||
|
|||||||
@@ -74,18 +74,25 @@ module WootMessageSeeder
|
|||||||
message_type: :template,
|
message_type: :template,
|
||||||
content_type: 'form',
|
content_type: 'form',
|
||||||
content: 'form',
|
content: 'form',
|
||||||
content_attributes: {
|
content_attributes: sample_form
|
||||||
items: [
|
|
||||||
{ name: 'email', placeholder: 'Please enter your email', type: 'email', label: 'Email' },
|
|
||||||
{ name: 'text_area', placeholder: 'Please enter text', type: 'text_area', label: 'Large Text' },
|
|
||||||
{ name: 'text', placeholder: 'Please enter text', type: 'text', label: 'text', default: 'defaut value' },
|
|
||||||
{ name: 'select', label: 'Select Option', type: 'select', options: [{ label: '🌯 Burito', value: 'Burito' },
|
|
||||||
{ label: '🍝 Pasta', value: 'Pasta' }] }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.sample_form
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{ "name": 'email', "placeholder": 'Please enter your email', "type": 'email', "label": 'Email', "required": 'required',
|
||||||
|
"pattern_error": 'Please fill this field', "pattern": '^[^\s@]+@[^\s@]+\.[^\s@]+$' },
|
||||||
|
{ "name": 'text_area', "placeholder": 'Please enter text', "type": 'text_area', "label": 'Large Text', "required": 'required',
|
||||||
|
"pattern_error": 'Please fill this field' },
|
||||||
|
{ "name": 'text', "placeholder": 'Please enter text', "type": 'text', "label": 'text', "default": 'defaut value', "required": 'required',
|
||||||
|
"pattern": '^[a-zA-Z ]*$', "pattern_error": 'Only alphabets are allowed' },
|
||||||
|
{ "name": 'select', "label": 'Select Option', "type": 'select', "options": [{ "label": '🌯 Burito', "value": 'Burito' },
|
||||||
|
{ "label": '🍝 Pasta', "value": 'Pasta' }] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def self.create_sample_articles_message(conversation)
|
def self.create_sample_articles_message(conversation)
|
||||||
Message.create!(
|
Message.create!(
|
||||||
account: conversation.account,
|
account: conversation.account,
|
||||||
|
|||||||
Reference in New Issue
Block a user