parent
a1bcb12f20
commit
053bcad80a
@ -0,0 +1,78 @@ |
||||
<template> |
||||
<v-app id="inspire"> |
||||
<snackbar></snackbar> |
||||
<v-navigation-drawer v-model="drawer" app> |
||||
<v-list dense> |
||||
<v-list-item> |
||||
<v-list-item-action> |
||||
<v-icon>mdi-home</v-icon> |
||||
</v-list-item-action> |
||||
<v-list-item-content> |
||||
<v-list-item-title>Home</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
<v-list-item> |
||||
<v-list-item-action> |
||||
<v-icon>mdi-book</v-icon> |
||||
</v-list-item-action> |
||||
<v-list-item-content> |
||||
<v-list-item-title> |
||||
<router-link :to="{ name: 'CourseList' }">Courses</router-link> |
||||
</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
<v-list-item> |
||||
<v-list-item-action> |
||||
<v-icon>mdi-book</v-icon> |
||||
</v-list-item-action> |
||||
<v-list-item-content> |
||||
<v-list-item-title> |
||||
<router-link :to="{ name: 'CourseCategoryList' }">Courses category</router-link> |
||||
</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
|
||||
<v-list-item> |
||||
<v-list-item-action> |
||||
<v-icon>mdi-comment-quote</v-icon> |
||||
</v-list-item-action> |
||||
<v-list-item-content> |
||||
<v-list-item-title> |
||||
<router-link :to="{ name: 'ReviewList' }">Reviews</router-link> |
||||
</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
</v-list> |
||||
</v-navigation-drawer> |
||||
<v-app-bar app color="indigo" dark> |
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon> |
||||
<v-toolbar-title>Application</v-toolbar-title> |
||||
</v-app-bar> |
||||
|
||||
<v-content> |
||||
<Breadcrumb layout-class="pl-3 py-3" /> |
||||
<router-view></router-view> |
||||
</v-content> |
||||
<v-footer color="indigo" app> |
||||
<span class="white--text">© 2019</span> |
||||
</v-footer> |
||||
</v-app> |
||||
</template> |
||||
|
||||
<script> |
||||
import Breadcrumb from './components/Breadcrumb'; |
||||
import Snackbar from './components/Snackbar'; |
||||
|
||||
export default { |
||||
name: "App", |
||||
components: { |
||||
Breadcrumb, |
||||
Snackbar |
||||
}, |
||||
data: () => ({ |
||||
drawer: null |
||||
}), |
||||
beforeMount() { |
||||
} |
||||
} |
||||
</script> |
@ -0,0 +1,3 @@ |
||||
export const VueConfig = { |
||||
delimiters: ['[[', ']]'] |
||||
}; |
@ -0,0 +1,45 @@ |
||||
<template> |
||||
<div> |
||||
<v-row justify="space-around"> |
||||
<v-icon v-if="handleShow" small class="mr-2" @click="handleShow">mdi-eye</v-icon> |
||||
<v-icon v-if="handleEdit" small class="mr-2" @click="handleEdit">mdi-pencil</v-icon> |
||||
<v-icon v-if="handleDelete" small @click="confirmDelete = true">mdi-delete</v-icon> |
||||
</v-row> |
||||
<ConfirmDelete |
||||
v-if="handleDelete" |
||||
:visible="confirmDelete" |
||||
:handle-delete="handleDelete" |
||||
@close="confirmDelete = false" |
||||
/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import ConfirmDelete from './ConfirmDelete'; |
||||
|
||||
export default { |
||||
name: 'ActionCell', |
||||
components: { |
||||
ConfirmDelete |
||||
}, |
||||
data() { |
||||
return { |
||||
confirmDelete: false |
||||
}; |
||||
}, |
||||
props: { |
||||
handleShow: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleEdit: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleDelete: { |
||||
type: Function, |
||||
required: false |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,40 @@ |
||||
<template> |
||||
<div> |
||||
<v-breadcrumbs :items="items" divider="/" :class="layoutClass" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'Breadcrumb', |
||||
props: ['layoutClass'], |
||||
data() { |
||||
return {}; |
||||
}, |
||||
computed: { |
||||
items() { |
||||
const { path, matched } = this.$route; |
||||
const items = [ |
||||
{ |
||||
text: 'Home', |
||||
href: '/' |
||||
} |
||||
]; |
||||
const lastItem = matched[matched.length - 1]; |
||||
for (let i = 0, len = matched.length; i < len; i += 1) { |
||||
const route = matched[i]; |
||||
|
||||
if (route.path) { |
||||
items.push({ |
||||
text: route.name, |
||||
disabled: route.path === path || lastItem.path === route.path, |
||||
href: route.path |
||||
}); |
||||
} |
||||
} |
||||
|
||||
return items; |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,45 @@ |
||||
<template> |
||||
<v-dialog v-model="show" persistent width="300"> |
||||
<v-card> |
||||
<v-card-text>{{ $t('Are you sure you want to delete this item?') }}</v-card-text> |
||||
<v-card-actions> |
||||
<v-spacer></v-spacer> |
||||
<v-btn color="error darken-1" @click="handleDelete"> |
||||
{{ $t('Delete') }} |
||||
</v-btn> |
||||
<v-btn color="secondary darken-1" text @click.stop="show = false"> |
||||
{{ $t('Cancel') }} |
||||
</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
</v-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'ConfirmDelete', |
||||
props: { |
||||
visible: { |
||||
type: Boolean, |
||||
required: true, |
||||
default: () => false |
||||
}, |
||||
handleDelete: { |
||||
type: Function, |
||||
required: true |
||||
} |
||||
}, |
||||
computed: { |
||||
show: { |
||||
get() { |
||||
return this.visible; |
||||
}, |
||||
set(value) { |
||||
if (!value) { |
||||
this.$emit('close'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,40 @@ |
||||
<template> |
||||
<v-expansion-panels v-model="filtersExpanded"> |
||||
<v-expansion-panel> |
||||
<v-expansion-panel-header> |
||||
{{ $t('Filters') }} |
||||
|
||||
<template slot="actions"> |
||||
<v-icon large>mdi-filter-variant</v-icon> |
||||
</template> |
||||
</v-expansion-panel-header> |
||||
<v-expansion-panel-content> |
||||
<slot name="filter"></slot> |
||||
|
||||
<v-btn color="primary" @click="handleFilter">{{ $t('Filter')}}</v-btn> |
||||
<v-btn color="primary" class="ml-2" text @click="handleReset">{{ $t('Reset') }}</v-btn> |
||||
</v-expansion-panel-content> |
||||
</v-expansion-panel> |
||||
</v-expansion-panels> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'DataFilter', |
||||
props: { |
||||
handleReset: { |
||||
type: Function, |
||||
required: true |
||||
}, |
||||
handleFilter: { |
||||
type: Function, |
||||
required: true |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
filtersExpanded: false |
||||
}; |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,52 @@ |
||||
<template> |
||||
<v-menu |
||||
v-model="showMenu" |
||||
:close-on-content-click="false" |
||||
:nudge-right="40" |
||||
transition="scale-transition" |
||||
offset-y |
||||
min-width="290px" |
||||
> |
||||
<template v-slot:activator="{ on }"> |
||||
<v-text-field |
||||
v-model="date" |
||||
:label="label" |
||||
prepend-icon="mdi-calendar" |
||||
readonly |
||||
v-on="on" |
||||
></v-text-field> |
||||
</template> |
||||
<v-date-picker v-model="date" @input="handleInput"></v-date-picker> |
||||
</v-menu> |
||||
</template> |
||||
|
||||
<script> |
||||
import { formatDateTime } from '../utils/dates'; |
||||
|
||||
export default { |
||||
props: { |
||||
label: { |
||||
type: String, |
||||
required: false, |
||||
default: () => '' |
||||
}, |
||||
value: String |
||||
}, |
||||
created() { |
||||
this.date = this.value ? this.value : this.date; |
||||
}, |
||||
data() { |
||||
return { |
||||
date: this.value ? this.value : new Date().toISOString().substr(0, 10), |
||||
showMenu: false |
||||
}; |
||||
}, |
||||
methods: { |
||||
formatDateTime, |
||||
handleInput() { |
||||
this.showMenu = false; |
||||
this.$emit('input', this.date); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,18 @@ |
||||
<template> |
||||
<div class="text-center"> |
||||
<v-overlay :value="visible"> |
||||
<v-progress-circular indeterminate size="64"></v-progress-circular> |
||||
</v-overlay> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
props: { |
||||
visible: { |
||||
type: Boolean, |
||||
required: true |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,32 @@ |
||||
<template> |
||||
<v-snackbar |
||||
v-model="show" |
||||
:color="color" |
||||
:multi-line="true" |
||||
:timeout="timeout" |
||||
right |
||||
top |
||||
> |
||||
{{ text }} |
||||
<template v-if="subText"> |
||||
<p>{{ subText }}</p> |
||||
</template> |
||||
<v-btn dark text @click.native="close">{{ $t('Close') }}</v-btn> |
||||
</v-snackbar> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
computed: { |
||||
...mapFields('notifications', ['color', 'show', 'subText', 'text', 'timeout']) |
||||
}, |
||||
|
||||
methods: { |
||||
close() { |
||||
this.show = false; |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,137 @@ |
||||
<template> |
||||
<v-toolbar class="my-md-auto" elevation="0"> |
||||
<slot name="left"></slot> |
||||
<v-spacer /> |
||||
<div> |
||||
<v-btn |
||||
v-if="handleList" |
||||
:loading="isLoading" |
||||
color="primary" |
||||
@click="listItem" |
||||
> |
||||
{{ $t('List') }} |
||||
</v-btn> |
||||
<v-btn |
||||
v-if="handleEdit" |
||||
:loading="isLoading" |
||||
color="primary" |
||||
@click="editItem" |
||||
> |
||||
{{ $t('Edit') }} |
||||
</v-btn> |
||||
<v-btn |
||||
v-if="handleSubmit" |
||||
:loading="isLoading" |
||||
color="primary" |
||||
@click="submitItem" |
||||
> |
||||
<v-icon left>mdi-content-save</v-icon> |
||||
{{ $t('Submit') }} |
||||
</v-btn> |
||||
<v-btn |
||||
v-if="handleReset" |
||||
color="primary" |
||||
class="ml-sm-2" |
||||
@click="resetItem" |
||||
> |
||||
{{ $t('Reset') }} |
||||
</v-btn> |
||||
<v-btn |
||||
v-if="handleDelete" |
||||
color="error" |
||||
class="ml-sm-2" |
||||
@click="confirmDelete = true" |
||||
> |
||||
{{ $t('Delete') }} |
||||
</v-btn> |
||||
|
||||
<v-btn v-if="handleAdd" color="primary" rounded @click="addItem"> |
||||
<v-icon>mdi-plus-circle</v-icon> |
||||
</v-btn> |
||||
</div> |
||||
<ConfirmDelete |
||||
v-if="handleDelete" |
||||
:visible="confirmDelete" |
||||
:handle-delete="handleDelete" |
||||
@close="confirmDelete = false" |
||||
/> |
||||
</v-toolbar> |
||||
</template> |
||||
|
||||
<script> |
||||
import ConfirmDelete from './ConfirmDelete'; |
||||
|
||||
export default { |
||||
name: 'Toolbar', |
||||
components: { |
||||
ConfirmDelete |
||||
}, |
||||
data() { |
||||
return { |
||||
confirmDelete: false |
||||
}; |
||||
}, |
||||
props: { |
||||
handleList: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleEdit: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleSubmit: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleReset: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleDelete: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
handleAdd: { |
||||
type: Function, |
||||
required: false |
||||
}, |
||||
title: { |
||||
type: String, |
||||
required: false |
||||
}, |
||||
isLoading: { |
||||
type: Boolean, |
||||
required: false, |
||||
default: () => false |
||||
} |
||||
}, |
||||
methods: { |
||||
listItem() { |
||||
if (this.handleList) { |
||||
this.handleList(); |
||||
} |
||||
}, |
||||
addItem() { |
||||
if (this.handleAdd) { |
||||
this.handleAdd(); |
||||
} |
||||
}, |
||||
editItem() { |
||||
if (this.handleEdit) { |
||||
this.handleEdit(); |
||||
} |
||||
}, |
||||
submitItem() { |
||||
if (this.handleSubmit) { |
||||
this.handleSubmit(); |
||||
} |
||||
}, |
||||
resetItem() { |
||||
if (this.handleReset) { |
||||
this.handleReset(); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,76 @@ |
||||
<template> |
||||
<v-container fluid> |
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.title" |
||||
:label="$t('title')" |
||||
type="text" |
||||
/> |
||||
</v-col> |
||||
|
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.code" |
||||
:label="$t('code')" |
||||
type="text" |
||||
/> |
||||
</v-col> |
||||
</v-row> |
||||
|
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-combobox |
||||
v-model="item.category" |
||||
:items="categorySelectItems" |
||||
:no-data-text="$t('No results')" |
||||
:label="$t('category')" |
||||
item-text="name" |
||||
item-value="@id" |
||||
chips |
||||
/> |
||||
</v-col> |
||||
|
||||
<v-row cols="12"></v-row> |
||||
</v-row> |
||||
|
||||
</v-container> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
name: 'CourseFilter', |
||||
props: { |
||||
values: { |
||||
type: Object, |
||||
required: true |
||||
} |
||||
}, |
||||
data() { |
||||
return {}; |
||||
}, |
||||
mounted() { |
||||
this.categoryGetSelectItems(); |
||||
}, |
||||
methods: { |
||||
...mapActions({ |
||||
categoryGetSelectItems: 'coursecategory/fetchSelectItems' |
||||
}), |
||||
}, |
||||
|
||||
computed: { |
||||
...mapFields('coursecategory', { |
||||
categorySelectItems: 'selectItems' |
||||
}), |
||||
|
||||
// eslint-disable-next-line |
||||
item() { |
||||
return this.initialValues || this.values; |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,173 @@ |
||||
<template> |
||||
<v-form> |
||||
<v-container fluid> |
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.title" |
||||
:error-messages="titleErrors" |
||||
:label="$t('title')" |
||||
required |
||||
@input="$v.item.title.$touch()" |
||||
@blur="$v.item.title.$touch()" |
||||
/> |
||||
</v-col> |
||||
|
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.code" |
||||
:error-messages="codeErrors" |
||||
:label="$t('code')" |
||||
required |
||||
@input="$v.item.code.$touch()" |
||||
@blur="$v.item.code.$touch()" |
||||
/> |
||||
</v-col> |
||||
</v-row> |
||||
|
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-combobox |
||||
v-model="item.category" |
||||
:items="categorySelectItems" |
||||
:error-messages="categoryErrors" |
||||
:no-data-text="$t('No results')" |
||||
:label="$t('category')" |
||||
item-text="name" |
||||
item-value="@id" |
||||
/> |
||||
</v-col> |
||||
|
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model.number="item.visibility" |
||||
:error-messages="visibilityErrors" |
||||
:label="$t('visibility')" |
||||
required |
||||
@input="$v.item.visibility.$touch()" |
||||
@blur="$v.item.visibility.$touch()" |
||||
/> |
||||
</v-col> |
||||
</v-row> |
||||
|
||||
</v-container> |
||||
</v-form> |
||||
</template> |
||||
|
||||
<script> |
||||
import has from 'lodash/has'; |
||||
import { validationMixin } from 'vuelidate'; |
||||
import { required } from 'vuelidate/lib/validators'; |
||||
import { mapActions } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
name: 'CourseForm', |
||||
mixins: [validationMixin], |
||||
props: { |
||||
values: { |
||||
type: Object, |
||||
required: true |
||||
}, |
||||
|
||||
errors: { |
||||
type: Object, |
||||
default: () => {} |
||||
}, |
||||
|
||||
initialValues: { |
||||
type: Object, |
||||
default: () => {} |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
title: null, |
||||
code: null, |
||||
category: null, |
||||
visibility: null, |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapFields('coursecategory', { |
||||
categorySelectItems: 'selectItems' |
||||
}), |
||||
|
||||
// eslint-disable-next-line |
||||
item() { |
||||
return this.initialValues || this.values; |
||||
}, |
||||
|
||||
titleErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.title.$dirty) return errors; |
||||
|
||||
has(this.violations, 'title') && errors.push(this.violations.title); |
||||
|
||||
!this.$v.item.title.required && errors.push(this.$t('Field is required')); |
||||
|
||||
return errors; |
||||
}, |
||||
codeErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.code.$dirty) return errors; |
||||
|
||||
has(this.violations, 'code') && errors.push(this.violations.code); |
||||
|
||||
!this.$v.item.code.required && errors.push(this.$t('Field is required')); |
||||
|
||||
return errors; |
||||
}, |
||||
categoryErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.category.$dirty) return errors; |
||||
|
||||
has(this.violations, 'category') && errors.push(this.violations.category); |
||||
|
||||
|
||||
return errors; |
||||
}, |
||||
visibilityErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.visibility.$dirty) return errors; |
||||
|
||||
has(this.violations, 'visibility') && errors.push(this.violations.visibility); |
||||
|
||||
!this.$v.item.visibility.required && errors.push(this.$t('Field is required')); |
||||
|
||||
return errors; |
||||
}, |
||||
|
||||
violations() { |
||||
return this.errors || {}; |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.categoryGetSelectItems(); |
||||
}, |
||||
methods: { |
||||
...mapActions({ |
||||
categoryGetSelectItems: 'coursecategory/fetchSelectItems' |
||||
}), |
||||
}, |
||||
validations: { |
||||
item: { |
||||
title: { |
||||
required, |
||||
}, |
||||
code: { |
||||
required, |
||||
}, |
||||
category: { |
||||
}, |
||||
visibility: { |
||||
required, |
||||
}, |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,9 @@ |
||||
<template> |
||||
<router-view></router-view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'CourseLayout' |
||||
} |
||||
</script> |
@ -0,0 +1,40 @@ |
||||
<template> |
||||
<v-container fluid> |
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.name" |
||||
:label="$t('name')" |
||||
type="text" |
||||
/> |
||||
</v-col> |
||||
</v-row> |
||||
</v-container> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
export default { |
||||
name: 'CourseCategoryFilter', |
||||
props: { |
||||
values: { |
||||
type: Object, |
||||
required: true |
||||
} |
||||
}, |
||||
data() { |
||||
return {}; |
||||
}, |
||||
mounted() { |
||||
}, |
||||
|
||||
computed: { |
||||
// eslint-disable-next-line |
||||
item() { |
||||
return this.initialValues || this.values; |
||||
} |
||||
}, |
||||
methods: { |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,113 @@ |
||||
<template> |
||||
<v-form> |
||||
<v-container fluid> |
||||
<v-row> |
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.name" |
||||
:error-messages="nameErrors" |
||||
:label="$t('name')" |
||||
required |
||||
@input="$v.item.name.$touch()" |
||||
@blur="$v.item.name.$touch()" |
||||
/> |
||||
</v-col> |
||||
|
||||
<v-col cols="12" sm="6" md="6"> |
||||
<v-text-field |
||||
v-model="item.code" |
||||
:error-messages="codeErrors" |
||||
:label="$t('code')" |
||||
required |
||||
@input="$v.item.code.$touch()" |
||||
@blur="$v.item.code.$touch()" |
||||
/> |
||||
</v-col> |
||||
|
||||
</v-row> |
||||
|
||||
</v-container> |
||||
</v-form> |
||||
</template> |
||||
|
||||
<script> |
||||
import has from 'lodash/has'; |
||||
import { validationMixin } from 'vuelidate'; |
||||
import { required } from 'vuelidate/lib/validators'; |
||||
import { mapActions } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
name: 'CourseCategoryForm', |
||||
mixins: [validationMixin], |
||||
props: { |
||||
values: { |
||||
type: Object, |
||||
required: true |
||||
}, |
||||
|
||||
errors: { |
||||
type: Object, |
||||
default: () => {} |
||||
}, |
||||
|
||||
initialValues: { |
||||
type: Object, |
||||
default: () => {} |
||||
} |
||||
}, |
||||
mounted() { |
||||
}, |
||||
data() { |
||||
return { |
||||
}; |
||||
}, |
||||
computed: { |
||||
|
||||
// eslint-disable-next-line |
||||
item() { |
||||
return this.initialValues || this.values; |
||||
}, |
||||
|
||||
nameErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.name.$dirty) return errors; |
||||
|
||||
has(this.violations, 'name') && errors.push(this.violations.name); |
||||
|
||||
!this.$v.item.name.required && errors.push(this.$t('Field is required')); |
||||
|
||||
return errors; |
||||
}, |
||||
codeErrors() { |
||||
const errors = []; |
||||
|
||||
if (!this.$v.item.code.$dirty) return errors; |
||||
|
||||
has(this.violations, 'code') && errors.push(this.violations.code); |
||||
|
||||
!this.$v.item.code.required && errors.push(this.$t('Field is required')); |
||||
|
||||
return errors; |
||||
}, |
||||
|
||||
|
||||
violations() { |
||||
return this.errors || {}; |
||||
} |
||||
}, |
||||
methods: { |
||||
}, |
||||
validations: { |
||||
item: { |
||||
name: { |
||||
required, |
||||
}, |
||||
code: { |
||||
required, |
||||
}, |
||||
} |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,9 @@ |
||||
<template> |
||||
<router-view></router-view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'CourseCategoryLayout' |
||||
} |
||||
</script> |
@ -0,0 +1,10 @@ |
||||
export default class SubmissionError extends Error { |
||||
constructor (errors) { |
||||
super('Submit Validation Failed'); |
||||
this.errors = errors; |
||||
//Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name; |
||||
|
||||
return this; |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import Vue from 'vue'; |
||||
import VueI18n from 'vue-i18n'; |
||||
import messages from './locales/en'; |
||||
|
||||
Vue.use(VueI18n); |
||||
|
||||
export default new VueI18n({ |
||||
locale: process.env.VUE_APP_I18N_LOCALE || 'en', |
||||
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', |
||||
messages: { |
||||
en: messages |
||||
} |
||||
}); |
@ -0,0 +1,22 @@ |
||||
export default { |
||||
'Submit': 'Submit', |
||||
'Reset': 'Reset', |
||||
'Delete': 'Delete', |
||||
'Edit': 'Edit', |
||||
'Are you sure you want to delete this item?': 'Are you sure you want to delete this item?', |
||||
'No results': 'No results', |
||||
'Close': 'Close', |
||||
'Cancel': 'Cancel', |
||||
'Updated': 'Updated', |
||||
'Field': 'Field', |
||||
'Value': 'Value', |
||||
'Filters': 'Filters', |
||||
'Filter': 'Filter', |
||||
'Data unavailable': 'Data unavailable', |
||||
'Loading...': 'Loading...', |
||||
'Deleted': 'Deleted', |
||||
'Please, insert a value bigger than zero!': 'Please, insert a value bigger than zero!', |
||||
'Please type something': 'Please type something', |
||||
'Field is required': 'Field is required', |
||||
'Records per page:': 'Records per page:', |
||||
}; |
@ -0,0 +1,67 @@ |
||||
import Vue from "vue"; |
||||
import App from "./App"; |
||||
import router from "./router"; |
||||
import store from "./store"; |
||||
import courseCategoryService from './services/coursecategory'; |
||||
import courseService from './services/course'; |
||||
import makeCrudModule from './store/modules/crud'; |
||||
|
||||
// import '@mdi/font/css/materialdesignicons.css'
|
||||
|
||||
/*router.beforeEach((to, from, next) => { |
||||
// hack to allow for forward slashes in path ids
|
||||
if (to.fullPath.includes('%2F')) { |
||||
next(to.fullPath.replace('%2F', '/')); |
||||
} |
||||
next(); |
||||
});*/ |
||||
|
||||
import vuetify from './plugins/vuetify' // path to vuetify export
|
||||
|
||||
import ApolloClient from 'apollo-boost' |
||||
const apolloClient = new ApolloClient({ |
||||
// You should use an absolute URL here
|
||||
uri: '/api/graphql/' |
||||
}) |
||||
|
||||
import VueApollo from 'vue-apollo'; |
||||
Vue.use(VueApollo); |
||||
|
||||
import Vuelidate from 'vuelidate'; |
||||
import i18n from './i18n'; |
||||
Vue.config.productionTip = false; |
||||
Vue.use(Vuelidate); |
||||
|
||||
const apolloProvider = new VueApollo({ |
||||
defaultClient: apolloClient, |
||||
}); |
||||
|
||||
//import './quasar'
|
||||
|
||||
store.registerModule( |
||||
'course', |
||||
makeCrudModule({ |
||||
service: courseService |
||||
}) |
||||
); |
||||
|
||||
store.registerModule( |
||||
'coursecategory', |
||||
makeCrudModule({ |
||||
service: courseCategoryService |
||||
}) |
||||
); |
||||
|
||||
Vue.config.productionTip = false; |
||||
|
||||
new Vue({ |
||||
vuetify, |
||||
i18n, |
||||
components: {App}, |
||||
apolloProvider, |
||||
data: {}, |
||||
store, |
||||
router, |
||||
render: h => h(App) |
||||
}). |
||||
$mount("#app"); |
@ -0,0 +1,41 @@ |
||||
import NotificationMixin from './NotificationMixin'; |
||||
import { formatDateTime } from '../utils/dates'; |
||||
|
||||
export default { |
||||
mixins: [NotificationMixin], |
||||
methods: { |
||||
formatDateTime, |
||||
onCreated(item) { |
||||
this.showMessage(`${item['@id']} created`); |
||||
|
||||
this.$router.push({ |
||||
name: `${this.$options.servicePrefix}Update`, |
||||
params: { id: item['@id'] } |
||||
}); |
||||
}, |
||||
onSendForm() { |
||||
const createForm = this.$refs.createForm; |
||||
createForm.$v.$touch(); |
||||
if (!createForm.$v.$invalid) { |
||||
this.create(createForm.$v.item.$model); |
||||
} |
||||
}, |
||||
resetForm() { |
||||
this.$refs.createForm.$v.$reset(); |
||||
this.item = {}; |
||||
} |
||||
}, |
||||
watch: { |
||||
created(created) { |
||||
if (!created) { |
||||
return; |
||||
} |
||||
|
||||
this.onCreated(created); |
||||
}, |
||||
|
||||
error(message) { |
||||
message && this.showError(message); |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,88 @@ |
||||
import isEmpty from 'lodash/isEmpty'; |
||||
import { formatDateTime } from '../utils/dates'; |
||||
import NotificationMixin from './NotificationMixin'; |
||||
|
||||
export default { |
||||
mixins: [NotificationMixin], |
||||
|
||||
data() { |
||||
return { |
||||
options: { |
||||
sortBy: [], |
||||
descending: false, |
||||
page: 1, |
||||
itemsPerPage: 15 |
||||
}, |
||||
filters: {} |
||||
}; |
||||
}, |
||||
|
||||
watch: { |
||||
deletedItem(item) { |
||||
this.showMessage(`${item['@id']} deleted.`); |
||||
}, |
||||
|
||||
error(message) { |
||||
message && this.showError(message); |
||||
}, |
||||
|
||||
items() { |
||||
this.options.totalItems = this.totalItems; |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
onUpdateOptions(props) { |
||||
const { page, itemsPerPage, sortBy, descending, totalItems } = props; |
||||
let params = { |
||||
...this.filters |
||||
}; |
||||
if (itemsPerPage > 0) { |
||||
params = { ...params, itemsPerPage, page }; |
||||
} |
||||
|
||||
if (!isEmpty(sortBy)) { |
||||
params[`order[${sortBy}]`] = descending ? 'desc' : 'asc'; |
||||
} |
||||
|
||||
this.getPage(params).then(() => { |
||||
this.options.sortBy = sortBy; |
||||
this.options.descending = descending; |
||||
this.options.itemsPerPage = itemsPerPage; |
||||
this.options.totalItems = totalItems; |
||||
}); |
||||
}, |
||||
|
||||
onSendFilter() { |
||||
this.resetList = true; |
||||
this.onUpdateOptions(this.options); |
||||
}, |
||||
|
||||
resetFilter() { |
||||
this.filters = {}; |
||||
}, |
||||
|
||||
addHandler() { |
||||
this.$router.push({ name: `${this.$options.servicePrefix}Create` }); |
||||
}, |
||||
|
||||
showHandler(item) { |
||||
this.$router.push({ |
||||
name: `${this.$options.servicePrefix}Show`, |
||||
params: { id: item['@id'] } |
||||
}); |
||||
}, |
||||
|
||||
editHandler(item) { |
||||
this.$router.push({ |
||||
name: `${this.$options.servicePrefix}Update`, |
||||
params: { id: item['@id'] } |
||||
}); |
||||
}, |
||||
|
||||
deleteHandler(item) { |
||||
this.deleteItem(item).then(() => this.onUpdateOptions(this.options)); |
||||
}, |
||||
formatDateTime |
||||
} |
||||
}; |
@ -0,0 +1,37 @@ |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
computed: { |
||||
...mapFields('notifications', ['color', 'show', 'subText', 'text', 'timeout']) |
||||
}, |
||||
|
||||
methods: { |
||||
cleanState() { |
||||
setTimeout(() => { |
||||
this.show = false; |
||||
}, this.timeout); |
||||
}, |
||||
|
||||
showError(error) { |
||||
this.showMessage(error, 'danger'); |
||||
}, |
||||
|
||||
showMessage(message, color = 'success') { |
||||
this.show = true; |
||||
this.color = color; |
||||
|
||||
if (typeof message === 'string') { |
||||
this.text = message; |
||||
this.cleanState(); |
||||
|
||||
return; |
||||
} |
||||
|
||||
this.text = message.message; |
||||
|
||||
if (message.response) this.subText = message.response.data.message; |
||||
|
||||
this.cleanState(); |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,47 @@ |
||||
import NotificationMixin from './NotificationMixin'; |
||||
import { formatDateTime } from '../utils/dates'; |
||||
|
||||
export default { |
||||
mixins: [NotificationMixin], |
||||
created() { |
||||
this.retrieve(decodeURIComponent(this.$route.params.id)); |
||||
}, |
||||
computed: { |
||||
item() { |
||||
return this.find(decodeURIComponent(this.$route.params.id)); |
||||
} |
||||
}, |
||||
methods: { |
||||
list() { |
||||
this.$router |
||||
.push({ name: `${this.$options.servicePrefix}List` }) |
||||
.catch(() => {}); |
||||
}, |
||||
del() { |
||||
this.deleteItem(this.item).then(() => { |
||||
this.showMessage(`${this.item['@id']} deleted.`); |
||||
this.$router |
||||
.push({ name: `${this.$options.servicePrefix}List` }) |
||||
.catch(() => {}); |
||||
}); |
||||
}, |
||||
formatDateTime, |
||||
editHandler() { |
||||
this.$router.push({ |
||||
name: `${this.$options.servicePrefix}Update`, |
||||
params: { id: this.item['@id'] } |
||||
}); |
||||
} |
||||
}, |
||||
watch: { |
||||
error(message) { |
||||
message && this.showError(message); |
||||
}, |
||||
deleteError(message) { |
||||
message && this.showError(message); |
||||
} |
||||
}, |
||||
beforeDestroy() { |
||||
this.reset(); |
||||
} |
||||
}; |
@ -0,0 +1,79 @@ |
||||
import NotificationMixin from './NotificationMixin'; |
||||
import { formatDateTime } from '../utils/dates'; |
||||
|
||||
export default { |
||||
mixins: [NotificationMixin], |
||||
data() { |
||||
return { |
||||
item: {} |
||||
}; |
||||
}, |
||||
created() { |
||||
this.retrieve(decodeURIComponent(this.$route.params.id)); |
||||
}, |
||||
beforeDestroy() { |
||||
this.reset(); |
||||
}, |
||||
computed: { |
||||
retrieved() { |
||||
return this.find(decodeURIComponent(this.$route.params.id)); |
||||
} |
||||
}, |
||||
methods: { |
||||
del() { |
||||
this.deleteItem(this.retrieved).then(() => { |
||||
this.showMessage(`${this.item['@id']} deleted.`); |
||||
this.$router |
||||
.push({ name: `${this.$options.servicePrefix}List` }) |
||||
.catch(() => {}); |
||||
}); |
||||
}, |
||||
formatDateTime, |
||||
reset() { |
||||
this.$refs.updateForm.$v.$reset(); |
||||
this.updateReset(); |
||||
this.delReset(); |
||||
this.createReset(); |
||||
}, |
||||
|
||||
onSendForm() { |
||||
const updateForm = this.$refs.updateForm; |
||||
updateForm.$v.$touch(); |
||||
|
||||
if (!updateForm.$v.$invalid) { |
||||
this.update(updateForm.$v.item.$model); |
||||
} |
||||
}, |
||||
|
||||
resetForm() { |
||||
this.$refs.updateForm.$v.$reset(); |
||||
this.item = { ...this.retrieved }; |
||||
} |
||||
}, |
||||
watch: { |
||||
deleted(deleted) { |
||||
if (!deleted) { |
||||
return; |
||||
} |
||||
this.$router |
||||
.push({ name: `${this.$options.servicePrefix}List` }) |
||||
.catch(() => {}); |
||||
}, |
||||
|
||||
error(message) { |
||||
message && this.showError(message); |
||||
}, |
||||
|
||||
deleteError(message) { |
||||
message && this.showError(message); |
||||
}, |
||||
|
||||
updated(val) { |
||||
this.showMessage(`${val['@id']} updated.`); |
||||
}, |
||||
|
||||
retrieved(val) { |
||||
this.item = { ...val }; |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,15 @@ |
||||
// src/plugins/vuetify.js
|
||||
|
||||
import Vue from 'vue' |
||||
import Vuetify from 'vuetify' |
||||
import 'vuetify/dist/vuetify.min.css' |
||||
|
||||
Vue.use(Vuetify) |
||||
|
||||
const opts = { |
||||
icons: { |
||||
iconfont: 'mdi' |
||||
} |
||||
}; |
||||
|
||||
export default new Vuetify(opts) |
@ -0,0 +1,5 @@ |
||||
import Vue from 'vue' |
||||
|
||||
// import './styles/quasar.sass'
|
||||
import '@quasar/extras/material-icons/material-icons.css' |
||||
import Quasar from 'quasar/dist/quasar.umd.js' |
@ -0,0 +1,28 @@ |
||||
export default { |
||||
path: '/courses', |
||||
name: 'courses', |
||||
component: () => import('../components/course/Layout'), |
||||
redirect: { name: 'CourseList' }, |
||||
children: [ |
||||
{ |
||||
name: 'CourseList', |
||||
path: '', |
||||
component: () => import('../views/course/List') |
||||
}, |
||||
{ |
||||
name: 'CourseCreate', |
||||
path: 'new', |
||||
component: () => import('../views/course/Create') |
||||
}, |
||||
{ |
||||
name: 'CourseUpdate', |
||||
path: ':id/edit', |
||||
component: () => import('../views/course/Update') |
||||
}, |
||||
{ |
||||
name: 'CourseShow', |
||||
path: ':id', |
||||
component: () => import('../views/course/Show') |
||||
} |
||||
] |
||||
}; |
@ -0,0 +1,28 @@ |
||||
export default { |
||||
path: '/course_categories', |
||||
name: 'course_categories', |
||||
component: () => import('../components/coursecategory/Layout'), |
||||
redirect: { name: 'CourseCategoryList' }, |
||||
children: [ |
||||
{ |
||||
name: 'CourseCategoryList', |
||||
path: '', |
||||
component: () => import('../views/coursecategory/List') |
||||
}, |
||||
{ |
||||
name: 'CourseCategoryCreate', |
||||
path: 'new', |
||||
component: () => import('../views/coursecategory/Create') |
||||
}, |
||||
{ |
||||
name: 'CourseCategoryUpdate', |
||||
path: ':id/edit', |
||||
component: () => import('../views/coursecategory/Update') |
||||
}, |
||||
{ |
||||
name: 'CourseCategoryShow', |
||||
path: ':id', |
||||
component: () => import('../views/coursecategory/Show') |
||||
} |
||||
] |
||||
}; |
@ -0,0 +1,18 @@ |
||||
import Vue from "vue"; |
||||
import VueRouter from "vue-router"; |
||||
|
||||
Vue.use(VueRouter); |
||||
|
||||
import courseRoutes from './course'; |
||||
import coursecategoryRoutes from './coursecategory'; |
||||
import sessionRoutes from './../../quasar/router/session'; |
||||
|
||||
export default new VueRouter({ |
||||
mode: "history", |
||||
routes: [ |
||||
courseRoutes, |
||||
...sessionRoutes, |
||||
coursecategoryRoutes, |
||||
// { path: "*", redirect: "/home" }
|
||||
] |
||||
}); |
@ -0,0 +1,24 @@ |
||||
import fetch from '../utils/fetch'; |
||||
|
||||
export default function makeService(endpoint) { |
||||
return { |
||||
find(id) { |
||||
return fetch(`${id}`); |
||||
}, |
||||
findAll(params) { |
||||
return fetch(endpoint, params); |
||||
}, |
||||
create(payload) { |
||||
return fetch(endpoint, { method: 'POST', body: JSON.stringify(payload) }); |
||||
}, |
||||
del(item) { |
||||
return fetch(item['@id'], { method: 'DELETE' }); |
||||
}, |
||||
update(payload) { |
||||
return fetch(payload['@id'], { |
||||
method: 'PUT', |
||||
body: JSON.stringify(payload) |
||||
}); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,3 @@ |
||||
import makeService from './api'; |
||||
|
||||
export default makeService('courses'); |
@ -0,0 +1,3 @@ |
||||
import makeService from './api'; |
||||
|
||||
export default makeService('course_categories'); |
@ -0,0 +1,14 @@ |
||||
import Vue from "vue"; |
||||
import Vuex from "vuex"; |
||||
|
||||
import notifications from './modules/notifications'; |
||||
//import session from './../../quasar/store/modules/session/';
|
||||
|
||||
Vue.use(Vuex); |
||||
|
||||
export default new Vuex.Store({ |
||||
modules: { |
||||
notifications, |
||||
//session,
|
||||
} |
||||
}); |
@ -0,0 +1,273 @@ |
||||
import Vue from 'vue'; |
||||
import { getField, updateField } from 'vuex-map-fields'; |
||||
import remove from 'lodash/remove'; |
||||
import SubmissionError from '../../error/SubmissionError'; |
||||
|
||||
const initialState = () => ({ |
||||
allIds: [], |
||||
byId: {}, |
||||
created: null, |
||||
deleted: null, |
||||
error: "", |
||||
isLoading: false, |
||||
resetList: false, |
||||
selectItems: null, |
||||
totalItems: 0, |
||||
updated: null, |
||||
view: null, |
||||
violations: null |
||||
}); |
||||
|
||||
const handleError = (commit, e) => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
if (e instanceof SubmissionError) { |
||||
commit(ACTIONS.SET_VIOLATIONS, e.errors); |
||||
// eslint-disable-next-line
|
||||
commit(ACTIONS.SET_ERROR, e.errors._error); |
||||
|
||||
return Promise.reject(e); |
||||
} |
||||
|
||||
// eslint-disable-next-line
|
||||
commit(ACTIONS.SET_ERROR, e.message); |
||||
|
||||
return Promise.reject(e); |
||||
}; |
||||
|
||||
export const ACTIONS = { |
||||
ADD: 'ADD', |
||||
RESET_CREATE: 'RESET_CREATE', |
||||
RESET_DELETE: 'RESET_DELETE', |
||||
RESET_LIST: 'RESET_LIST', |
||||
RESET_SHOW: 'RESET_SHOW', |
||||
RESET_UPDATE: 'RESET_UPDATE', |
||||
SET_CREATED: 'SET_CREATED', |
||||
SET_DELETED: 'SET_DELETED', |
||||
SET_ERROR: 'SET_ERROR', |
||||
SET_SELECT_ITEMS: 'SET_SELECT_ITEMS', |
||||
SET_TOTAL_ITEMS: 'SET_TOTAL_ITEMS', |
||||
SET_UPDATED: 'SET_UPDATED', |
||||
SET_VIEW: 'SET_VIEW', |
||||
SET_VIOLATIONS: 'SET_VIOLATIONS', |
||||
TOGGLE_LOADING: 'TOGGLE_LOADING' |
||||
}; |
||||
|
||||
export default function makeCrudModule({ |
||||
normalizeRelations = x => x, |
||||
resolveRelations = x => x, |
||||
service |
||||
} = {}) { |
||||
return { |
||||
actions: { |
||||
create: ({ commit }, values) => { |
||||
commit(ACTIONS.SET_ERROR, ''); |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
service |
||||
.create(values) |
||||
.then(response => response.json()) |
||||
.then(data => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
commit(ACTIONS.ADD, data); |
||||
commit(ACTIONS.SET_CREATED, data); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
}, |
||||
del: ({ commit }, item) => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
service |
||||
.del(item) |
||||
.then(() => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
commit(ACTIONS.SET_DELETED, item); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
}, |
||||
fetchAll: ({ commit, state }, params) => { |
||||
if (!service) throw new Error('No service specified!'); |
||||
|
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
service |
||||
.findAll({ params }) |
||||
.then(response => response.json()) |
||||
.then(retrieved => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
commit( |
||||
ACTIONS.SET_TOTAL_ITEMS, |
||||
retrieved['hydra:totalItems'] |
||||
); |
||||
commit(ACTIONS.SET_VIEW, retrieved['hydra:view']); |
||||
|
||||
if (true === state.resetList) { |
||||
commit(ACTIONS.RESET_LIST); |
||||
} |
||||
|
||||
retrieved['hydra:member'].forEach(item => { |
||||
commit(ACTIONS.ADD, normalizeRelations(item)); |
||||
}); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
}, |
||||
fetchSelectItems: ( |
||||
{ commit }, |
||||
{ params = { properties: ['@id', 'name'] } } = {} |
||||
) => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
if (!service) throw new Error('No service specified!'); |
||||
|
||||
service |
||||
.findAll({ params }) |
||||
.then(response => response.json()) |
||||
.then(retrieved => { |
||||
commit( |
||||
ACTIONS.SET_SELECT_ITEMS, |
||||
retrieved['hydra:member'] |
||||
); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
}, |
||||
load: ({ commit }, id) => { |
||||
if (!service) throw new Error('No service specified!'); |
||||
|
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
service |
||||
.find(id) |
||||
.then(response => response.json()) |
||||
.then(item => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
commit(ACTIONS.ADD, normalizeRelations(item)); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
}, |
||||
resetCreate: ({ commit }) => { |
||||
commit(ACTIONS.RESET_CREATE); |
||||
}, |
||||
resetDelete: ({ commit }) => { |
||||
commit(ACTIONS.RESET_DELETE); |
||||
}, |
||||
resetShow: ({ commit }) => { |
||||
commit(ACTIONS.RESET_SHOW); |
||||
}, |
||||
resetUpdate: ({ commit }) => { |
||||
commit(ACTIONS.RESET_UPDATE); |
||||
}, |
||||
update: ({ commit }, item) => { |
||||
commit(ACTIONS.SET_ERROR, ''); |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
|
||||
service |
||||
.update(item) |
||||
.then(response => response.json()) |
||||
.then(data => { |
||||
commit(ACTIONS.TOGGLE_LOADING); |
||||
commit(ACTIONS.SET_UPDATED, data); |
||||
}) |
||||
.catch(e => handleError(commit, e)); |
||||
} |
||||
}, |
||||
getters: { |
||||
find: state => id => { |
||||
return resolveRelations(state.byId[id]); |
||||
}, |
||||
getField, |
||||
list: (state, getters) => { |
||||
return state.allIds.map(id => getters.find(id)); |
||||
} |
||||
}, |
||||
mutations: { |
||||
updateField, |
||||
[ACTIONS.ADD]: (state, item) => { |
||||
Vue.set(state.byId, item['@id'], item); |
||||
Vue.set(state, 'isLoading', false); |
||||
if (state.allIds.includes(item['@id'])) return; |
||||
state.allIds.push(item['@id']); |
||||
}, |
||||
[ACTIONS.RESET_CREATE]: state => { |
||||
Object.assign(state, { |
||||
isLoading: false, |
||||
error: '', |
||||
created: null, |
||||
violations: null |
||||
}); |
||||
}, |
||||
[ACTIONS.RESET_DELETE]: state => { |
||||
Object.assign(state, { |
||||
isLoading: false, |
||||
error: '', |
||||
deleted: null |
||||
}); |
||||
}, |
||||
[ACTIONS.RESET_LIST]: state => { |
||||
Object.assign(state, { |
||||
allIds: [], |
||||
byId: {}, |
||||
error: '', |
||||
isLoading: false, |
||||
resetList: false |
||||
}); |
||||
}, |
||||
[ACTIONS.RESET_SHOW]: state => { |
||||
Object.assign(state, { |
||||
error: '', |
||||
isLoading: false |
||||
}); |
||||
}, |
||||
[ACTIONS.RESET_UPDATE]: state => { |
||||
Object.assign(state, { |
||||
error: '', |
||||
isLoading: false, |
||||
updated: null, |
||||
violations: null |
||||
}); |
||||
}, |
||||
[ACTIONS.SET_CREATED]: (state, created) => { |
||||
Object.assign(state, { created }); |
||||
}, |
||||
[ACTIONS.SET_DELETED]: (state, deleted) => { |
||||
if (!state.allIds.includes(deleted['@id'])) return; |
||||
Object.assign(state, { |
||||
allIds: remove(state.allIds, item => item['@id'] === deleted['@id']), |
||||
byId: remove(state.byId, id => id === deleted['@id']), |
||||
deleted |
||||
}); |
||||
}, |
||||
[ACTIONS.SET_ERROR]: (state, error) => { |
||||
Object.assign(state, { error, isLoading: false }); |
||||
}, |
||||
[ACTIONS.SET_SELECT_ITEMS]: (state, selectItems) => { |
||||
Object.assign(state, { |
||||
error: '', |
||||
isLoading: false, |
||||
selectItems |
||||
}); |
||||
}, |
||||
[ACTIONS.SET_TOTAL_ITEMS]: (state, totalItems) => { |
||||
Object.assign(state, { totalItems }); |
||||
}, |
||||
[ACTIONS.SET_UPDATED]: (state, updated) => { |
||||
Object.assign(state, { |
||||
byId: { |
||||
[updated['@id']]: updated |
||||
}, |
||||
updated |
||||
}); |
||||
}, |
||||
[ACTIONS.SET_VIEW]: (state, view) => { |
||||
Object.assign(state, { view }); |
||||
}, |
||||
[ACTIONS.SET_VIOLATIONS]: (state, violations) => { |
||||
Object.assign(state, { violations }); |
||||
}, |
||||
[ACTIONS.TOGGLE_LOADING]: state => { |
||||
Object.assign(state, { error: '', isLoading: !state.isLoading }); |
||||
} |
||||
}, |
||||
namespaced: true, |
||||
state: initialState |
||||
}; |
||||
} |
@ -0,0 +1,18 @@ |
||||
import {getField, updateField} from 'vuex-map-fields'; |
||||
|
||||
export default { |
||||
namespaced: true, |
||||
state: { |
||||
show: false, |
||||
color: 'error', |
||||
text: 'An error occurred', |
||||
subText: '', |
||||
timeout: 6000 |
||||
}, |
||||
getters: { |
||||
getField |
||||
}, |
||||
mutations: { |
||||
updateField |
||||
} |
||||
}; |
@ -0,0 +1,9 @@ |
||||
import moment from 'moment'; |
||||
|
||||
const formatDateTime = function(date) { |
||||
if (!date) return null; |
||||
|
||||
return moment(date).format('DD/MM/YYYY'); |
||||
}; |
||||
|
||||
export { formatDateTime }; |
@ -0,0 +1,71 @@ |
||||
import isObject from 'lodash/isObject'; |
||||
import { ENTRYPOINT } from '../config/entrypoint'; |
||||
import SubmissionError from '../error/SubmissionError'; |
||||
import { normalize } from './hydra'; |
||||
|
||||
const MIME_TYPE = 'application/ld+json'; |
||||
|
||||
const makeParamArray = (key, arr) => |
||||
arr.map(val => `${key}[]=${val}`).join('&'); |
||||
|
||||
export default function(id, options = {}) { |
||||
if ('undefined' === typeof options.headers) options.headers = new Headers(); |
||||
|
||||
if (null === options.headers.get('Accept')) |
||||
options.headers.set('Accept', MIME_TYPE); |
||||
|
||||
if ( |
||||
'undefined' !== options.body && |
||||
!(options.body instanceof FormData) && |
||||
null === options.headers.get('Content-Type') |
||||
) |
||||
options.headers.set('Content-Type', MIME_TYPE); |
||||
|
||||
if (options.params) { |
||||
const params = normalize(options.params); |
||||
let queryString = Object.keys(params) |
||||
.map(key => |
||||
Array.isArray(params[key]) |
||||
? makeParamArray(key, params[key]) |
||||
: `${key}=${params[key]}` |
||||
) |
||||
.join('&'); |
||||
id = `${id}?${queryString}`; |
||||
} |
||||
|
||||
const entryPoint = ENTRYPOINT + (ENTRYPOINT.endsWith('/') ? '' : '/'); |
||||
|
||||
const payload = options.body && JSON.parse(options.body); |
||||
if (isObject(payload) && payload['@id']) |
||||
options.body = JSON.stringify(normalize(payload)); |
||||
|
||||
//console.log(id); console.log(new URL(id, entryPoint));
|
||||
|
||||
return global.fetch(new URL(id, entryPoint), options).then(response => { |
||||
if (response.ok) return response; |
||||
|
||||
return response.json().then( |
||||
json => { |
||||
const error = |
||||
json['hydra:description'] || |
||||
json['hydra:title'] || |
||||
'An error occurred.'; |
||||
|
||||
if (!json.violations) throw Error(error); |
||||
|
||||
let errors = { _error: error }; |
||||
json.violations.forEach(violation => |
||||
errors[violation.propertyPath] |
||||
? (errors[violation.propertyPath] += |
||||
'\n' + errors[violation.propertyPath]) |
||||
: (errors[violation.propertyPath] = violation.message) |
||||
); |
||||
|
||||
throw new SubmissionError(errors); |
||||
}, |
||||
() => { |
||||
throw new Error(response.statusText || 'An error occurred.'); |
||||
} |
||||
); |
||||
}); |
||||
} |
@ -0,0 +1,19 @@ |
||||
import get from 'lodash/get'; |
||||
import has from 'lodash/has'; |
||||
import mapValues from 'lodash/mapValues'; |
||||
|
||||
export function normalize(data) { |
||||
if (has(data, 'hydra:member')) { |
||||
// Normalize items in collections
|
||||
data['hydra:member'] = data['hydra:member'].map(item => normalize(item)); |
||||
|
||||
return data; |
||||
} |
||||
|
||||
// Flatten nested documents
|
||||
return mapValues(data, value => |
||||
Array.isArray(value) |
||||
? value.map(v => get(v, '@id', v)) |
||||
: get(value, '@id', value) |
||||
); |
||||
} |
@ -0,0 +1,7 @@ |
||||
import moment from 'moment'; |
||||
|
||||
const date = function(value) { |
||||
return moment(value).isValid(); |
||||
}; |
||||
|
||||
export { date }; |
@ -0,0 +1,46 @@ |
||||
<template> |
||||
<div> |
||||
<CourseForm ref="createForm" :values="item" :errors="violations" /> |
||||
<Loading :visible="isLoading" /> |
||||
|
||||
<Toolbar :handle-submit="onSendForm" :handle-reset="resetForm"></Toolbar> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions } from 'vuex'; |
||||
import { createHelpers } from 'vuex-map-fields'; |
||||
import CourseForm from '../../components/course/Form'; |
||||
import Loading from '../../components/Loading'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
import CreateMixin from '../../mixins/CreateMixin'; |
||||
|
||||
const servicePrefix = 'Course'; |
||||
|
||||
const { mapFields } = createHelpers({ |
||||
getterType: 'course/getField', |
||||
mutationType: 'course/updateField' |
||||
}); |
||||
|
||||
export default { |
||||
name: 'CourseCreate', |
||||
servicePrefix, |
||||
mixins: [CreateMixin], |
||||
components: { |
||||
Loading, |
||||
Toolbar, |
||||
CourseForm |
||||
}, |
||||
data() { |
||||
return { |
||||
item: {} |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapFields(['error', 'isLoading', 'created', 'violations']) |
||||
}, |
||||
methods: { |
||||
...mapActions('course', ['create', 'reset']) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,122 @@ |
||||
<template> |
||||
<div class="course-list"> |
||||
<Toolbar :handle-add="addHandler" /> |
||||
|
||||
<v-container grid-list-xl fluid> |
||||
<v-layout row wrap> |
||||
<!-- <v-flex sm12>--> |
||||
<!-- <h1>Course List</h1>--> |
||||
<!-- </v-flex>--> |
||||
<v-flex lg12> |
||||
<DataFilter :handle-filter="onSendFilter" :handle-reset="resetFilter"> |
||||
<CourseFilterForm |
||||
ref="filterForm" |
||||
:values="filters" |
||||
slot="filter" |
||||
/> |
||||
</DataFilter> |
||||
|
||||
<br /> |
||||
|
||||
<v-data-table |
||||
v-model="selected" |
||||
:headers="headers" |
||||
:items="items" |
||||
:items-per-page.sync="options.itemsPerPage" |
||||
:loading="isLoading" |
||||
:loading-text="$t('Loading...')" |
||||
:options.sync="options" |
||||
:server-items-length="totalItems" |
||||
class="elevation-1" |
||||
item-key="@id" |
||||
show-select |
||||
@update:options="onUpdateOptions" |
||||
> |
||||
<template slot="item.category" slot-scope="{ item }"> |
||||
<div v-if="item['category']"> |
||||
{{ item['category'].name }} |
||||
</div> |
||||
<div v-else> |
||||
- |
||||
</div> |
||||
</template> |
||||
|
||||
<template slot="item.visibility" slot-scope="{ item }"> |
||||
{{ $n(item['visibility']) }} |
||||
</template> |
||||
|
||||
<template slot="item.expirationDate" slot-scope="{ item }"> |
||||
{{ formatDateTime(item['expirationDate'], 'long') }} |
||||
</template> |
||||
|
||||
<ActionCell |
||||
slot="item.action" |
||||
slot-scope="props" |
||||
:handle-show="() => showHandler(props.item)" |
||||
:handle-edit="() => editHandler(props.item)" |
||||
:handle-delete="() => deleteHandler(props.item)" |
||||
></ActionCell> |
||||
</v-data-table> |
||||
</v-flex> |
||||
</v-layout> |
||||
</v-container> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import ListMixin from '../../mixins/ListMixin'; |
||||
import ActionCell from '../../components/ActionCell'; |
||||
import CourseFilterForm from '../../components/course/Filter'; |
||||
import DataFilter from '../../components/DataFilter'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
|
||||
export default { |
||||
name: 'CourseList', |
||||
servicePrefix: 'Course', |
||||
mixins: [ListMixin], |
||||
components: { |
||||
Toolbar, |
||||
ActionCell, |
||||
CourseFilterForm, |
||||
DataFilter |
||||
}, |
||||
data() { |
||||
return { |
||||
headers: [ |
||||
{ text: 'title', value: 'title' }, |
||||
{ text: 'code', value: 'code' }, |
||||
{ text: 'courseLanguage', value: 'Language' }, |
||||
{ text: 'category', value: 'category' }, |
||||
{ text: 'visibility', value: 'visibility' }, |
||||
{ |
||||
text: 'Actions', |
||||
value: 'action', |
||||
sortable: false |
||||
} |
||||
], |
||||
selected: [] |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapGetters('course', { |
||||
items: 'list' |
||||
}), |
||||
...mapFields('course', { |
||||
deletedItem: 'deleted', |
||||
error: 'error', |
||||
isLoading: 'isLoading', |
||||
resetList: 'resetList', |
||||
totalItems: 'totalItems', |
||||
view: 'view' |
||||
}) |
||||
}, |
||||
methods: { |
||||
...mapActions('course', { |
||||
getPage: 'fetchAll', |
||||
deleteItem: 'del' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,119 @@ |
||||
<template> |
||||
<div> |
||||
<Toolbar |
||||
:handle-delete="del" |
||||
:handle-list="list" |
||||
> |
||||
<template slot="left"> |
||||
<v-toolbar-title v-if="item">{{ |
||||
`${$options.servicePrefix} ${item['@id']}` |
||||
}}</v-toolbar-title> |
||||
</template> |
||||
</Toolbar> |
||||
<br /> |
||||
<div v-if="item" class="table-course-show"> |
||||
<v-simple-table> |
||||
<template slot="default"> |
||||
<thead> |
||||
<tr> |
||||
<th>Field</th> |
||||
<th>Value</th> |
||||
<th>Field</th> |
||||
<th>Value</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td><strong>{{ $t('title') }}</strong></td> |
||||
<td> |
||||
{{ item['title'] }} |
||||
</td> |
||||
|
||||
<td><strong>{{ $t('code') }}</strong></td> |
||||
<td> |
||||
{{ item['code'] }} |
||||
</td> |
||||
</tr> |
||||
|
||||
<tr> |
||||
<td><strong>{{ $t('courseLanguage') }}</strong></td> |
||||
<td> |
||||
{{ item['courseLanguage'] }} |
||||
</td> |
||||
|
||||
<td><strong>{{ $t('category') }}</strong></td> |
||||
<td> |
||||
<div v-if="item['category']"> |
||||
{{ item['category'].name }} |
||||
</div> |
||||
<div v-else> |
||||
- |
||||
</div> |
||||
|
||||
</td> |
||||
</tr> |
||||
|
||||
<tr> |
||||
<td><strong>{{ $t('visibility') }}</strong></td> |
||||
<td> |
||||
{{ $n(item['visibility']) }} </td> |
||||
|
||||
<td><strong>{{ $t('departmentName') }}</strong></td> |
||||
<td> |
||||
{{ item['departmentName'] }} |
||||
</td> |
||||
</tr> |
||||
|
||||
<tr> |
||||
<td><strong>{{ $t('departmentUrl') }}</strong></td> |
||||
<td> |
||||
{{ item['departmentUrl'] }} |
||||
</td> |
||||
|
||||
<td><strong>{{ $t('expirationDate') }}</strong></td> |
||||
<td> |
||||
{{ formatDateTime(item['expirationDate'], 'long') }} </td> |
||||
</tr> |
||||
|
||||
</tbody> |
||||
</template> |
||||
</v-simple-table> |
||||
</div> |
||||
|
||||
<Loading :visible="isLoading" /> |
||||
|
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import Loading from '../../components/Loading'; |
||||
import ShowMixin from '../../mixins/ShowMixin'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
|
||||
const servicePrefix = 'Course'; |
||||
|
||||
export default { |
||||
name: 'CourseShow', |
||||
servicePrefix, |
||||
components: { |
||||
Loading, |
||||
Toolbar |
||||
}, |
||||
mixins: [ShowMixin], |
||||
computed: { |
||||
...mapFields('course', { |
||||
isLoading: 'isLoading' |
||||
}), |
||||
...mapGetters('course', ['find']) |
||||
}, |
||||
methods: { |
||||
...mapActions('course', { |
||||
deleteItem: 'del', |
||||
reset: 'resetShow', |
||||
retrieve: 'load' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,67 @@ |
||||
<template> |
||||
<div> |
||||
<v-card |
||||
class="mx-auto" |
||||
> |
||||
<CourseForm |
||||
ref="updateForm" |
||||
v-if="item" |
||||
:values="item" |
||||
:errors="violations" |
||||
/> |
||||
<Loading :visible="isLoading || deleteLoading" /> |
||||
<v-footer> |
||||
<Toolbar |
||||
:handle-submit="onSendForm" |
||||
:handle-reset="resetForm" |
||||
:handle-delete="del" |
||||
/> |
||||
</v-footer> |
||||
</v-card> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import CourseForm from '../../components/course/Form.vue'; |
||||
import Loading from '../../components/Loading'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
import UpdateMixin from '../../mixins/UpdateMixin'; |
||||
|
||||
const servicePrefix = 'Course'; |
||||
|
||||
export default { |
||||
name: 'CourseUpdate', |
||||
servicePrefix, |
||||
mixins: [UpdateMixin], |
||||
components: { |
||||
Loading, |
||||
Toolbar, |
||||
CourseForm |
||||
}, |
||||
|
||||
computed: { |
||||
...mapFields('course', { |
||||
deleteLoading: 'isLoading', |
||||
isLoading: 'isLoading', |
||||
error: 'error', |
||||
updated: 'updated', |
||||
violations: 'violations' |
||||
}), |
||||
...mapGetters('course', ['find']) |
||||
|
||||
}, |
||||
|
||||
methods: { |
||||
...mapActions('course', { |
||||
createReset: 'resetCreate', |
||||
deleteItem: 'del', |
||||
delReset: 'resetDelete', |
||||
retrieve: 'load', |
||||
update: 'update', |
||||
updateReset: 'resetUpdate' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,45 @@ |
||||
<template> |
||||
<div> |
||||
<Toolbar :handle-submit="onSendForm" :handle-reset="resetForm"></Toolbar> |
||||
<CourseCategoryForm ref="createForm" :values="item" :errors="violations" /> |
||||
<Loading :visible="isLoading" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions } from 'vuex'; |
||||
import { createHelpers } from 'vuex-map-fields'; |
||||
import CourseCategoryForm from '../../components/coursecategory/Form'; |
||||
import Loading from '../../components/Loading'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
import CreateMixin from '../../mixins/CreateMixin'; |
||||
|
||||
const servicePrefix = 'CourseCategory'; |
||||
|
||||
const { mapFields } = createHelpers({ |
||||
getterType: 'coursecategory/getField', |
||||
mutationType: 'coursecategory/updateField' |
||||
}); |
||||
|
||||
export default { |
||||
name: 'CourseCategoryCreate', |
||||
servicePrefix, |
||||
mixins: [CreateMixin], |
||||
components: { |
||||
Loading, |
||||
Toolbar, |
||||
CourseCategoryForm |
||||
}, |
||||
data() { |
||||
return { |
||||
item: {} |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapFields(['error', 'isLoading', 'created', 'violations']) |
||||
}, |
||||
methods: { |
||||
...mapActions('coursecategory', ['create', 'reset']) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,104 @@ |
||||
<template> |
||||
<div class="coursecategory-list"> |
||||
<Toolbar :handle-add="addHandler" /> |
||||
|
||||
<v-container grid-list-xl fluid> |
||||
<v-layout row wrap> |
||||
<v-flex sm12> |
||||
<h1>CourseCategory List</h1> |
||||
</v-flex> |
||||
<v-flex lg12> |
||||
<DataFilter :handle-filter="onSendFilter" :handle-reset="resetFilter"> |
||||
<CourseCategoryFilterForm |
||||
ref="filterForm" |
||||
:values="filters" |
||||
slot="filter" |
||||
/> |
||||
</DataFilter> |
||||
|
||||
<br /> |
||||
|
||||
<v-data-table |
||||
v-model="selected" |
||||
:headers="headers" |
||||
:items="items" |
||||
:items-per-page.sync="options.itemsPerPage" |
||||
:loading="isLoading" |
||||
:loading-text="$t('Loading...')" |
||||
:options.sync="options" |
||||
:server-items-length="totalItems" |
||||
class="elevation-1" |
||||
item-key="@id" |
||||
show-select |
||||
@update:options="onUpdateOptions" |
||||
> |
||||
|
||||
<ActionCell |
||||
slot="item.action" |
||||
slot-scope="props" |
||||
:handle-show="() => showHandler(props.item)" |
||||
:handle-edit="() => editHandler(props.item)" |
||||
:handle-delete="() => deleteHandler(props.item)" |
||||
></ActionCell> |
||||
</v-data-table> |
||||
</v-flex> |
||||
</v-layout> |
||||
</v-container> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import ListMixin from '../../mixins/ListMixin'; |
||||
import ActionCell from '../../components/ActionCell'; |
||||
import CourseCategoryFilterForm from '../../components/coursecategory/Filter'; |
||||
import DataFilter from '../../components/DataFilter'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
|
||||
export default { |
||||
name: 'CourseCategoryList', |
||||
servicePrefix: 'CourseCategory', |
||||
mixins: [ListMixin], |
||||
components: { |
||||
Toolbar, |
||||
ActionCell, |
||||
CourseCategoryFilterForm, |
||||
DataFilter |
||||
}, |
||||
data() { |
||||
return { |
||||
headers: [ |
||||
{ text: 'name', value: 'name' }, |
||||
{ text: 'code', value: 'code' }, |
||||
//{ text: 'description', value: 'description' }, |
||||
{ |
||||
text: 'Actions', |
||||
value: 'action', |
||||
sortable: false |
||||
} |
||||
], |
||||
selected: [] |
||||
}; |
||||
}, |
||||
computed: { |
||||
...mapGetters('coursecategory', { |
||||
items: 'list' |
||||
}), |
||||
...mapFields('coursecategory', { |
||||
deletedItem: 'deleted', |
||||
error: 'error', |
||||
isLoading: 'isLoading', |
||||
resetList: 'resetList', |
||||
totalItems: 'totalItems', |
||||
view: 'view' |
||||
}) |
||||
}, |
||||
methods: { |
||||
...mapActions('coursecategory', { |
||||
getPage: 'fetchAll', |
||||
deleteItem: 'del' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,87 @@ |
||||
<template> |
||||
<div> |
||||
<Toolbar :handle-edit="editHandler" :handle-delete="del"> |
||||
<template slot="left"> |
||||
<v-toolbar-title v-if="item">{{ |
||||
`${$options.servicePrefix} ${item['@id']}` |
||||
}}</v-toolbar-title> |
||||
</template> |
||||
</Toolbar> |
||||
|
||||
<br /> |
||||
|
||||
<div v-if="item" class="table-coursecategory-show"> |
||||
<v-simple-table> |
||||
<template slot="default"> |
||||
<thead> |
||||
<tr> |
||||
<th>Field</th> |
||||
<th>Value</th> |
||||
|
||||
<th>Field</th> |
||||
<th>Value</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td><strong>{{ $t('name') }}</strong></td> |
||||
<td> |
||||
{{ item['name'] }} |
||||
</td> |
||||
|
||||
<td><strong>{{ $t('code') }}</strong></td> |
||||
<td> |
||||
{{ item['code'] }} |
||||
</td> |
||||
</tr> |
||||
|
||||
<tr> |
||||
<td><strong>{{ $t('description') }}</strong></td> |
||||
<td> |
||||
{{ item['description'] }} |
||||
</td> |
||||
|
||||
<td></td> |
||||
</tr> |
||||
|
||||
</tbody> |
||||
</template> |
||||
</v-simple-table> |
||||
</div> |
||||
|
||||
<Loading :visible="isLoading" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import Loading from '../../components/Loading'; |
||||
import ShowMixin from '../../mixins/ShowMixin'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
|
||||
const servicePrefix = 'CourseCategory'; |
||||
|
||||
export default { |
||||
name: 'CourseCategoryShow', |
||||
servicePrefix, |
||||
components: { |
||||
Loading, |
||||
Toolbar |
||||
}, |
||||
mixins: [ShowMixin], |
||||
computed: { |
||||
...mapFields('coursecategory', { |
||||
isLoading: 'isLoading' |
||||
}), |
||||
...mapGetters('coursecategory', ['find']) |
||||
}, |
||||
methods: { |
||||
...mapActions('coursecategory', { |
||||
deleteItem: 'del', |
||||
reset: 'resetShow', |
||||
retrieve: 'load' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,61 @@ |
||||
<template> |
||||
<div> |
||||
<Toolbar |
||||
:handle-submit="onSendForm" |
||||
:handle-reset="resetForm" |
||||
:handle-delete="del" |
||||
/> |
||||
<CourseCategoryForm |
||||
ref="updateForm" |
||||
v-if="item" |
||||
:values="item" |
||||
:errors="violations" |
||||
/> |
||||
<Loading :visible="isLoading || deleteLoading" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { mapActions, mapGetters } from 'vuex'; |
||||
import { mapFields } from 'vuex-map-fields'; |
||||
import CourseCategoryForm from '../../components/coursecategory/Form.vue'; |
||||
import Loading from '../../components/Loading'; |
||||
import Toolbar from '../../components/Toolbar'; |
||||
import UpdateMixin from '../../mixins/UpdateMixin'; |
||||
|
||||
const servicePrefix = 'CourseCategory'; |
||||
|
||||
export default { |
||||
name: 'CourseCategoryUpdate', |
||||
servicePrefix, |
||||
mixins: [UpdateMixin], |
||||
components: { |
||||
Loading, |
||||
Toolbar, |
||||
CourseCategoryForm |
||||
}, |
||||
|
||||
computed: { |
||||
...mapFields('coursecategory', { |
||||
deleteLoading: 'isLoading', |
||||
isLoading: 'isLoading', |
||||
error: 'error', |
||||
updated: 'updated', |
||||
violations: 'violations' |
||||
}), |
||||
...mapGetters('coursecategory', ['find']) |
||||
|
||||
}, |
||||
|
||||
methods: { |
||||
...mapActions('coursecategory', { |
||||
createReset: 'resetCreate', |
||||
deleteItem: 'del', |
||||
delReset: 'resetDelete', |
||||
retrieve: 'load', |
||||
update: 'update', |
||||
updateReset: 'resetUpdate' |
||||
}) |
||||
} |
||||
}; |
||||
</script> |
@ -0,0 +1,11 @@ |
||||
module.exports = { |
||||
pluginOptions: { |
||||
quasar: { |
||||
importStrategy: 'manual', |
||||
rtlSupport: false |
||||
} |
||||
}, |
||||
transpileDependencies: [ |
||||
'quasar' |
||||
] |
||||
} |
Loading…
Reference in new issue