update (end of the main panel feature I think)

This commit is contained in:
Matthieu Bessat 2020-07-15 15:21:53 +02:00
parent 4791bd8037
commit 79a24f6ae7
15 changed files with 814 additions and 71 deletions

View file

@ -15,6 +15,7 @@
"vue-apitator": "^0.0.16",
"vue-class-component": "^7.2.3",
"vue-croppa": "^1.3.8",
"vue-input-facade": "^1.3.1",
"vue-property-decorator": "^8.4.2",
"vue-router": "^3.2.0",
"vuetify": "^2.2.11",

View file

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Forum virtuel des associaitons</title>
<title>Forum virtuel des associations</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">

View file

@ -1,7 +1,7 @@
<template>
<v-dialog
v-model="enabled"
max-width="500px">
:max-width="modalSize">
<v-card>
<v-card-title>
{{ caption }}
@ -13,8 +13,8 @@
v-model="plugin"
:show-remove-button="false"
canvas-color="white"
:width="size"
:height="size"
:width="width"
:height="height"
prevent-white-space
:placeholder-font-size="22"
@file-size-exceed="handleFileSizeExceed"
@ -22,27 +22,29 @@
placeholder="Choisissez une image"
></croppa>
</v-layout>
<v-layout justify-center wrap>
<v-layout justify-center>
<v-btn
class="mr-2"
dark
outlined
color="indigo"
@click="rotate()">
Tourner 90°
<v-icon>rotate_right</v-icon>
</v-btn>
<v-btn
class="mr-2"
dark
outlined
color="indigo"
@click="rotate(-1)">
Tourner -90°
<v-icon>rotate_left</v-icon>
</v-btn>
<v-btn
dark
outlined
color="purple"
@click="clear">
Supprimer
<v-icon>clear</v-icon>
</v-btn>
</v-layout>
</v-card-text>
@ -72,14 +74,25 @@ export default {
name: 'AvatarEditor',
data: () => ({
enabled: false,
plugin: {},
size: 400
plugin: {}
}),
props: {
caption: {
type: String,
default: 'Modifier votre logo'
},
width: {
type: Number,
default: 400
},
height: {
type: Number,
default: 400
},
modalSize: {
type: Number,
default: 500
},
loading: {
type: Boolean,
default: false
@ -130,7 +143,6 @@ export default {
}
.canvas-container canvas {
border: 1px solid #bdc3c7;
border-radius: 50%;
}
@media screen and (max-width: 959px) {
.canvas-container canvas {

View file

@ -6,9 +6,9 @@
dark
flat
>
<v-toolbar-title>Gestion de l'association We Robot</v-toolbar-title>
<v-toolbar-title>{{ $store.state.delegateAdminName }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn outlined>
<v-btn outlined class="mr-3">
Publier
</v-btn>
<v-btn icon @click="logout()">
@ -45,7 +45,7 @@
</v-toolbar>
<v-container fluid>
<v-row class="justify-center">
<v-col cols="12" sm="12" md="8">
<v-col cols="12" sm="12" md="8" lg="7" xl="5">
<router-view></router-view>
<div class="mt-3 d-flex justify-end">
<v-btn color="success" :loading="isSaving" @click="save()">Sauvegarder</v-btn>
@ -99,11 +99,13 @@ export default {
created () {
this.init()
},
watch: {
$route (to, from) {
this.selectRoute(to)
}
},
mounted () {
const path = this.$route.path.split('/')
const name = path[path.length - 1]
const routes = ['', 'gallery', 'presentation', 'schedule', 'pricing', 'contact']
this.tab = routes.indexOf(name)
this.selectRoute(this.$route)
/**
* this is very ugly I kown
*/
@ -121,6 +123,12 @@ export default {
// }, 3000)
},
methods: {
selectRoute (route) {
const path = route.path.split('/')
const name = path[path.length - 1]
const routes = ['', 'gallery', 'presentation', 'schedule', 'pricing', 'contact']
this.tab = routes.indexOf(name)
},
init () {
this.enabled = false
@ -154,8 +162,15 @@ export default {
clearTimeout(this.loadingHandle)
this.loading = false
this.enabled = true
const organization = res.data.data
const organization = res.data.data.organization
const tags = res.data.data.tags
if (organization.proposedVersion.tag !== undefined && organization.proposedVersion.tag !== null) {
organization.proposedVersion.tag = organization.proposedVersion.tag._id
}
this.$store.commit('SET_DELEGATE_ADMIN_NAME', organization.adminName)
this.$store.commit('SET_DELEGATE_EMAIL', organization.email)
this.$store.commit('SET_DATA', organization.proposedVersion)
this.$store.commit('SET_TAGS', tags)
this.$nextTick(() => {
setTimeout(this.$refs.tabs.callSlider, 200)
setTimeout(this.$refs.tabs.callSlider, 400)
@ -185,6 +200,16 @@ export default {
delete i._id
return i
})
data.schedule = data.schedule.map(i => {
delete i._id
if (Array.isArray(i.when) && i.when.length > 0) {
i.when = i.when.map(w => {
delete w._id
return w
})
}
return i
})
this.$apitator.put('/delegate', data, { withAuth: true }).then(() => {
this.isSaving = false
this.$store.commit('ADD_ALERT', {

View file

@ -21,7 +21,6 @@ const routes: Array<RouteConfig> = [
{
path: '/admin',
name: 'AdminLayout',
component: () => import(/* webpackChunkName: "adminLayout" */ '../layouts/Admin.vue'),
children: [
{
@ -39,7 +38,6 @@ const routes: Array<RouteConfig> = [
{
path: '/delegate',
name: 'DelegateLayout',
component: () => import(/* webpackChunkName: "delegateLayout" */ '../layouts/Delegate.vue'),
children: [
{

View file

@ -11,6 +11,7 @@ export default new Vuex.Store({
enabled: false
},
title: '',
tags: [],
data: {
name: '',
descriptionShort: '',
@ -31,7 +32,9 @@ export default new Vuex.Store({
email: '',
phone: ''
}
}
},
delegateAdminName: '',
delegateEmail: ''
},
mutations: {
SET_TITLE (state, payload) {
@ -51,6 +54,15 @@ export default new Vuex.Store({
if (payload !== null) {
state.data = { ...state.data, ...payload }
}
},
SET_TAGS (state, payload) {
state.tags = payload
},
SET_DELEGATE_ADMIN_NAME (state, payload) {
state.delegateAdminName = payload
},
SET_DELEGATE_EMAIL (state, payload) {
state.delegateEmail = payload
}
},
actions: {

View file

@ -1,15 +1,27 @@
<template>
<div>
<div class="mb-2">
<p class="text-body">
<div class="mb-3 pb-1">
<v-alert
border="left"
colored-border
type="info"
elevation="1"
>
Ici vous pouvez préciser quelques manières de contacter votre association, aucun des champs indiqués n'est obligatoire.
</p>
</v-alert>
</div>
<v-text-field
prepend-icon="person"
label="Personne responsable"
outlined
v-model="$store.state.data.contacts.person" />
<v-text-field
prepend-icon="alternate_email"
label="Email"
:rules="rules.email"
outlined
v-model="$store.state.data.contacts.email" />
<v-text-field
prepend-icon="call"
label="Numéro de téléphone"
@ -45,7 +57,22 @@
<script>
export default {
data: () => ({}),
data: () => ({
rules: {
email: [
v => /.+@.+\..+/.test(v) || "L'email doit être valide"
],
facebook: [
v => /(http(s)?:\/\/)?(www\.)?facebook\.com\/(\S+){3,}/.test(v) || 'Ce champs doit être une url facebook valide'
],
twitter: [
v => /(http(s)?:\/\/)?(www\.)?twitter\.com\/(\S+){3,}/.test(v) || 'Ce champs doit être une url twitter valide'
],
instagram: [
v => /(http(s)?:\/\/)?(www\.)?instagram\.com\/(\S+){3,}/.test(v) || 'Ce champs doit être une url instagram valide'
]
}
}),
methods: {}
}
</script>

View file

@ -1,5 +1,281 @@
<template>
<div>
Gallery
<!-- COVER START -->
<div
class="cover-common cover-container"
:style="coverStyle">
</div>
<div class="cover-common cover-upper">
<v-btn
@click="$refs.avatarEditor.toggle()"
color="white"
>Changer la couverture</v-btn>
</div>
<AvatarEditor
ref="avatarEditor"
:loading="coverLoading"
caption="Modifier votre couverture"
:width="960"
:height="300"
:modal-size="1000"
@submitted="handleAvatarEditorSubmitted"
/>
<!-- COVER END -->
<div class="d-flex justify-end mt-3 mb-3">
<v-btn color="primary" outlined @click="addMediaModal = true">
<v-icon left>add</v-icon>
Ajouter une image
</v-btn>
</div>
<div
v-if="$store.state.data.gallery.length === 0"
class="mb-8 mt-8">
<v-alert
border="left"
color="grey darken-2"
outlined
type="info"
>
Aucun medias n'ont été ajoutés pour l'instant
</v-alert>
</div>
<v-row v-else>
<v-col
v-for="(media, index) in $store.state.data.gallery"
:key="media._id"
cols="12"
xs="12"
sm="6"
md="6"
lg="4"
>
<v-card>
<v-img
:src="media.location"
height="200"
class="grey darken-4"
></v-img>
<v-card-actions>
<v-btn
icon
color="gray"
small
:disabled="index === 0"
@click="shiftLeftMedia(media)"
>
<v-icon>keyboard_arrow_left</v-icon>
</v-btn>
<v-btn
icon
color="gray"
small
:disabled="index + 1 === $store.state.data.gallery.length"
@click="shiftRightMedia(media)"
>
<v-icon>keyboard_arrow_right</v-icon>
</v-btn>
<v-spacer />
<v-btn icon color="error" small @click="openDeleteMediaModal(media)">
<v-icon small>delete</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<v-dialog v-model="addMediaModal" max-width="600px">
<v-card>
<v-card-title>
Ajouter un ou des médias dans la gallerie
</v-card-title>
<v-card-text>
<div class="mt-2">
<v-file-input
v-model="files"
chips
multiple
show-size
counter
label="Sélectionnez les images ou vidéos à importer">
</v-file-input>
</div>
</v-card-text>
<v-card-actions>
<v-btn text color="primary" @click="addMediaModal = false">
Fermer
</v-btn>
<v-spacer />
<v-btn text color="primary" @click="uploadMedias()" :loading="uploadLoading">
Téléverser
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="deleteMediaModal" max-width="500px">
<v-card>
<v-card-title>Êtes vous sur de vouloir supprimer ce media ?</v-card-title>
<v-card-actions>
<v-btn color="primary" text @click="deleteMediaModal = false">
Fermer
</v-btn>
<v-btn color="error" text @click="deleteMedia()">
Supprimer
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import AvatarEditor from '../../components/AvatarEditor'
export default {
components: { AvatarEditor },
data: () => ({
coverLoading: false,
addMediaModal: false,
uploadLoading: false,
files: [],
deleteMediaModal: false,
mediaToDelete: {}
}),
computed: {
coverStyle () {
if (this.$store.state.data.cover === undefined || this.$store.state.data.cover === null) {
return {}
}
return { backgroundImage: 'url(' + this.$store.state.data.cover.location + ')' }
}
},
methods: {
handleAvatarEditorSubmitted (blob) {
const form = new FormData()
form.append('file', blob, blob.filename)
this.coverLoading = true
this.$apitator.post('/delegate/cover', form, { withAuth: true }).then(res => {
this.coverLoading = false
this.$store.commit('SET_DATA', { cover: res.data.cover })
this.$refs.avatarEditor.finish()
this.$store.commit('ADD_ALERT', {
color: 'success',
text: 'Couverture mise à jour !'
})
}).catch(() => {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Impossible de mettre à jour la couverture'
})
this.coverLoading = false
})
},
loadFile (file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsArrayBuffer(file)
reader.onloadend = (event) => {
resolve(['file', event.target.result, file.name])
}
reader.onerror = () => {
console.log(reader.error)
reject(reader.error)
}
})
},
uploadMedias () {
this.uploadLoading = true
const promises = this.files.map(file => this.loadFile(file))
Promise.all(promises).then(results => {
const formData = new FormData()
results.forEach(r => formData.append(r[0], new Blob([new Uint8Array(r[1])]), r[2]))
this.$apitator.post('/delegate/medias', formData, { withAuth: true }).then(res => {
this.$store.commit('SET_DATA', { gallery: res.data.data.gallery })
this.$store.commit('ADD_ALERT', {
color: 'success',
text: 'Les médias ont été téléversés'
})
this.uploadLoading = false
this.addMediaModal = false
this.files = []
}).catch(() => {
this.coverLoading = false
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Impossible de téleverser ces fichiers'
})
})
}).catch(() => {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Erreur de chargement des fichiers'
})
})
},
openDeleteMediaModal (media) {
this.deleteMediaModal = true
this.mediaToDelete = media
},
deleteMedia () {
this.deleteMediaModal = false
let gallery = this.$store.state.data.gallery
gallery = gallery.filter(media => this.mediaToDelete._id !== media._id)
this.$store.commit('SET_DATA', { gallery })
},
shiftLeftMedia (media) {
const originalGallery = this.$store.state.data.gallery
const index = originalGallery.indexOf(media)
if (index === 0) {
return
}
const gallery = originalGallery.map((m, i) => {
if (i === index - 1) {
return originalGallery[index]
}
if (i === index) {
return originalGallery[index - 1]
}
return m
})
this.$store.commit('SET_DATA', { gallery })
},
shiftRightMedia (media) {
const originalGallery = this.$store.state.data.gallery
const index = originalGallery.indexOf(media)
if (index === originalGallery.length - 1) {
return
}
const gallery = originalGallery.map((m, i) => {
if (i === index + 1) {
return originalGallery[index]
}
if (i === index) {
return originalGallery[index + 1]
}
return m
})
this.$store.commit('SET_DATA', { gallery })
}
}
}
</script>
<style scoped>
.cover-common {
height: 16em;
border-radius: .5em;
}
.cover-container {
background-size: cover;
}
.cover-upper {
display: flex;
justify-content: center;
align-items: center;
margin-top: -16em;
background-color: rgba(41, 128, 185, 0.4);
}
</style>

View file

@ -1,28 +1,37 @@
<template>
<div>
<v-row class="mb-5">
<v-row class="mb-6 pb-2">
<v-col cols="12" sm="12" md="6" style="align-items: center; justify-content: center; display: flex;">
<v-avatar size="200" style="border: 2px solid #95a5a6">
<img :src="$store.state.data.thumbnail.location" />
</v-avatar>
</v-col>
<v-col cols="12" sm="12" md="6" style="align-items: center; justify-content: center; display: flex;">
<v-btn @click="$refs.avatarEditor.toggle()" color="primary">Changer le logo</v-btn>
<v-btn @click="$refs.avatarEditor.toggle()" color="primary" outlined>
Changer le logo
</v-btn>
</v-col>
</v-row>
<v-row style="align-items: center;">
<v-col cols="12" sm="12" md="12">
<v-text-field
label="Nom de l'association"
prepend-icon="tag"
outlined
prepend-icon="label"
v-model="$store.state.data.name" />
</v-col>
</v-row>
<v-select
label="Catégorie de l'association"
outlined
prepend-icon="category"
item-text="name"
item-value="_id"
v-model="$store.state.data.tag"
:items="$store.state.tags">
</v-select>
<v-textarea
outlined
prepend-icon="description"
label="Description ou résumé rapide"
placeholder="Courte description qui apparaitra sur la page d'accueil"
:rules="rules.descriptionShort"

View file

@ -8,10 +8,15 @@
</div>
<div
v-if="$store.state.data.pricing.length === 0"
class="mb-2 mt-2 d-flex justify-center">
<span class="text-gray">
Pas de tarifs ajoutés
</span>
class="mb-8 mt-8">
<v-alert
border="left"
color="grey darken-2"
outlined
type="info"
>
Aucune catégorie horaires ajoutées pour le moment
</v-alert>
</div>
<v-row v-else>
<v-col
@ -40,16 +45,18 @@
<v-btn
@click="openDeleteModal(pricing)"
icon
outlined
color="error"
>
<v-icon>delete</v-icon>
<v-icon small>delete</v-icon>
</v-btn>
<v-btn
@click="openEditModal(pricing)"
icon
outlined
color="info"
>
<v-icon>edit</v-icon>
<v-icon small>edit</v-icon>
</v-btn>
</v-card-actions>
</v-card>

View file

@ -1,9 +1,27 @@
<template>
<div>
<div class="d-flex">
<div class="d-flex justify-end">
<v-btn @click="openAddCategoryModal()" color="teal" outlined>
<v-icon left>add</v-icon>
Ajouter une catégorie
</v-btn>
</div>
<div
v-if="$store.state.data.schedule.length === 0"
class="mb-8 mt-8">
<v-alert
border="left"
color="grey darken-2"
outlined
type="info"
>
Aucune catégorie horaires ajoutées pour le moment
</v-alert>
</div>
<div v-else>
<div
class="schedule-item"
v-for="item in scheduleData"
v-for="item in $store.state.data.schedule"
:key="item.name"
>
<!--
@ -12,27 +30,222 @@
md="6"
lg="6"
-->
<v-card>
<v-card-title>
<v-card class="mt-4">
<div class="schedule-content">
<v-card-title class="flex-column justify-space-between">
<div class="schedule-title-container">
<div class="subheading font-weight-bold">{{ item.name }}</div>
<div class="text-subtitle-2">{{ item.description }}</div>
<div class="text-body-1">{{ item.description }}</div>
</div>
<div class="mt-5 schedule-actions">
<div class="mr-3">
<v-btn outlined icon small color="info" class="mr-2" @click="openEditCategoryModal(item)">
<v-icon small>edit</v-icon>
</v-btn>
<v-btn outlined icon small color="error" @click="openDeleteCategoryModal(item)">
<v-icon small>delete</v-icon>
</v-btn>
</div>
<v-btn
@click="openAddWhenModal(item)"
outlined
small
color="success">
Ajouter un horaire
</v-btn>
</div>
</v-card-title>
<v-divider></v-divider>
<v-list dense>
<v-list-item
<div class="d-flex align-center schedule-days">
<v-list
v-if="Array.isArray(item.when) && item.when.length > 0"
dense
class="schedule-list">
<v-divider />
<div
v-for="when in item.when"
:key="when.day">
<v-list-item-content>{{ when.day }}</v-list-item-content>
<v-list-item-content class="align-end">
{{ when.from }} - {{ when.to }}
<v-list-item>
<v-list-item-content>
<div class="d-flex justify-space-between">
<div>{{ when.day }}</div>
<div>{{ when.from }} - {{ when.to }}</div>
</div>
</v-list-item-content>
<v-list-item-action>
<div class="d-flex justify-space-between">
<v-btn icon color="info" @click="openEditWhenModal(when)">
<v-icon small>
edit
</v-icon>
</v-btn>
<v-btn icon color="error" @click="openDeleteWhenModal(when)">
<v-icon small>delete</v-icon>
</v-btn>
</div>
</v-list-item-action>
</v-list-item>
<v-divider />
</div>
</v-list>
<div v-else class="grey--text text--darken-1">
Pas d'interval horaires pour cette catégorie pour l'instant
</div>
</div>
</div>
</v-card>
</div>
</div>
<v-dialog
max-width="500px"
v-model="categoryModal">
<v-card>
<v-card-title v-text="modalTitle + ' une catégorie horaire'" />
<v-card-text>
<v-text-field
v-model="category.name"
label="Nom"
hint="Requis"
/>
<v-text-field
v-model="category.description"
label="Description"
hint="Optionel"
/>
</v-card-text>
<v-card-actions>
<v-btn @click="categoryModal = false" text color="primary">
Fermer
</v-btn>
<v-spacer />
<v-btn
@click="saveCategory()"
text
color="success"
:disabled="category.name.length < 3"
>
Valider
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
max-width="500px"
v-model="deleteCategoryModal">
<v-card>
<v-card-title>
Voulez vous vraiment supprimer cette catégorie horaire ?
</v-card-title>
<v-card-actions>
<v-btn @click="deleteCategoryModal = false" text color="primary">
Fermer
</v-btn>
<v-spacer />
<v-btn @click="deleteCategory()" text color="error">
Supprimer
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
max-width="500px"
v-model="whenModal">
<v-card>
<v-card-title v-text="modalTitle + ' un interval horaire'" />
<v-card-text>
<v-select
prepend-icon="event"
v-model="when.day"
:items="days"
label="Sélectionnez un jour"
></v-select>
<v-menu
ref="fromMenu"
v-model="fromMenu"
:close-on-content-click="false"
:return-value.sync="when.from"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="when.from"
label="Heure de début"
prepend-icon="schedule"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-time-picker v-model="when.from" format="24hr">
<v-btn text color="primary" @click="fromMenu = false">Fermer</v-btn>
<v-spacer></v-spacer>
<v-btn text color="primary" @click="$refs.fromMenu.save(when.from)">OK</v-btn>
</v-time-picker>
</v-menu>
<v-menu
ref="toMenu"
v-model="toMenu"
:close-on-content-click="false"
:return-value.sync="when.to"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="when.to"
label="Heure de fin"
prepend-icon="schedule"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-time-picker v-model="when.to" format="24hr">
<v-btn text color="primary" @click="toMenu = false">Fermer</v-btn>
<v-spacer></v-spacer>
<v-btn
text
color="primary"
@click="$refs.toMenu.save(when.to)">OK</v-btn>
</v-time-picker>
</v-menu>
</v-card-text>
<v-card-actions>
<v-btn @click="categoryModal = false" text color="primary">
Fermer
</v-btn>
<v-spacer />
<v-btn
@click="saveWhen()"
text
color="success"
:disabled="when.day.length < 4 || when.to.length !== 5 || when.from.length !== 5">
Valider
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
max-width="500px"
v-model="deleteWhenModal">
<v-card>
<v-card-title>
Voulez vous vraiment supprimer cette interval horaire ?
</v-card-title>
<v-card-actions>
<v-btn @click="deleteWhenModal = false" text color="primary">
Fermer
</v-btn>
<v-spacer />
<v-btn @click="deleteWhen()" text color="error">
Supprimer
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
@ -103,13 +316,171 @@ export default {
}
]
}
],
mode: '',
categoryModal: false,
category: {
name: '',
description: ''
},
deleteCategoryModal: false,
toDeleteCategory: {},
oldEditCategory: {},
whenModal: false,
when: {
day: '',
from: '',
to: ''
},
deleteWhenModal: false,
toDeleteWhen: {},
oldEditWhen: {},
fromMenu: false,
toMenu: false,
days: [
'Lundi',
'Mardi',
'Mercredi',
'Jeudi',
'Vendredi',
'Samedi'
]
}),
computed: {
modalTitle () { return this.mode === 'add' ? 'Ajouter' : 'Editer' }
},
methods: {
openAddCategoryModal () {
this.mode = 'add'
this.categoryModal = true
},
openEditCategoryModal (item) {
this.category = item
this.oldEditCategory = item
this.mode = 'edit'
this.categoryModal = true
},
openDeleteCategoryModal (item) {
this.deleteCategoryModal = true
this.toDeleteCategory = item
},
saveCategory () {
let schedule = this.$store.state.data.schedule
if (this.mode === 'add') {
this.category._id = Date.now().toString()
schedule.push(this.category)
} else {
schedule = schedule.map(i => i.name === this.oldEditCategory.name && i.description === this.oldEditCategory.description ? this.category : i)
}
this.category = {
name: '',
description: ''
}
this.$store.commit('SET_DATA', { schedule })
this.categoryModal = false
},
deleteCategory () {
let schedule = this.$store.state.data.schedule
schedule = schedule.filter(item => item.name !== this.toDeleteCategory.name && item.description !== this.toDeleteCategory.description)
this.$store.commit('SET_DATA', { schedule })
this.deleteCategoryModal = false
},
openAddWhenModal (parent) {
this.mode = 'add'
this.whenParent = parent
this.whenModal = true
},
openEditWhenModal (item) {
this.when = item
this.oldEditWhen = item
this.mode = 'edit'
this.whenModal = true
},
openDeleteWhenModal (item) {
this.deleteWhenModal = true
this.toDeleteWhen = item
},
saveWhen () {
let schedule = this.$store.state.data.schedule
if (this.mode === 'add') {
this.when._id = Date.now().toString()
schedule = schedule.map(i => {
if (this.whenParent._id === i._id) {
if (!Array.isArray(i.when)) {
i.when = []
}
i.when.push(this.when)
}
return i
})
} else {
schedule = schedule.map(i => {
if (this.whenParent._id === i._id) {
i.when = i.when.map(w => w._id === this.when._id ? this.when : w)
}
return i
})
}
this.when = {
day: '',
from: '10:00',
to: '11:00'
}
this.$store.commit('SET_DATA', { schedule })
this.whenModal = false
},
deleteWhen () {
let schedule = this.$store.state.data.schedule
schedule = schedule.map(i => {
if (this.whenParent._id === i._id) {
i.when = i.when.filter(w => w._id !== this.toDeleteWhen._id)
}
return i
})
this.$store.commit('SET_DATA', { schedule })
this.deleteWhenModal = false
}
}
}
</script>
<style scoped>
.schedule-item {
width: 25em;
<style>
.schedule-content {
display: grid;
grid-template-columns: 1fr 1fr;
}
.schedule-title-container {
width: 100%;
word-break: break-word;
}
.schedule-list {
width: 100%;
}
.schedule-actions {
display: flex;
align-items: center;
width: 100%;
}
.schedule-actions div:first-child {
display: flex;
align-items: center;
}
@media (max-width: 900px) {
.schedule-content {
grid-template-columns: 1fr;
}
.schedule-days {
margin-right: 0;
}
.schedule-actions {
justify-content: space-between;
}
.schedule-actions div:first-child {
margin: 0;
}
}
</style>

View file

@ -9120,6 +9120,11 @@ vue-hot-reload-api@^2.3.0:
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==
vue-input-facade@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/vue-input-facade/-/vue-input-facade-1.3.1.tgz#87cdca62f75fcdf8205fc7345e4f6a560f4daaa8"
integrity sha512-8tgWiBObwVT3v9XD9OruwPDdWFwbTxaGdtUd1vXqAwNMZceETxZdfsgkMjUtwT1u2tCY3Ptu09WNDPwiwa4huw==
vue-loader@^15.9.2:
version "15.9.3"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda"