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",
"tiptap-vuetify": "^2.23.0",
"vue": "^2.6.11",
"vue-apitator": "^0.0.16",
"vue-apitator": "0.0.17",
"vue-class-component": "^7.2.3",
"vue-croppa": "^1.3.8",
"vue-input-facade": "^1.3.1",

View file

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

View file

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

View file

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

View file

@ -46,14 +46,14 @@ const routes: Array<RouteConfig> = [
component: () => import(/* webpackChunkName: "delegateMain" */ '../views/Delegate/Main.vue')
},
{
path: 'contact',
name: 'DelegateContact',
component: () => import(/* webpackChunkName: "delegateContact" */ '../views/Delegate/Contact.vue')
path: 'gallery',
name: 'DelegateGallery',
component: () => import(/* webpackChunkName: "delegateGallery" */ '../views/Delegate/Gallery.vue')
},
{
path: 'pricing',
name: 'DelegatePricing',
component: () => import(/* webpackChunkName: "delegatePricing" */ '../views/Delegate/Pricing.vue')
path: 'description',
name: 'DelegateDescription',
component: () => import(/* webpackChunkName: "delegateDescription" */ '../views/Delegate/Description.vue')
},
{
path: 'schedule',
@ -61,16 +61,21 @@ const routes: Array<RouteConfig> = [
component: () => import(/* webpackChunkName: "delegateSchedule" */ '../views/Delegate/Schedule.vue')
},
{
path: 'gallery',
name: 'DelegateGallery',
component: () => import(/* webpackChunkName: "delegateGallery" */ '../views/Delegate/Gallery.vue')
path: 'pricing',
name: 'DelegatePricing',
component: () => import(/* webpackChunkName: "delegatePricing" */ '../views/Delegate/Pricing.vue')
},
{
path: 'presentation',
name: 'DelegatePresentation',
component: () => import(/* webpackChunkName: "delegatePresentation" */ '../views/Delegate/Presentation.vue')
path: 'contact',
name: 'DelegateContact',
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: '',
email: '',
publicUrl: ''
}
},
debug: false,
validateMain: false,
validateMainCallback: () => false
},
mutations: {
SET_DEBUG (state, payload) {
state.debug = payload
},
SET_TITLE (state, payload) {
state.title = payload
window.document.title = state.title + ' | Panel forum des associations'
},
ADD_ALERT (state, payload) {
state.alert = {
@ -64,6 +71,13 @@ export default new Vuex.Store({
},
SET_DELEGATE (state, payload) {
state.delegate = { ...state.delegate, ...payload }
},
VALIDATE_MAIN (state, callback) {
state.validateMain = true
state.validateMainCallback = () => {
state.validateMain = false
return callback()
}
}
},
actions: {

View file

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

View file

@ -1,6 +1,7 @@
<template>
<div>
<!-- COVER START -->
<div v-if="false" class="mb-3">
<div
class="cover-common cover-container"
:style="coverStyle">
@ -30,7 +31,7 @@
</v-tooltip>
</div>
</div>
</div>
<AvatarEditor
ref="avatarEditor"
:loading="coverLoading"
@ -41,10 +42,10 @@
@submitted="handleAvatarEditorSubmitted"
/>
<!-- 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-icon left>add</v-icon>
Ajouter une image
Ajouter un média
</v-btn>
</div>
<div
@ -56,10 +57,10 @@
outlined
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>
</div>
<v-row v-else>
<v-row v-else class="justify-center">
<v-col
v-for="(media, index) in $store.state.data.gallery"
:key="media._id"
@ -109,6 +110,22 @@
<span>Décaler vers la droite</span>
</v-tooltip>
<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-icon small>delete</v-icon>
</v-btn>
@ -124,14 +141,27 @@
<v-card-text>
<div class="mt-2">
<v-file-input
prepend-icon="perm_media"
v-model="files"
chips
multiple
show-size
counter
accept="image/png,image/jpeg,video/mp4"
label="Sélectionnez les images ou vidéos à importer">
</v-file-input>
</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-actions>
<v-btn text color="primary" @click="addMediaModal = false">
@ -172,7 +202,8 @@ export default {
uploadLoading: false,
files: [],
deleteMediaModal: false,
mediaToDelete: {}
mediaToDelete: {},
uploadProgress: 50
}),
computed: {
@ -225,7 +256,17 @@ export default {
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.$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('ADD_ALERT', {
color: 'success',

View file

@ -1,12 +1,25 @@
<template>
<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-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">
<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-col cols="12" sm="6" style="align-items: center; justify-content: center; display: flex;">
<v-btn @click="$refs.avatarEditor.toggle()" color="primary" outlined>
<v-icon left>
edit
@ -20,10 +33,11 @@
label="Nom de l'association"
prepend-icon="tag"
outlined
v-model="$store.state.data.name" />
v-model="$store.state.data.name"
:rules="rules.name" />
<v-select
label="Catégorie de l'association"
label="Catégorie(s) de l'association"
outlined
prepend-icon="category"
item-text="name"
@ -32,15 +46,34 @@
:items="$store.state.tags">
</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
outlined
prepend-icon="description"
label="Description ou résumé rapide"
placeholder="Courte description qui apparaitra sur la page d'accueil"
:rules="rules.descriptionShort"
counter
v-model="$store.state.data.descriptionShort" />
</v-form>
<AvatarEditor
ref="avatarEditor"
:loading="logoLoading"
@ -56,10 +89,19 @@ export default {
components: { AvatarEditor },
data: () => ({
formValid: true,
logoLoading: false,
rules: {
descriptionShort: [v => v.length <= 200 || 'Au maximum 200 caractères'],
logo: [v => !v || v.size < 2000000 || 'La taille du logo doit être inférieur à 2 Mega Octets!']
name: [
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: {
handleAvatarEditorSubmitted: function (blob) {
const form = new FormData()

View file

@ -1,6 +1,4 @@
<template>
<div class="home">
<v-main>
<v-container>
<h2 class="text-center">Vous êtes :</h2>
@ -51,8 +49,6 @@
</ul> -->
</v-row>
</v-container>
</v-main>
</div>
</template>
<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"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
vue-apitator@^0.0.16:
version "0.0.16"
resolved "https://registry.yarnpkg.com/vue-apitator/-/vue-apitator-0.0.16.tgz#4c59f617aa692e7f0d3fd059424c3938a9666bea"
integrity sha512-k0hQS8jR10avPTd28vPHjTV+H7aW2pfSz4O3gJ4D1z5J/ne4l+ygNAqQofOzgGL9bKaiojc9Guxr2f34fnUvYg==
vue-apitator@0.0.17:
version "0.0.17"
resolved "https://registry.yarnpkg.com/vue-apitator/-/vue-apitator-0.0.17.tgz#3fcd925effa833956f07c63c57eaf72523c2dc0f"
integrity sha512-xa3ChL3v2pq77x0KYQnSkrt5jSyYhvCGIKRsCQLAyvOqQqA1TPBYazBjQeEvP2FSFficIu9lJHlqp0gflkGy/A==
dependencies:
axios "^0.19.0"
vue "^2.5.17"