This commit is contained in:
Matthieu Bessat 2020-07-18 00:10:58 +02:00
parent 4a94799602
commit de2e56baae
13 changed files with 314 additions and 171 deletions

View file

@ -12,7 +12,7 @@
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"tiptap-vuetify": "^2.23.0", "tiptap-vuetify": "^2.23.0",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-apitator": "^0.0.16", "vue-apitator": "0.0.17",
"vue-class-component": "^7.2.3", "vue-class-component": "^7.2.3",
"vue-croppa": "^1.3.8", "vue-croppa": "^1.3.8",
"vue-input-facade": "^1.3.1", "vue-input-facade": "^1.3.1",

View file

@ -20,6 +20,9 @@ export default Vue.extend({
created () { created () {
console.log('Using ' + process.env.VUE_APP_BASE_URL) console.log('Using ' + process.env.VUE_APP_BASE_URL)
if (window.localStorage.getItem('associations_condorcet_debug') === 'YES') {
this.$store.commit('SET_DEBUG', true)
}
}, },
data: () => ({}) data: () => ({})

View file

@ -6,7 +6,7 @@
:vertical="$vuetify.breakpoint.mobile" :vertical="$vuetify.breakpoint.mobile"
:multi-line="$vuetify.breakpoint.mobile" :multi-line="$vuetify.breakpoint.mobile"
v-model="$store.state.alert.enabled" v-model="$store.state.alert.enabled"
:timeout="500000" :timeout="2500"
> >
{{ $store.state.alert.text }} {{ $store.state.alert.text }}

View file

@ -58,7 +58,7 @@
<v-tab @click="navigate('DelegateGallery')"> <v-tab @click="navigate('DelegateGallery')">
Images/vidéos Images/vidéos
</v-tab> </v-tab>
<v-tab @click="navigate('DelegatePresentation')"> <v-tab @click="navigate('DelegateDescription')">
Description Description
</v-tab> </v-tab>
<v-tab @click="navigate('DelegateSchedule')"> <v-tab @click="navigate('DelegateSchedule')">
@ -254,7 +254,7 @@ export default {
selectRoute (route) { selectRoute (route) {
const path = route.path.split('/') const path = route.path.split('/')
const name = path[path.length - 1] const name = path[path.length - 1]
const routes = ['', 'gallery', 'presentation', 'schedule', 'pricing', 'contact'] const routes = ['', 'gallery', 'description', 'schedule', 'pricing', 'contact']
this.tab = routes.indexOf(name) this.tab = routes.indexOf(name)
}, },
init () { init () {
@ -374,15 +374,22 @@ export default {
} }
}, },
goToPage () { goToPage () {
window.open(this.publicUrl, '_blank').focus() window.open(this.$store.state.delegate.publicUrl, '_blank').focus()
}, },
openPublishModal () { openPublishModal () {
// compute if the user can ask approval // compute if the user can ask approval
this.$store.commit('VALIDATE_MAIN', isValid => {
if (isValid) {
this.publishModal = true this.publishModal = true
const data = this.$store.state.data } else {
this.canPublish = this.$store.commit('ADD_ALERT', {
data.name.length > 0 && color: 'error',
data.descriptionShort.length > 10 text: 'Vous devez remplir tout les champs requis afin de publier'
})
this.publishModal = false
}
this.canPublish = isValid
})
}, },
askApproval () { askApproval () {
this.askingApprovalLoading = true this.askingApprovalLoading = true

View file

@ -46,14 +46,14 @@ const routes: Array<RouteConfig> = [
component: () => import(/* webpackChunkName: "delegateMain" */ '../views/Delegate/Main.vue') component: () => import(/* webpackChunkName: "delegateMain" */ '../views/Delegate/Main.vue')
}, },
{ {
path: 'contact', path: 'gallery',
name: 'DelegateContact', name: 'DelegateGallery',
component: () => import(/* webpackChunkName: "delegateContact" */ '../views/Delegate/Contact.vue') component: () => import(/* webpackChunkName: "delegateGallery" */ '../views/Delegate/Gallery.vue')
}, },
{ {
path: 'pricing', path: 'description',
name: 'DelegatePricing', name: 'DelegateDescription',
component: () => import(/* webpackChunkName: "delegatePricing" */ '../views/Delegate/Pricing.vue') component: () => import(/* webpackChunkName: "delegateDescription" */ '../views/Delegate/Description.vue')
}, },
{ {
path: 'schedule', path: 'schedule',
@ -61,16 +61,21 @@ const routes: Array<RouteConfig> = [
component: () => import(/* webpackChunkName: "delegateSchedule" */ '../views/Delegate/Schedule.vue') component: () => import(/* webpackChunkName: "delegateSchedule" */ '../views/Delegate/Schedule.vue')
}, },
{ {
path: 'gallery', path: 'pricing',
name: 'DelegateGallery', name: 'DelegatePricing',
component: () => import(/* webpackChunkName: "delegateGallery" */ '../views/Delegate/Gallery.vue') component: () => import(/* webpackChunkName: "delegatePricing" */ '../views/Delegate/Pricing.vue')
}, },
{ {
path: 'presentation', path: 'contact',
name: 'DelegatePresentation', name: 'DelegateContact',
component: () => import(/* webpackChunkName: "delegatePresentation" */ '../views/Delegate/Presentation.vue') component: () => import(/* webpackChunkName: "delegateContact" */ '../views/Delegate/Contact.vue')
} }
] ]
},
{
path: '*',
name: 'NotFound',
component: () => import(/* webpackChunkName: "notFound" */ '../views/NotFound.vue')
} }
] ]

