panel/src/layouts/Delegate.vue

531 lines
18 KiB
Vue

<template>
<div>
<div v-if="enabled">
<v-toolbar
color="orange darken-3"
dark
flat
>
<v-toolbar-title>{{ $store.state.data.name }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
outlined
class="mr-3"
:disabled="$store.state.delegate.validationState === 'pending'"
@click="openPublishModal()"
>
Publier
</v-btn>
<v-menu bottom close-on-content-click>
<template v-slot:activator="{ on, attrs }">
<v-btn
class="mr-2"
icon
v-bind="attrs"
v-on="on"
>
<v-icon>more_vert</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item @click="openPreviewModal()">
<v-list-item-title>Prévisualiser votre page</v-list-item-title>
</v-list-item>
<v-list-item @click="goToPage()">
<v-list-item-title>Ouvrir la page publique</v-list-item-title>
</v-list-item>
<v-list-item @click="openDestroyModal()">
<v-list-item-title>Supprimer votre association</v-list-item-title>
</v-list-item>
<v-list-item @click="logout()">
<v-list-item-title>Se déconnecter</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!--
<v-btn icon @click="goToPage()">
<v-icon>open_in_new</v-icon>
</v-btn>
<v-btn icon @click="logout()">
<v-icon>exit_to_app</v-icon>
</v-btn> -->
<template v-slot:extension>
<v-tabs
v-model="tab"
centered
show-arrows
slider-color="accent"
ref="tabs"
>
<v-tab @click="navigate('DelegateMain')">
Présentation
</v-tab>
<v-tab @click="navigate('DelegateGallery')">
Images/vidéos
</v-tab>
<v-tab @click="navigate('DelegateDescription')">
Description
</v-tab>
<v-tab @click="navigate('DelegateSchedule')">
Horaires
</v-tab>
<v-tab @click="navigate('DelegatePricing')">
Tarifs
</v-tab>
<v-tab @click="navigate('DelegateContact')">
Contact
</v-tab>
</v-tabs>
</template>
</v-toolbar>
<v-container fluid>
<v-row class="justify-center">
<v-col cols="12" sm="12" md="8" lg="7" xl="5">
<v-alert
v-if="$store.state.delegate.validationState === 'rejected'"
prominent
dense
type="info"
border="left"
color="error">
<v-row align="center">
<v-col class="grow">
Les changements apportés ont été refusés, vous devez donc les modifier
</v-col>
<v-col class="shrink">
<v-btn outlined @click="rejectionDetailsModal = true">Pourquoi ?</v-btn>
</v-col>
</v-row>
</v-alert>
<v-alert
v-if="$store.state.delegate.validationState === 'pending'"
dense
type="error"
color="error">
Votre association est verrouillée car elle est en attente d'une vérification, même si vous pouvez faire des changements il sera impossible de les sauvegarder.
</v-alert>
<router-view></router-view>
<div class="mt-3 d-flex justify-end">
<v-btn
color="success"
:loading="isSaving"
@click="save()"
:disabled="$store.state.delegate.validationState === 'pending'"
>
<v-icon left>
check_circle
</v-icon>
Sauvegarder
</v-btn>
</div>
</v-col>
</v-row>
</v-container>
</div>
<div v-if="!enabled && loading" class="d-flex align-center justify-center mt-10 pt-10">
<v-progress-circular indeterminate color="primary" />
</div>
<div v-if="!enabled && !loading" class="d-flex align-center justify-center mt-5">
<v-card max-width="600">
<v-card-title>
Connexion au panel de modification
</v-card-title>
<v-card-text>
<p>
Vous n'êtes pas encore connecté à l'interface de modification de votre association, veuillez copier-coller la clé qui vous a été envoyé par e-mail dans la boîte ci-dessous.
</p>
<v-text-field
label="Clé"
autofocus
v-model="token"
:type="showPassword ? 'text' : 'password'"
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
v-on:keydown.enter="submitForm()"
@click:append="showPassword = !showPassword" />
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
@click="submitForm()"
:disabled="token === ''">
Se connecter
</v-btn>
</v-card-actions>
</v-card>
</div>
<v-dialog v-model="publishModal" max-width="700px">
<v-card>
<v-card-title>
Demander une vérification humaine
</v-card-title>
<v-card-text>
<div v-if="canPublish">
<p>
Avant que vos changements ne soient publiés et accessibles à l'adresse <a :href="$store.state.delegate.publicUrl">{{ $store.state.delegatePublicUrl }}</a>, ils doivent être vérifiés par la personne en charge de la publication.
</p>
<p>
Veillez à ne pas demander de vérification manuelle trop souvent : veuillez limiter vos demandes.
Nous vous demandons de bien relire vos différentes modifications apportés afin de corriger d'éventuelles erreurs
</p>
<p>
La vérification étant manuelle et prenant du temps humain nous vous demandons de bien vouloir être patient, le résultat de cette vérification sera notifé à l'adresse {{ this.$store.state.delegate.email }}.
</p>
<v-alert type="warning" class="mb-0">
Attention, l'action de demander une vérification entraîne le verouillage de l'association, il ne sera plus possible de faire des modifications sur cette interface jusqu'a ce que vos changements soient validés ou rejetés.
</v-alert>
</div>
<v-alert v-else type="error" class="mt-2 mb-0">
Vous ne pouvez pas être publié car vous n'avez pas remplis les champs requis
</v-alert>
</v-card-text>
<v-card-actions>
<v-btn text @click="publishModal = false">Fermer</v-btn>
<v-spacer />
<v-btn
color="primary"
text
:disabled="!canPublish"
:loading="askingApprovalLoading"
@click="askApproval()">
Demander une vérification
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="rejectionDetailsModal" max-width="700px">
<v-card>
<v-card-title>
Détails sur le refus de publication
</v-card-title>
<v-card-text>
<p>
Vos changements ont été refusés, veuillez prendre connaissance de la raison :
</p>
<blockquote>
{{ $store.state.delegate.rejectionDescription }}
</blockquote>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="primary" text @click="rejectionDetailsModal = false">Fermer</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="destroyModal" max-width="600px">
<v-card>
<v-card-title>
Êtes vous sur de vouloir supprimer votre association ?
</v-card-title>
<v-card-text>
<p>
<b>Attention, ceci est une action pottentiellement dangereuse !</b>
</p>
<p>
La suppression de votre association entraine une supression <b>DEFINITIVE</b> de manière <b>IMMEDIATE</b> de toute les données associés à votre association (textes, images, vidéos...).
</p>
<p>
Cette action est <b>IRREVERSIBLE</b>.
</p>
</v-card-text>
<v-card-actions>
<div style="display:flex; justify-content:space-between; flex-wrap: wrap; width: 100%">
<v-btn color="primary" text @click="destroyModal = false">Fermer</v-btn>
<v-btn
:loading="destroyLoading"
color="error" text
@click="destroy">
je comprends, supprimer
</v-btn>
</div>
</v-card-actions>
</v-card>
</v-dialog>
<Preview
ref="preview"
:enabled="previewModal"
:id="$store.state.delegate._id"
:slug="$store.state.delegate.slug"
:dialogTitle="'Prévisualisation'"
@close="previewModal = false"
/>
</div>
</template>
<script>
import Preview from '../components/Preview'
export default {
components: {
Preview
},
data: () => ({
enabled: false,
loading: true,
token: '',
loadingHandle: null,
isSaving: false,
tab: 0,
slug: '',
canPublish: false,
publishModal: false,
askingApprovalLoading: false,
rejectionDetailsModal: false,
showPassword: false,
previewModal: false,
destroyModal: false,
destroyLoading: false
}),
created () {
this.init()
},
watch: {
$route (to) {
this.selectRoute(to)
}
},
mounted () {
this.$store.commit('SET_TITLE', "Modification de l'association")
this.selectRoute(this.$route)
/**
* this is very ugly I kown
*/
// setTimeout(() => {
// window.dispatchEvent(new Event('resize'))
// }, 100)
// const i = setInterval(() => {
// window.dispatchEvent(new Event('resize'))
// }, 500)
// setTimeout(() => {
// window.dispatchEvent(new Event('resize'))
// }, 1000)
// setTimeout(() => {
// clearInterval(i)
// }, 3000)
},
methods: {
openPreviewModal () {
this.save(false).then(() => {
this.$refs.preview.reload()
this.previewModal = true
}).catch((err) => {
console.log(err)
})
},
selectRoute (route) {
const path = route.path.split('/')
const name = path[path.length - 1]
const routes = ['', 'gallery', 'description', 'schedule', 'pricing', 'contact']
this.tab = routes.indexOf(name)
},
init () {
this.enabled = false
this.loadingHandle = setTimeout(() => {
this.loading = true
}, 300)
let inUrl = false
let delegateToken = (new URL(window.location)).searchParams.get('delegateToken')
if (this.token !== '') {
delegateToken = this.token
}
if (delegateToken === null) {
delegateToken = window.localStorage.getItem('delegateToken')
if (delegateToken === null || delegateToken === 'null') {
clearTimeout(this.loadingHandle)
this.loading = false
return
}
} else {
inUrl = true
}
// get the size
// this.$apitator.get('/delegate/size', { withAuth: true }).then(res => {
// console.log(res.data.data)
// })
this.$apitator.setAuthorizationToken(delegateToken)
// verify the token
this.$apitator.get('/delegate', { withAuth: true }).then(res => {
window.localStorage.setItem('delegateToken', delegateToken)
if (this.token !== '') {
this.$router.push({ query: { delegateToken: this.token } })
}
clearTimeout(this.loadingHandle)
this.loading = false
this.enabled = true
const organization = res.data.data.organization
const proposedVersion = res.data.data.organization.proposedVersion
const tags = res.data.data.tags
this.$store.commit('SET_DELEGATE', {
_id: organization._id,
adminName: organization.adminName,
email: organization.email,
slug: organization.slugs[organization.slugs.length - 1],
publicUrl: process.env.VUE_APP_BASE_URL + '/association/' + organization.slugs[organization.slugs.length - 1],
validationState: organization.validationState,
rejectionDescription: organization.rejectionDescription
})
this.$store.commit('SET_DATA', proposedVersion)
this.$store.commit('SET_TAGS', tags)
this.$nextTick(() => {
setTimeout(this.$refs.tabs.callSlider, 200)
setTimeout(this.$refs.tabs.callSlider, 400)
setTimeout(this.$refs.tabs.callSlider, 500)
setTimeout(this.$refs.tabs.callSlider, 1000)
setTimeout(this.$refs.tabs.callSlider, 1500)
})
}).catch(err => {
console.log(err)
clearTimeout(this.loadingHandle)
this.loading = false
if (this.token !== '' || inUrl) {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Clé invalide !'
})
}
})
},
logout () {
window.localStorage.removeItem('delegateToken')
this.$router.push('/')
},
save (showSuccessAlert = true) {
return new Promise((resolve, reject) => {
this.isSaving = true
const data = this.$store.state.data
this.$apitator.put('/delegate', data, { withAuth: true }).then(() => {
this.isSaving = false
if (showSuccessAlert) {
this.$store.commit('ADD_ALERT', {
color: 'success',
text: 'Vos changements ont été sauvegardés !'
})
}
resolve()
}).catch(err => {
console.log(err.response)
if (err.response != null) {
console.log(err.response.data)
console.log(err.response.code)
}
this.isSaving = false
if (err.response != null && err.response.status === 413) {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Le contenu renseigné est trop grand, vous devez le réduire'
})
} else {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Impossible de sauvegarder vos changements'
})
}
reject(err)
})
})
},
navigate (routeName) {
if (routeName !== this.$route.name) {
this.$router.push({ name: routeName })
}
},
submitForm () {
if (this.token !== '') {
this.init()
}
},
goToPage () {
window.open(this.$store.state.delegate.publicUrl, '_blank').focus()
},
openPublishModal () {
const open = () => {
// compute if the user can ask approval
// check against rules in the store
const validation = []
const keys = Object.keys(this.$store.state.mainRules)
keys.forEach(key => {
let value = ''
if (key.indexOf('contacts.') !== -1) {
value = this.$store.state.data.contacts[key.replace('contacts.', '')]
} else {
value = this.$store.state.data[key]
}
const rules = this.$store.state.mainRules[key]
validation.push(rules.map(r => r(value)).filter(r => r === true).length === rules.length)
})
validation.push(this.$store.state.data.thumbnail.location.length > 1)
const isValid = validation.filter(v => v).length === validation.length
if (isValid) {
this.publishModal = true
this.save(false).catch((err) => {
console.log(err)
})
} else {
this.$store.commit('ADD_ALERT', {
color: 'error',
text: 'Vous devez remplir tout les champs requis afin de publier'
})
if (this.$route.name !== 'DelegateMain') {
this.$store.commit('ON_MAIN_READY', () => {
return true
})
this.$router.push({ name: 'DelegateMain' }, () => {
this.$store.commit('VALIDATE_MAIN')
})
} else {
this.$store.commit('VALIDATE_MAIN')
}
this.publishModal = false
}
this.canPublish = isValid
}
this.$store.state.validateMain = false
open()
},
askApproval () {
this.askingApprovalLoading = true
this.$apitator.post('/delegate/submit', {}, { withAuth: true }).then(() => {
this.publishModal = false
this.askingApprovalLoading = false
this.$store.commit('SET_DELEGATE', { validationState: 'pending' })
this.$store.commit('ADD_ALERT', {
color: 'success',
text: 'Les changements vont êtres soumis à une vérification manuelle'
})
}).catch(err => {
console.log(err.data)
this.askingApprovalLoading = false
this.$store.commit('ADD_ALERT', {
color: 'error',
text: "Impossible de soumettre l'association"
})
})
},
openDestroyModal () {
this.destroyModal = true
},
destroy () {
this.destroyLoading = true
this.$apitator.delete('/delegate/', { withAuth: true }).then(() => {
this.destroyModal = false
this.destroyLoading = false
this.$store.commit('ADD_ALERT', {
color: 'success',
text: "Votre association vient d'être supprimé, bonne continuation!"
})
this.logout()
}).catch(err => {
console.error(err)
this.destroyLoading = false
this.$store.commit('ADD_ALERT', {
color: 'error',
text: "Impossible de supprimer l'association"
})
})
}
}
}
</script>