1
1
mirror of https://github.com/aelve/guide.git synced 2024-12-23 12:52:31 +03:00

Front/feat/duplicate category creation warning (#300)

* Confirm Dialog extended

* Confirmation for duplicate name dialog creation
This commit is contained in:
avele 2019-06-26 17:44:07 +04:00 committed by GitHub
parent 466eb11363
commit 199e55d435
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 67 deletions

View File

@ -1,58 +1,76 @@
<template> <template>
<v-dialog <div>
:value="value" <v-dialog
@input="close" :value="value"
max-width="500px" @input="close"
> max-width="500px"
>
<slot slot="activator" /> <slot slot="activator" />
<v-card @keyup.esc.native="close" tabindex="0"> <v-card @keyup.esc.native="close" tabindex="0">
<v-card-text> <v-card-text>
<v-form <v-form
ref="form" ref="form"
lazy-validation lazy-validation
v-model="isValid" v-model="isValid"
@keydown.native.enter="submit" @keydown.native.enter="submit"
> >
<!-- v-if="value" - cause without it autofocus triggers on first modal open <!-- v-if="value" - cause without it autofocus triggers on first modal open
https://stackoverflow.com/questions/51472947/vuetifys-autofocus-works-only-on-first-modal-open --> https://stackoverflow.com/questions/51472947/vuetifys-autofocus-works-only-on-first-modal-open -->
<v-text-field <v-text-field
v-if="value" v-if="value"
class="mb-3" class="mb-3"
label="Category name" label="Category name"
autofocus autofocus
:rules="categoryValidationRules" :rules="categoryValidationRules"
v-model="categoryName" v-model="categoryName"
ref="categoryNameInput" ref="categoryNameInput"
/> />
<v-text-field <v-text-field
v-model="groupNameInternal" v-model="groupNameInternal"
label="Group" label="Group"
/> />
</v-form> </v-form>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn <v-btn
flat flat
color="primary" color="primary"
@click.native="close" @click.native="close"
> >
Cancel Cancel
</v-btn> </v-btn>
<v-btn <v-btn
flat flat
color="primary" color="primary"
class="add-category-submit-btn" class="add-category-submit-btn"
:disabled="!isValid" :disabled="!isValid"
@click.native="submit" @click.native="submit"
> >
Submit Submit
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog>
</v-dialog>
<ConfirmDialog
v-if="isDuplicateConfirmShow"
:value="isDuplicateConfirmShow"
max-width="500px"
attach="#app"
ref="duplicateConfirm"
>
This group already has categories with the same name:
<a
v-for="category in sameNameCategories"
:key="category.id"
target="_blank"
>{{ category.title }}</a>
</ConfirmDialog>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -60,18 +78,37 @@ import Vue from 'vue'
import Component from 'vue-class-component' import Component from 'vue-class-component'
import { Prop, Watch } from 'vue-property-decorator' import { Prop, Watch } from 'vue-property-decorator'
import { CategoryService } from 'client/service/Category' import { CategoryService } from 'client/service/Category'
import ConfirmDialog from 'client/components/ConfirmDialog.vue'
import DeferredPromise from 'utils/DeferredPromise'
@Component @Component({
components: {
ConfirmDialog
}
})
export default class AddCategoryDialog extends Vue { export default class AddCategoryDialog extends Vue {
@Prop(String) groupName!: string @Prop(String) groupName!: string
@Prop(Boolean) value!: boolean @Prop(Boolean) value!: boolean
groupNameInternal: string = this.groupName || '' groupNameInternal: string = this.groupName || ''
categoryName: string = '' categoryName: string = ''
categoryValidationRules: Function[] = [ categoryValidationRules: Array<(x: string) => boolean | string> = [
(x: string) => !!x || `Category name can't be empty` (x: string) => !!x || `Category name can't be empty`
] ]
isValid: boolean = false isValid: boolean = false
isDuplicateConfirmShow: boolean = false
get categories () {
return this.$store.state.category.categoryList
}
get sameNameCategories () {
if (!this.categoryName || !this.groupName) {
return []
}
return this.categories
.filter(x => x.group === this.groupName && x.title === this.categoryName)
}
@Watch('value') @Watch('value')
onOpen (newVal: boolean) { onOpen (newVal: boolean) {
@ -87,12 +124,37 @@ export default class AddCategoryDialog extends Vue {
if (!this.$refs.form.validate()) { if (!this.$refs.form.validate()) {
return return
} }
if (this.sameNameCategories.length) {
const isDuplicationConfirmed = await this.confirmDuplicate()
if (!isDuplicationConfirmed) {
return
}
}
const createdId = await this.$store.dispatch('category/createCategory', { const createdId = await this.$store.dispatch('category/createCategory', {
title: this.categoryName, title: this.categoryName,
group: this.groupNameInternal group: this.groupNameInternal
}) })
this.$router.push(`haskell/${createdId}`) this.$router.push(`haskell/${createdId}`)
} }
async confirmDuplicate () {
const promise = new DeferredPromise()
this.isDuplicateConfirmShow = true
this.$nextTick(() => {
const duplicateConfirm = this.$refs.duplicateConfirm
duplicateConfirm.$once('canceled', () => {
promise.resolve(false)
this.isDuplicateConfirmShow = false
})
duplicateConfirm.$once('confirmed', () => {
promise.resolve(true)
this.isDuplicateConfirmShow = false
})
})
return promise
}
} }
</script> </script>

View File

@ -2,14 +2,18 @@
<template> <template>
<v-dialog <v-dialog
:value="value" :value="value"
@input="close" :attach="attach"
max-width="500px" max-width="500px"
@input="close"
> >
<slot slot="activator" /> <slot slot="activator" />
<v-card> <v-card>
<v-card-text> <v-card-text v-if="$slots.default">
Are you sure you want to {{ text }} ? <slot />
</v-card-text>
<v-card-text v-else>
{{ fullText || `Are you sure you want to ${text} ?` }}
</v-card-text> </v-card-text>
<v-divider /> <v-divider />
<v-card-actions> <v-card-actions>
@ -17,17 +21,16 @@
<v-btn <v-btn
flat flat
color="primary" color="primary"
class="confirm-btn" @click.native="cancel"
@click.native="confirm"
> >
{{ confirmBtnText }} {{ cancelBtnText }}
</v-btn> </v-btn>
<v-btn <v-btn
flat flat
color="primary" color="primary"
@click.native="cancel" @click.native="confirm"
> >
{{ cancelBtnText }} {{ confirmBtnText }}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
@ -42,12 +45,15 @@ import { Prop } from 'vue-property-decorator'
@Component @Component
export default class ConfirmDialog extends Vue { export default class ConfirmDialog extends Vue {
@Prop(String) text!: string @Prop(String) text!: string
@Prop(String) fullText!: string
@Prop(Boolean) value!: boolean @Prop(Boolean) value!: boolean
@Prop(String) attach!: string
@Prop({ default: 'Continue' }) confirmBtnText!: string @Prop({ default: 'Continue' }) confirmBtnText!: string
@Prop({ default: 'Cancel' }) cancelBtnText!: string @Prop({ default: 'Cancel' }) cancelBtnText!: string
close () { close () {
this.$emit('input', false) this.$emit('input', false)
this.$emit('canceled')
} }
confirm () { confirm () {
this.$emit('confirmed') this.$emit('confirmed')

View File

@ -1,14 +1,17 @@
interface IConfirmDialogProps { interface IConfirmDialogProps {
text: string, fullText: string
confirmBtnText?: string, text: string
confirmBtnText?: string
cancelBtnText?: string cancelBtnText?: string
} }
// TODO looks like documentation isn't working
/** /**
* Use only for vue components methods. * Use only for vue components methods.
* Executes '_confirm' function (see confirmDialogMixin.ts) with provided options before executing following method. * Executes '_confirm' function (see confirmDialogMixin.ts) with provided options before executing following method.
* If not confirmed method is not executed. * If not confirmed method is not executed.
* @param {Object} options - options passed to _confirm function * @param {Object} options - options passed to _confirm function
* @param {String} options.text * @param {String} options.text
* @param {String} options.fullText
* @param {String} options.confirmBtnText * @param {String} options.confirmBtnText
* @param {String} options.cancelBtnText * @param {String} options.cancelBtnText
*/ */

View File

@ -6,18 +6,20 @@ import DeferredPromise from 'utils/DeferredPromise'
const ComponentClass = Vue.extend(ConfirmDialog) const ComponentClass = Vue.extend(ConfirmDialog)
interface IConfirmDialogProps { interface IConfirmDialogProps {
text: string, fullText: string
confirmBtnText?: string, text: string
confirmBtnText?: string
cancelBtnText?: string cancelBtnText?: string
} }
@Mixin @Mixin
export default class ConfirmDialogMixin extends Vue { export default class ConfirmDialogMixin extends Vue {
async _confirm ({ text, confirmBtnText, cancelBtnText }: IConfirmDialogProps): Promise<boolean> { async _confirm ({ text, fullText, confirmBtnText, cancelBtnText }: IConfirmDialogProps): Promise<boolean> {
const instance = new ComponentClass({ const instance = new ComponentClass({
propsData: { propsData: {
value: true, value: true,
text, text,
fullText,
confirmBtnText, confirmBtnText,
cancelBtnText cancelBtnText
} }