View file

@ -38,11 +38,18 @@ export default new Vuex.Store({
admiName: '', admiName: '',
email: '', email: '',
publicUrl: '' publicUrl: ''
} },
debug: false,
validateMain: false,
validateMainCallback: () => false
}, },
mutations: { mutations: {
SET_DEBUG (state, payload) {
state.debug = payload
},
SET_TITLE (state, payload) { SET_TITLE (state, payload) {
state.title = payload state.title = payload
window.document.title = state.title + ' | Panel forum des associations'
}, },
ADD_ALERT (state, payload) { ADD_ALERT (state, payload) {
state.alert = { state.alert = {
@ -64,6 +71,13 @@ export default new Vuex.Store({
}, },
SET_DELEGATE (state, payload) { SET_DELEGATE (state, payload) {
state.delegate = { ...state.delegate, ...payload } state.delegate = { ...state.delegate, ...payload }
},
VALIDATE_MAIN (state, callback) {
state.validateMain = true
state.validateMainCallback = () => {
state.validateMain = false
return callback()
}
} }
}, },
actions: { actions: {

View file

@ -1,26 +1,13 @@
<template> <template>
<div> <div>
<div class="mb-3 pb-1"> <div class="mb-1 pb-1">
<v-alert <v-alert
border="left" dense
colored-border
type="info" type="info"
elevation="1"
> >
Ici vous pouvez préciser quelques manières de contacter votre association, aucun des champs indiqués n'est obligatoire. Ici vous pouvez préciser quelques manières de contacter votre association, aucun des champs indiqués n'est obligatoire.
</v-alert> </v-alert>
</div> </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 <v-text-field
prepend-icon="call" prepend-icon="call"
label="Numéro de téléphone" label="Numéro de téléphone"
@ -65,9 +52,6 @@ export default {
website: [ website: [
v => v === '' || /.+@.+\..+/.test(v) || "L'email doit être valide" v => v === '' || /.+@.+\..+/.test(v) || "L'email doit être valide"
], ],
email: [
v => v === '' || /.+@.+\..+/.test(v) || "L'email doit être valide"
],
address: [ address: [
v => v.length < 200 || 'Maximum 200 caractères' v => v.length < 200 || 'Maximum 200 caractères'
], ],

View file

@ -1,6 +1,7 @@
<template> <template>
<div> <div>
<!-- COVER START --> <!-- COVER START -->
<div v-if="false" class="mb-3">
<div <div
class="cover-common cover-container" class="cover-common cover-container"
:style="coverStyle"> :style="coverStyle">
@ -30,7 +31,7 @@
</v-tooltip> </v-tooltip>
</div> </div>
</div> </div>
</div>
<AvatarEditor <AvatarEditor
ref="avatarEditor" ref="avatarEditor"
:loading="coverLoading" :loading="coverLoading"
@ -41,10 +42,10 @@
@submitted="handleAvatarEditorSubmitted" @submitted="handleAvatarEditorSubmitted"
/> />
<!-- COVER END --> <!-- COVER END -->
<div class="d-flex justify-end mt-3 mb-3"> <div class="d-flex justify-end mb-3">
<v-btn color="primary" outlined @click="addMediaModal = true"> <v-btn color="primary" outlined @click="addMediaModal = true">
<v-icon left>add</v-icon> <v-icon left>add</v-icon>
Ajouter une image Ajouter un média
</v-btn> </v-btn>
</div> </div>
<div <div
@ -56,10 +57,10 @@
outlined outlined
type="info" type="info"
> >
Aucun medias n'ont été ajoutés pour l'instant Aucun médias n'ont été ajoutés pour l'instant
</v-alert> </v-alert>
</div> </div>
<v-row v-else> <v-row v-else class="justify-center">
<v-col <v-col
v-for="(media, index) in $store.state.data.gallery" v-for="(media, index) in $store.state.data.gallery"
:key="media._id" :key="media._id"
@ -109,6 +110,22 @@
<span>Décaler vers la droite</span> <span>Décaler vers la droite</span>
</v-tooltip> </v-tooltip>
<v-spacer /> <v-spacer />
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn
icon
color="teal"
small
v-bind="attrs"
v-on="on"
@click="shiftRightMedia(media)"
>
<v-icon small>stars</v-icon>
</v-btn>
</template>
<span>Choisir comme couverture</span>
</v-tooltip>
<v-btn icon color="error" small @click="openDeleteMediaModal(media)"> <v-btn icon color="error" small @click="openDeleteMediaModal(media)">
<v-icon small>delete</v-icon> <v-icon small>delete</v-icon>
</v-btn> </v-btn>
@ -124,14 +141,27 @@
<v-card-text> <v-card-text>
<div class="mt-2"> <div class="mt-2">
<v-file-input <v-file-input
prepend-icon="perm_media"
v-model="files" v-model="files"
chips chips
multiple multiple
show-size show-size
counter counter
accept="image/png,image/jpeg,video/mp4"
label="Sélectionnez les images ou vidéos à importer"> label="Sélectionnez les images ou vidéos à importer">
</v-file-input> </v-file-input>
</div> </div>
<div v-if="uploadLoading" class="mt-3">
<v-progress-linear
color="purple"
height="2em"
v-model="uploadProgress"
>
<template v-slot="{ value }">
<strong>{{ Math.ceil(value) }}%</strong>
</template>
</v-progress-linear>
</div>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-btn text color="primary" @click="addMediaModal = false"> <v-btn text color="primary" @click="addMediaModal = false">
@ -172,7 +202,8 @@ export default {
uploadLoading: false, uploadLoading: false,
files: [], files: [],
deleteMediaModal: false, deleteMediaModal: false,
mediaToDelete: {} mediaToDelete: {},
uploadProgress: 50
}), }),
computed: { computed: {
@ -225,7 +256,17 @@ export default {
const formData = new FormData() const formData = new FormData()
results.forEach(r => formData.append(r[0], new Blob([new Uint8Array(r[1])]), r[2])) 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.$apitator.post('/delegate/medias', formData, {
withAuth: true,
onUploadProgress: (progressEvent) => {
console.log(progressEvent)
const totalLength = progressEvent.lengthComputable ? progressEvent.total : progressEvent.target.getResponseHeader('content-length') || progressEvent.target.getResponseHeader('x-decompressed-content-length')
console.log('onUploadProgress', totalLength)
if (totalLength !== null) {
this.uploadProgress = Math.round((progressEvent.loaded * 100) / totalLength)
}
}
}).then(res => {
this.$store.commit('SET_DATA', { gallery: res.data.data.gallery }) this.$store.commit('SET_DATA', { gallery: res.data.data.gallery })
this.$store.commit('ADD_ALERT', { this.$store.commit('ADD_ALERT', {
color: 'success', color: 'success',

View file

@ -1,12 +1,25 @@
<template> <template>
<div> <div>
<div class="">
<v-alert
outlined
dense
type="info"
>
Tout les champs indiqués ici sont obligatoire afin de publier votre page
</v-alert>
</div>
<v-form
ref="form"
v-model="$store.state.mainFormIsValid"
lazy-validation>
<v-row class="mb-6 pb-2"> <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-col cols="12" sm="6" style="align-items: center; justify-content: center; display: flex;">
<v-avatar size="200" style="border: 2px solid #95a5a6"> <v-avatar size="200" style="border: 2px solid #95a5a6">
<img :src="$store.state.data.thumbnail.location" /> <img :src="$store.state.data.thumbnail.location" />
</v-avatar> </v-avatar>
</v-col> </v-col>
<v-col cols="12" sm="12" md="6" style="align-items: center; justify-content: center; display: flex;"> <v-col cols="12" sm="6" style="align-items: center; justify-content: center; display: flex;">
<v-btn @click="$refs.avatarEditor.toggle()" color="primary" outlined> <v-btn @click="$refs.avatarEditor.toggle()" color="primary" outlined>
<v-icon left> <v-icon left>
edit edit
@ -20,10 +33,11 @@
label="Nom de l'association" label="Nom de l'association"
prepend-icon="tag" prepend-icon="tag"
outlined outlined
v-model="$store.state.data.name" /> v-model="$store.state.data.name"
:rules="rules.name" />
<v-select <v-select
label="Catégorie de l'association" label="Catégorie(s) de l'association"
outlined outlined
prepend-icon="category" prepend-icon="category"
item-text="name" item-text="name"
@ -32,15 +46,34 @@
:items="$store.state.tags"> :items="$store.state.tags">
</v-select> </v-select>
<v-row>
<v-col cols="12" sm="6" class="py-0">
<v-text-field
prepend-icon="person"
label="Personne responsable"
outlined
v-model="$store.state.data.contacts.person"
:rules="rules.person" />
</v-col>
<v-col cols="12" sm="6" class="py-0">
<v-text-field
prepend-icon="alternate_email"
label="Email"
outlined
v-model="$store.state.data.contacts.email"
:rules="rules.email" />
</v-col>
</v-row>
<!-- placeholder="Courte description qui apparaitra sur la page d'accueil" -->
<v-textarea <v-textarea
outlined outlined
prepend-icon="description" prepend-icon="description"
label="Description ou résumé rapide" label="Description ou résumé rapide"
placeholder="Courte description qui apparaitra sur la page d'accueil"
:rules="rules.descriptionShort" :rules="rules.descriptionShort"
counter counter
v-model="$store.state.data.descriptionShort" /> v-model="$store.state.data.descriptionShort" />
</v-form>
<AvatarEditor <AvatarEditor
ref="avatarEditor" ref="avatarEditor"
:loading="logoLoading" :loading="logoLoading"
@ -56,10 +89,19 @@ export default {
components: { AvatarEditor }, components: { AvatarEditor },
data: () => ({ data: () => ({
formValid: true,
logoLoading: false, logoLoading: false,
rules: { rules: {
descriptionShort: [v => v.length <= 200 || 'Au maximum 200 caractères'], name: [
logo: [v => !v || v.size < 2000000 || 'La taille du logo doit être inférieur à 2 Mega Octets!'] v => v.length >= 3 || 'Au minimum 3 caractères',
v => v.length <= 50 || 'Au maximum 50 caractères'
],
descriptionShort: [
v => v.length >= 20 || 'Au minimum 20 caractères',
v => v.length <= 200 || 'Au maximum 200 caractères'
],
email: [v => /.+@.+\..+/.test(v) || "L'email est requis et dois être valide"],
person: [v => v.length >= 4 || 'Au minimum 4 caractères']
} }
}), }),
@ -70,6 +112,14 @@ export default {
// }) // })
// }, // },
watch: {
'$store.state.validateMain' (val) {
if (val) {
this.$store.state.validateMainCallback(this.$refs.form.validate())
}
}
},
methods: { methods: {
handleAvatarEditorSubmitted: function (blob) { handleAvatarEditorSubmitted: function (blob) {
const form = new FormData() const form = new FormData()

View file

@ -1,6 +1,4 @@
<template> <template>
<div class="home">
<v-main>
<v-container> <v-container>
<h2 class="text-center">Vous êtes :</h2> <h2 class="text-center">Vous êtes :</h2>
@ -51,8 +49,6 @@
</ul> --> </ul> -->
</v-row> </v-row>
</v-container> </v-container>
</v-main>
</div>
</template> </template>
<script> <script>

43
src/views/NotFound.vue Normal file
View file

@ -0,0 +1,43 @@
<template>
<v-container fluid class="gradient">
<v-layout class="d-flex justify-center mt-3">
<v-card class="text-center pt-10 pb-1 px-5 mt-5" max-width="500px">
<v-card-text class="black--text">
<div style="font-size: 5em">🤷🤷</div>
<div class="text-h4 mb-3 mt-10">Page non trouvée !</div>
<v-divider />
<div class="text-subtitle-1 mt-2">
Avez-vous essayé de l'éteindre et de le rallumer?
<span style="font-size: 1.5em">🔌</span>
</div>
<div class="text-subtitle-1 mt-3 mb-1 text-left">
Liens magiques :
<ul>
<li><a href="https://www.youtube.com/watch?v=2B1ub5g5L0k">Pour les gens friant de video (très émetteur)</a></li>
<li><a href="https://www.youtube.com/watch?v=StfLy3pzno0">Pour les gens qui veulent pas trop consommer de flux vidéo et donc émettre moins de CO2</a></li>
</ul>
</div>
<blockquote class="grey--text text-left">
Si vous voyez des carrés blancs à la place d'emojis alors vous devez mettre à jour votre navigateur ou pire, changer d'OS
</blockquote>
</v-card-text>
</v-card>
</v-layout>
</v-container>
</template>
<script>
export default {
mounted () {
this.$store.commit('SET_TITLE', 'Page non trouvé !')
}
}
</script>
<style scoped>
.gradient {
height: 100vh;
background: rgb(52,152,219);
background: linear-gradient(45deg, rgba(52,152,219,1) 0%, rgba(39,174,96,1) 100%);
}
</style>

View file

@ -9074,10 +9074,10 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
vue-apitator@^0.0.16: vue-apitator@0.0.17:
version "0.0.16" version "0.0.17"
resolved "https://registry.yarnpkg.com/vue-apitator/-/vue-apitator-0.0.16.tgz#4c59f617aa692e7f0d3fd059424c3938a9666bea" resolved "https://registry.yarnpkg.com/vue-apitator/-/vue-apitator-0.0.17.tgz#3fcd925effa833956f07c63c57eaf72523c2dc0f"
integrity sha512-k0hQS8jR10avPTd28vPHjTV+H7aW2pfSz4O3gJ4D1z5J/ne4l+ygNAqQofOzgGL9bKaiojc9Guxr2f34fnUvYg== integrity sha512-xa3ChL3v2pq77x0KYQnSkrt5jSyYhvCGIKRsCQLAyvOqQqA1TPBYazBjQeEvP2FSFficIu9lJHlqp0gflkGy/A==
dependencies: dependencies:
axios "^0.19.0" axios "^0.19.0"
vue "^2.5.17" vue "^2.5.17"