feat: Create category component (#5103)

This commit is contained in:
Muhsin Keloth 2022-07-26 12:15:01 +05:30 committed by GitHub
parent 92bb84127b
commit bd7a56061e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 264 additions and 9 deletions

View file

@ -0,0 +1,30 @@
import AddCategoryComponent from './AddCategory.vue';
import { action } from '@storybook/addon-actions';
export default {
title: 'Components/Help Center',
component: AddCategoryComponent,
argTypes: {
show: {
defaultValue: true,
control: {
type: 'boolean',
},
},
},
};
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { AddCategoryComponent },
template:
'<add-category-component v-bind="$props" @create="onCreate" @cancel="onClose" />',
});
export const AddCategory = Template.bind({});
AddCategory.args = {
portalName: 'Chatwoot help center',
locale: 'En-US',
onCreate: action('create'),
onClose: action('cancel'),
};

View file

@ -0,0 +1,160 @@
<template>
<modal :show.sync="show" :on-close="onClose">
<woot-modal-header
:header-title="$t('HELP_CENTER.CATEGORY.ADD.TITLE')"
:header-content="$t('HELP_CENTER.CATEGORY.ADD.SUB_TITLE')"
/>
<form class="row" @submit.prevent="onCreate">
<div class="medium-12 columns">
<div class="row article-info">
<div class="columns medium-6">
<label>
<span>{{ $t('HELP_CENTER.CATEGORY.ADD.PORTAL') }}</span>
<p class="value">{{ portalName }}</p>
</label>
</div>
<div class="columns medium-6">
<label>
<span>{{ $t('HELP_CENTER.CATEGORY.ADD.LOCALE') }}</span>
<p class="value">{{ locale }}</p>
</label>
</div>
</div>
<woot-input
v-model.trim="name"
:class="{ error: $v.name.$error }"
class="medium-12 columns"
:error="nameError"
:label="$t('HELP_CENTER.CATEGORY.ADD.NAME.LABEL')"
:placeholder="$t('HELP_CENTER.CATEGORY.ADD.NAME.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.CATEGORY.ADD.NAME.HELP_TEXT')"
@input="onNameChange"
/>
<woot-input
v-model.trim="slug"
:class="{ error: $v.slug.$error }"
class="medium-12 columns"
:error="slugError"
:label="$t('HELP_CENTER.CATEGORY.ADD.SLUG.LABEL')"
:placeholder="$t('HELP_CENTER.CATEGORY.ADD.SLUG.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.CATEGORY.ADD.SLUG.HELP_TEXT')"
@input="$v.slug.$touch"
/>
<label :class="{ error: $v.description.$error }">
{{ $t('HELP_CENTER.CATEGORY.ADD.DESCRIPTION.LABEL') }}
<textarea
v-model="description"
rows="3"
type="text"
:placeholder="
$t('HELP_CENTER.CATEGORY.ADD.DESCRIPTION.PLACEHOLDER')
"
@blur="$v.description.$touch"
/>
<span v-if="$v.description.$error" class="message">
{{ $t('HELP_CENTER.CATEGORY.ADD.DESCRIPTION.ERROR') }}
</span>
</label>
<div class="medium-12 columns">
<div class="modal-footer justify-content-end w-full">
<woot-button class="button clear" @click.prevent="onClose">
{{ $t('HELP_CENTER.CATEGORY.ADD.BUTTONS.CANCEL') }}
</woot-button>
<woot-button>
{{ $t('HELP_CENTER.CATEGORY.ADD.BUTTONS.CREATE') }}
</woot-button>
</div>
</div>
</div>
</form>
</modal>
</template>
<script>
import Modal from 'dashboard/components/Modal';
import alertMixin from 'shared/mixins/alertMixin';
import { required, minLength } from 'vuelidate/lib/validators';
import { convertToCategorySlug } from 'dashboard/helper/commons.js';
export default {
components: {
Modal,
},
mixins: [alertMixin],
props: {
show: {
type: Boolean,
default: true,
},
portalName: {
type: String,
default: '',
},
locale: {
type: String,
default: '',
},
},
data() {
return {
name: '',
slug: '',
description: '',
};
},
validations: {
name: {
required,
minLength: minLength(2),
},
slug: {
required,
},
description: {
required,
},
},
computed: {
nameError() {
if (this.$v.name.$error) {
return this.$t('HELP_CENTER.CATEGORY.ADD.NAME.ERROR');
}
return '';
},
slugError() {
if (this.$v.slug.$error) {
return this.$t('HELP_CENTER.CATEGORY.ADD.SLUG.ERROR');
}
return '';
},
},
methods: {
onNameChange() {
this.slug = convertToCategorySlug(this.name);
},
onCreate() {
this.$emit('create');
this.reset();
this.$emit('cancel');
},
onClose() {
this.reset();
this.$emit('cancel');
},
reset() {
this.name = '';
this.slug = '';
this.description = '';
},
},
};
</script>
<style scoped lang="scss">
.article-info {
width: 100%;
margin: 0 0 var(--space-normal);
.value {
color: var(--s-600);
}
}
</style>

