- Complete GGZ Ecademy Nuxt.js user portal - Learning products browser and management - Member management interface - User authentication and roles - Multi-language support (NL/EN) - Vuex store for state management - Component-based architecture
This commit is contained in:
168
pages/manager/components/_component.vue
Normal file
168
pages/manager/components/_component.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<v-card tile>
|
||||
<v-toolbar dark color="primary" flat>
|
||||
<v-toolbar-title>Add Component</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items>
|
||||
<v-btn dark text @click="saveComponent">Save</v-btn>
|
||||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
|
||||
<!-- Components -->
|
||||
<v-overflow-btn
|
||||
:items="component_types"
|
||||
v-model="componentSelected"
|
||||
item-text="name"
|
||||
return-object
|
||||
label="Choose one"
|
||||
hide-details
|
||||
class="ma-4"
|
||||
overflow
|
||||
dense
|
||||
flat
|
||||
solo
|
||||
></v-overflow-btn>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<!-- Dynamic Component -->
|
||||
<component :is="component" :data="model" />
|
||||
<!-- End Dynamic Component -->
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" class="pr-4">
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="valid"
|
||||
:lazy-validation="lazy"
|
||||
v-if="componentSelected"
|
||||
class="my-4"
|
||||
>
|
||||
<v-text-field v-model="name" label="Name" :rules="nameRules" required></v-text-field>
|
||||
<v-text-field
|
||||
v-model="description"
|
||||
label="Description"
|
||||
:rules="descriptionRules"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-form>
|
||||
|
||||
<vue-form-generator
|
||||
v-if="componentSelected"
|
||||
:schema="componentSelected.schema"
|
||||
:model="model"
|
||||
:options="formOptions"
|
||||
></vue-form-generator>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-tabs vertical v-if="componentSelected">
|
||||
<v-tab>Data</v-tab>
|
||||
<v-tab>Schema</v-tab>
|
||||
<v-tab-item>
|
||||
<pre v-if="model" v-html="model"></pre>
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<pre v-if="componentSelected.schema" v-html="componentSelected.schema"></pre>
|
||||
</v-tab-item>
|
||||
</v-tabs>
|
||||
|
||||
<div class="d-flex ma-4">
|
||||
<v-btn class="ma-2" color="info" tile depressed to="/manager/components">Back to components</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn class="ma-2" color="success" tile depressed @click="saveComponent">Save Component</v-btn>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueFormGenerator from 'vue-form-generator'
|
||||
import 'vue-form-generator/dist/vfg.css'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
components: {
|
||||
'vue-form-generator': VueFormGenerator.component
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
components: [],
|
||||
dialog: false,
|
||||
componentSelected: '',
|
||||
component_types: [],
|
||||
model: {},
|
||||
name: '',
|
||||
description: '',
|
||||
valid: true,
|
||||
lazy: false,
|
||||
nameRules: [
|
||||
v => !!v || 'Name is required',
|
||||
v => (v && v.length <= 20) || 'Name must be less than 20 characters'
|
||||
],
|
||||
descriptionRules: [
|
||||
v => !!v || 'Description is required',
|
||||
v =>
|
||||
(v && v.length <= 50) || 'Description must be less than 50 characters'
|
||||
],
|
||||
formOptions: {
|
||||
validateAfterLoad: true,
|
||||
validateAfterChanged: true,
|
||||
validateAsync: true
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageSlug() {
|
||||
return this.$route.params.component
|
||||
},
|
||||
isNewComponent() {
|
||||
return this.pageSlug.toLowerCase() === 'new'
|
||||
},
|
||||
hasComponents() {
|
||||
return Object.keys(this.components).length > 0
|
||||
},
|
||||
isSlugNumber() {
|
||||
return !isNaN(this.pageSlug)
|
||||
},
|
||||
isValidSlug() {
|
||||
return this.isNewComponent || this.isSlugNumber
|
||||
},
|
||||
component() {
|
||||
if (!this.componentSelected) return
|
||||
return this.componentSelected.name
|
||||
? () =>
|
||||
import(
|
||||
`@/components/DynamicComponents/${this.componentSelected.name}`
|
||||
)
|
||||
: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getComponentTypes()
|
||||
},
|
||||
methods: {
|
||||
async getComponentTypes() {
|
||||
try {
|
||||
const response = await this.$axios.get(`/component-types`)
|
||||
this.component_types = response.data
|
||||
} catch (error) {
|
||||
console.log('TCL: getComponentTypes -> error', error)
|
||||
}
|
||||
},
|
||||
async saveComponent() {
|
||||
const data = {
|
||||
name: this.name,
|
||||
description: this.description || null,
|
||||
content: this.model,
|
||||
component_type_id: this.componentSelected.id
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.$axios.post('/components', data)
|
||||
this.$router.push('/manager/components')
|
||||
} catch (error) {
|
||||
console.log('TCL: saveComponent -> error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
132
pages/manager/components/index.vue
Normal file
132
pages/manager/components/index.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header v-if="components">COMPONENTS ({{components.length}})</page-header>
|
||||
|
||||
<v-sheet class="mx-auto pa-4" tile>
|
||||
<v-data-table :search="search" :headers="headers" :items="components" :items-per-page="10">
|
||||
<template v-slot:top>
|
||||
<v-toolbar flat>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
class="hidden-sm-and-down"
|
||||
hide-details
|
||||
flat
|
||||
append-icon="mdi-filter"
|
||||
placeholder="Filter..."
|
||||
light
|
||||
outlined
|
||||
dense
|
||||
></v-text-field>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn depressed to="/manager/components/new">
|
||||
<v-icon small>icon-add</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
color="info"
|
||||
class="mr-2"
|
||||
small
|
||||
dark
|
||||
tile
|
||||
depressed
|
||||
nuxt
|
||||
:to="localePath(`/manager/components/${item.id}`)"
|
||||
>
|
||||
<v-icon small>mdi-file-edit</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-dialog v-model="dialog" persistent max-width="500">
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn color="error" v-on="on" small dark tile depressed>
|
||||
<v-icon small>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">Do you want to delete this component?</v-card-title>
|
||||
<v-card-text>Deleting this component it will disappear from all the pages where it is attached and won't be possible to restore it anymore.</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="info" @click="dialog = false" tile depressed>back</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="error" @click="deleteComponent(item.id)" tile depressed>Yes, Delete</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-sheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
components: {
|
||||
PageHeader
|
||||
},
|
||||
data: () => ({
|
||||
dialog: false,
|
||||
search: '',
|
||||
components: [],
|
||||
componentToDelete: null,
|
||||
headers: [
|
||||
{ text: 'id', sortable: true, value: 'id' },
|
||||
{ text: 'Name', sortable: true, value: 'name' },
|
||||
// { text: 'Description', value: 'description' },
|
||||
// { text: 'Created', value: 'created_at', sortable: true },
|
||||
{ text: 'Updated', value: 'updated_at', sortable: true },
|
||||
{ text: 'Actions', value: 'actions', sortable: false }
|
||||
]
|
||||
}),
|
||||
|
||||
watch: {
|
||||
dialog(val) {
|
||||
val || this.close()
|
||||
}
|
||||
},
|
||||
|
||||
async asyncData({ $axios }) {
|
||||
const response = await $axios.get('/components')
|
||||
return { components: response.data }
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.dialog = false
|
||||
},
|
||||
|
||||
save() {
|
||||
this.close()
|
||||
},
|
||||
|
||||
async deleteComponent(id) {
|
||||
try {
|
||||
const response = await this.$axios.delete(`/components/${id}`)
|
||||
this.components = this.components.filter(
|
||||
component => component.id !== id
|
||||
)
|
||||
this.dialog = false
|
||||
|
||||
this.$store.commit('snackbar/set', {
|
||||
color: 'success',
|
||||
display: true,
|
||||
text: 'Component deleted',
|
||||
icon: 'mdi-delete'
|
||||
})
|
||||
} catch (error) {
|
||||
this.dialog = false
|
||||
this.$store.commit('snackbar/set', {
|
||||
color: 'error',
|
||||
display: true,
|
||||
text: 'Error on deleting component',
|
||||
icon: 'mdi-delete'
|
||||
})
|
||||
console.log('TCL: deleteComponent -> error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user