View file

@ -1,5 +1,5 @@
<template> <template>
<label> <label class="container">
<span v-if="label">{{ label }}</span> <span v-if="label">{{ label }}</span>
<input <input
:value="value" :value="value"
@ -10,7 +10,7 @@
@input="onChange" @input="onChange"
@blur="onBlur" @blur="onBlur"
/> />
<p v-if="helpText" class="help-text" /> <p v-if="helpText" class="help-text">{{ helpText }}</p>
<span v-if="error" class="message"> <span v-if="error" class="message">
{{ error }} {{ error }}
</span> </span>
@ -46,7 +46,7 @@ export default {
}, },
readonly: { readonly: {
type: Boolean, type: Boolean,
deafaut: false, default: false,
}, },
styles: { styles: {
type: Object, type: Object,
@ -63,3 +63,21 @@ export default {
}, },
}; };
</script> </script>
<style scoped lang="scss">
.container {
margin: 0 0 var(--space-normal);
input {
margin: 0;
}
.page-sub-title {
color: var(--s-600);
}
.help-text {
margin-top: var(--space-micro);
font-size: var(--font-size-mini);
color: var(--s-600);
font-style: normal;
}
}
</style>

View file

@ -62,9 +62,16 @@ export const createPendingMessage = data => {
return pendingMessage; return pendingMessage;
}; };
export const convertToSlug = text => { export const convertToAttributeSlug = text => {
return text return text
.toLowerCase() .toLowerCase()
.replace(/[^\w ]+/g, '') .replace(/[^\w ]+/g, '')
.replace(/ +/g, '_'); .replace(/ +/g, '_');
}; };
export const convertToCategorySlug = text => {
return text
.toLowerCase()
.replace(/[^\w ]+/g, '')
.replace(/ +/g, '-');
};

View file

@ -1,7 +1,8 @@
import { import {
getTypingUsersText, getTypingUsersText,
createPendingMessage, createPendingMessage,
convertToSlug, convertToAttributeSlug,
convertToCategorySlug,
} from '../commons'; } from '../commons';
describe('#getTypingUsersText', () => { describe('#getTypingUsersText', () => {
@ -88,8 +89,18 @@ describe('#createPendingMessage', () => {
}); });
}); });
describe('convertToSlug', () => { describe('convertToAttributeSlug', () => {
it('should convert to slug', () => { it('should convert to slug', () => {
expect(convertToSlug('Test@%^&*(){}>.!@`~_ ing')).toBe('test__ing'); expect(convertToAttributeSlug('Test@%^&*(){}>.!@`~_ ing')).toBe(
'test__ing'
);
});
});
describe('convertToCategorySlug', () => {
it('should convert to slug', () => {
expect(convertToCategorySlug('User profile guide')).toBe(
'user-profile-guide'
);
}); });
}); });

View file

@ -40,6 +40,35 @@
"SEARCH": { "SEARCH": {
"PLACEHOLDER": "Search for articles" "PLACEHOLDER": "Search for articles"
} }
},
"CATEGORY": {
"ADD": {
"TITLE": "Create a category",
"SUB_TITLE": "The category will be used in the public facing portal to categorize articles.",
"PORTAL": "Portal",
"LOCALE": "Locale",
"NAME": {
"LABEL": "Name",
"PLACEHOLDER": "Category name",
"HELP_TEXT": "The category name will be used in the public facing portal to categorize articles.",
"ERROR": "Name is required"
},
"SLUG": {
"LABEL": "Slug",
"PLACEHOLDER": "Category slug for urls",
"HELP_TEXT": "app.chatwoot.com/portal/my-portal/en-US/categories/my-slug",
"ERROR": "Slug is required"
},
"DESCRIPTION": {
"LABEL": "Description",
"PLACEHOLDER": "Give a short description about the category.",
"ERROR": "Description is required"
},
"BUTTONS": {
"CREATE": "Create category",
"CANCEL": "Cancel"
}
}
} }
} }
} }

View file

@ -104,7 +104,7 @@
<script> <script>
import { required, minLength } from 'vuelidate/lib/validators'; import { required, minLength } from 'vuelidate/lib/validators';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { convertToSlug } from 'dashboard/helper/commons.js'; import { convertToAttributeSlug } from 'dashboard/helper/commons.js';
import { ATTRIBUTE_MODELS, ATTRIBUTE_TYPES } from './constants'; import { ATTRIBUTE_MODELS, ATTRIBUTE_TYPES } from './constants';
import alertMixin from 'shared/mixins/alertMixin'; import alertMixin from 'shared/mixins/alertMixin';
@ -199,7 +199,7 @@ export default {
this.isTouched = true; this.isTouched = true;
}, },
onDisplayNameChange() { onDisplayNameChange() {
this.attributeKey = convertToSlug(this.displayName); this.attributeKey = convertToAttributeSlug(this.displayName);
}, },
async addAttributes() { async addAttributes() {
this.$v.$touch(); this.$v.$touch();