mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 16:42:22 +01:00
major on recurrent events
This commit is contained in:
parent
beab52d349
commit
f9e0883eaf
19 changed files with 210 additions and 76 deletions
|
@ -1,5 +1,5 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
nuxt-link.event(:to='`event/${event.id}`' :class='{ withImg: event.image_path }')
|
nuxt-link.event(:to='`event/${event.id}${event.recurrent && "_" + event.start_datetime/1000}`' :class='{ withImg: event.image_path }')
|
||||||
//- image
|
//- image
|
||||||
img(v-if='showImage && event.image_path' :src='`/media/thumb/${event.image_path}`')
|
img(v-if='showImage && event.image_path' :src='`/media/thumb/${event.image_path}`')
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
//- date / place
|
//- date / place
|
||||||
.date
|
.date
|
||||||
div <v-icon name='clock'/> {{event|event_when}}
|
div <v-icon name='clock'/> {{event|when('home')}}
|
||||||
div <v-icon name='map-marker-alt' /> {{event.place.name}}
|
div <v-icon name='map-marker-alt' /> {{event.place.name}}
|
||||||
|
|
||||||
//- p(v-if='showDescription') {{event.description}}
|
//- p(v-if='showDescription') {{event.description}}
|
||||||
|
|
|
@ -5,14 +5,14 @@ div#list
|
||||||
el-timeline-item(
|
el-timeline-item(
|
||||||
v-for='event in events'
|
v-for='event in events'
|
||||||
:key='event.id'
|
:key='event.id'
|
||||||
:timestamp='event|event_when'
|
:timestamp='event|when'
|
||||||
placement='top' icon='el-icon-arrow-down' size='large'
|
placement='top' icon='el-icon-arrow-down' size='large'
|
||||||
)
|
)
|
||||||
|
|
||||||
div.float-right
|
div.float-right
|
||||||
small @{{event.place.name}}
|
small @{{event.place.name}}
|
||||||
|
|
||||||
a(:href='"/event/" + event.id' target='_blank') {{event.title}}
|
a(:href='"/event/" + link(event)' target='_blank') {{event.title}}
|
||||||
hr
|
hr
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
@ -23,6 +23,14 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return { }
|
return { }
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
link (event) {
|
||||||
|
if (event.recurrent) {
|
||||||
|
return `${event.id}_${event.start_datetime/1000}`
|
||||||
|
}
|
||||||
|
return event.id
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
//- v-model='onlyMine'
|
//- v-model='onlyMine'
|
||||||
//- )
|
//- )
|
||||||
el-switch.mt-1.mb-1.ml-2.d-block(
|
el-switch.mt-1.mb-1.ml-2.d-block(
|
||||||
v-if='pastFilter'
|
v-if='recurrentFilter && settings.allow_recurrent_event'
|
||||||
inactive-text=''
|
inactive-text=''
|
||||||
active-text='anche appuntamenti fissi'
|
active-text='anche appuntamenti fissi'
|
||||||
inactive-color='lightgreen'
|
inactive-color='lightgreen'
|
||||||
v-model='showRecurrent'
|
v-model='showRecurrent'
|
||||||
)
|
)
|
||||||
el-switch.mt-1.mb-1.ml-2.d-block(
|
el-switch.mt-1.mb-1.ml-2.d-block(
|
||||||
v-if='recurrentFilter'
|
v-if='pastFilter'
|
||||||
inactive-text='solo futuri'
|
inactive-text='solo futuri'
|
||||||
active-text='anche passati'
|
active-text='anche passati'
|
||||||
inactive-color='lightgreen'
|
inactive-color='lightgreen'
|
||||||
|
@ -45,7 +45,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: mapActions(['setSearchPlaces', 'setSearchTags', 'showPastEvents', 'showRecurrentEvents']),
|
methods: mapActions(['setSearchPlaces', 'setSearchTags', 'showPastEvents', 'showRecurrentEvents']),
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['tags', 'places', 'filters']),
|
...mapState(['tags', 'places', 'filters', 'settings']),
|
||||||
// TOFIX: optimize
|
// TOFIX: optimize
|
||||||
keywords () {
|
keywords () {
|
||||||
const tags = this.tags.map( t => ({ value: 't' + t.tag, label: t.tag, weigth: t.weigth }))
|
const tags = this.tags.map( t => ({ value: 't' + t.tag, label: t.tag, weigth: t.weigth }))
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
"email.confirm": "Il tuo account su gancio è stato attivato e quindi puoi cominciare a pubblicare eventi",
|
"email.confirm": "Il tuo account su gancio è stato attivato e quindi puoi cominciare a pubblicare eventi",
|
||||||
"email.recover": "Ciao, hai richiesto un recupero della password su gancio.",
|
"email.recover": "Ciao, hai richiesto un recupero della password su gancio.",
|
||||||
"email.press_here": "Premi qui",
|
"email.press_here": "Premi qui",
|
||||||
"email.confirm.subject": "Registrazione confermata"
|
"email.confirm.subject": "Registrazione confermata",
|
||||||
|
"email.user_confirm": "Ciao, il tuo account su <a href='{{config.baseurl}}'>{{config.title}}<a/> è stato creato. <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>Confermalo</a>."
|
||||||
}
|
}
|
|
@ -127,7 +127,7 @@ const it = {
|
||||||
recurrent_1m_days: '|Il giorno {days} di ogni mese|I giorni {days} di ogni mese',
|
recurrent_1m_days: '|Il giorno {days} di ogni mese|I giorni {days} di ogni mese',
|
||||||
recurrent_2m_days: '|Il giorno {days} ogni due mesi|I giorni {days} ogni due mesi',
|
recurrent_2m_days: '|Il giorno {days} ogni due mesi|I giorni {days} ogni due mesi',
|
||||||
recurrent_1m_ordinal: 'Il {n} {days} di ogni mese',
|
recurrent_1m_ordinal: 'Il {n} {days} di ogni mese',
|
||||||
recurrent_2m_ordinal: 'Il {n} {days} un mese sì e uno no',
|
recurrent_2m_ordinal: '|Il {n} {days} un mese sì e uno no|Il {n} {days} un mese sì e uno no',
|
||||||
due: 'alle',
|
due: 'alle',
|
||||||
from: 'Dalle',
|
from: 'Dalle',
|
||||||
image_too_big: 'Immagine troppo grande! Massimo 4M'
|
image_too_big: 'Immagine troppo grande! Massimo 4M'
|
||||||
|
|
|
@ -32,6 +32,7 @@ module.exports = {
|
||||||
'element-ui/lib/theme-chalk/index.css'
|
'element-ui/lib/theme-chalk/index.css'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Plugins to load before mounting the App
|
** Plugins to load before mounting the App
|
||||||
*/
|
*/
|
||||||
|
@ -59,6 +60,9 @@ module.exports = {
|
||||||
axios: {
|
axios: {
|
||||||
prefix: '/api'
|
prefix: '/api'
|
||||||
},
|
},
|
||||||
|
router: {
|
||||||
|
middleware: 'i18n'
|
||||||
|
},
|
||||||
auth: {
|
auth: {
|
||||||
strategies: {
|
strategies: {
|
||||||
local: {
|
local: {
|
||||||
|
@ -66,7 +70,9 @@ module.exports = {
|
||||||
login: { url: '/auth/login', method: 'post', propertyName: 'token' },
|
login: { url: '/auth/login', method: 'post', propertyName: 'token' },
|
||||||
logout: false,
|
logout: false,
|
||||||
user: { url: '/auth/user', method: 'get', propertyName: false }
|
user: { url: '/auth/user', method: 'get', propertyName: false }
|
||||||
}
|
},
|
||||||
|
tokenRequired: false,
|
||||||
|
tokenType: 'Bearer'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"nuxt": "^2.8.1",
|
"nuxt": "^2.8.1",
|
||||||
"nuxt-i18n": "^5.12.4",
|
|
||||||
"pg": "^7.11.0",
|
"pg": "^7.11.0",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"sequelize": "^5.10.1",
|
"sequelize": "^5.10.1",
|
||||||
|
@ -71,6 +70,7 @@
|
||||||
"v-calendar": "^1.0.0-beta.14",
|
"v-calendar": "^1.0.0-beta.14",
|
||||||
"vue-awesome": "^3.5.3",
|
"vue-awesome": "^3.5.3",
|
||||||
"vue-clipboard2": "^0.3.0",
|
"vue-clipboard2": "^0.3.0",
|
||||||
|
"vue-i18n": "^8.12.0",
|
||||||
"yargs": "^13.3.0"
|
"yargs": "^13.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -60,11 +60,11 @@
|
||||||
br
|
br
|
||||||
span {{$t(`event.${event.type}_description`)}}
|
span {{$t(`event.${event.type}_description`)}}
|
||||||
el-select.ml-2(v-if='event.type==="recurrent"' v-model='event.recurrent.frequency' placeholder='Frequenza')
|
el-select.ml-2(v-if='event.type==="recurrent"' v-model='event.recurrent.frequency' placeholder='Frequenza')
|
||||||
el-option(label='Tutti i giorni' value='1d' key='1d')
|
//- el-option(label='Tutti i giorni' value='1d' key='1d')
|
||||||
el-option(label='Ogni settimana' value='1w' key='1w')
|
el-option(label='Ogni settimana' value='1w' key='1w')
|
||||||
el-option(label='Ogni due settimane' value='2w' key='2w')
|
el-option(label='Ogni due settimane' value='2w' key='2w')
|
||||||
el-option(label='Ogni mese' value='1m' key='1m')
|
el-option(label='Ogni mese' value='1m' key='1m')
|
||||||
el-option(label='Ogni due mesi' value='2m' key='2m')
|
//- el-option(label='Ogni due mesi' value='2m' key='2m')
|
||||||
|
|
||||||
v-date-picker.mb-2.mt-3(
|
v-date-picker.mb-2.mt-3(
|
||||||
:mode='event.type === "multidate" ? "range" : event.type === "recurrent" ? "multiple" : "single"'
|
:mode='event.type === "multidate" ? "range" : event.type === "recurrent" ? "multiple" : "single"'
|
||||||
|
@ -118,6 +118,7 @@ import { mapActions, mapState, mapGetters } from 'vuex'
|
||||||
import uniq from 'lodash/uniq'
|
import uniq from 'lodash/uniq'
|
||||||
import map from 'lodash/map'
|
import map from 'lodash/map'
|
||||||
import moment from 'dayjs'
|
import moment from 'dayjs'
|
||||||
|
|
||||||
import List from '@/components/List'
|
import List from '@/components/List'
|
||||||
import { Message } from 'element-ui'
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
@ -170,8 +171,9 @@ export default {
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('Error ', e)
|
console.error('Error ', e)
|
||||||
}
|
}
|
||||||
|
moment.locale(store.state.locale)
|
||||||
},
|
},
|
||||||
async asyncData ( { params, $axios, error }) {
|
async asyncData ( { params, $axios, error, store }) {
|
||||||
if (params.edit) {
|
if (params.edit) {
|
||||||
const data = { time: {}, event: { place: {} }}
|
const data = { time: {}, event: { place: {} }}
|
||||||
data.id = params.edit
|
data.id = params.edit
|
||||||
|
|
|
@ -11,18 +11,21 @@
|
||||||
template(slot='label')
|
template(slot='label')
|
||||||
v-icon(name='users')
|
v-icon(name='users')
|
||||||
span.ml-1 {{$t('common.users')}}
|
span.ml-1 {{$t('common.users')}}
|
||||||
|
|
||||||
|
//- ADD NEW USER
|
||||||
el-collapse
|
el-collapse
|
||||||
el-collapse-item
|
el-collapse-item
|
||||||
template(slot='title')
|
template(slot='title')
|
||||||
p {{$t('common.new_user')}}
|
h4 <v-icon name='plus'/> {{$t('common.new_user')}}
|
||||||
el-form(inline)
|
el-form(inline)
|
||||||
el-form-item(:label="$t('common.email')")
|
el-form-item(:label="$t('common.email')")
|
||||||
el-input(v-model='new_user.email')
|
el-input(v-model='new_user.email')
|
||||||
el-form-item(:label="$t('common.password')")
|
//- el-form-item(:label="$t('common.password')")
|
||||||
el-input(v-model='new_user.password' type='password')
|
//- el-input(v-model='new_user.password' type='password')
|
||||||
el-form-item(:label="$t('common.admin')")
|
el-form-item(:label="$t('common.admin')")
|
||||||
el-switch(v-model='new_user.admin')
|
el-switch(v-model='new_user.is_admin')
|
||||||
el-button.float-right(@click='create_user' type='success' plain) {{$t('common.send')}}
|
el-button.float-right(@click='create_user' type='success' plain) {{$t('common.send')}}
|
||||||
|
|
||||||
el-table(:data='paginatedUsers' small)
|
el-table(:data='paginatedUsers' small)
|
||||||
el-table-column(label='Email')
|
el-table-column(label='Email')
|
||||||
template(slot-scope='data')
|
template(slot-scope='data')
|
||||||
|
@ -141,7 +144,6 @@ export default {
|
||||||
loading: false,
|
loading: false,
|
||||||
new_user: {
|
new_user: {
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
|
||||||
admin: false,
|
admin: false,
|
||||||
},
|
},
|
||||||
tab: "0",
|
tab: "0",
|
||||||
|
@ -266,7 +268,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const user = await this.$axios.$post('/user', this.new_user)
|
const user = await this.$axios.$post('/user', this.new_user)
|
||||||
this.new_user = { email: '', password: '', is_admin: false }
|
this.new_user = { email: '', is_admin: false }
|
||||||
Message({
|
Message({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
h5 {{$t('event.not_found')}}
|
h5 {{$t('event.not_found')}}
|
||||||
|
|
||||||
div(v-else)
|
div(v-else)
|
||||||
//- title, where, when
|
//- title
|
||||||
h5.text-center {{event.title}}
|
h5.text-center {{event.title}}
|
||||||
div.nextprev
|
div.nextprev
|
||||||
nuxt-link(v-if='prev' :to='`/event/${prev.id}`')
|
nuxt-link(v-if='prev' :to='`/event/${prev}`')
|
||||||
el-button( type='success' size='mini')
|
el-button( type='success' size='mini')
|
||||||
v-icon(name='chevron-left')
|
v-icon(name='chevron-left')
|
||||||
nuxt-link.float-right(v-if='next' :to='`/event/${next.id}`')
|
nuxt-link.float-right(v-if='next' :to='`/event/${next}`')
|
||||||
el-button(type='success' size='mini')
|
el-button(type='success' size='mini')
|
||||||
v-icon(name='chevron-right')
|
v-icon(name='chevron-right')
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
img.main(:src='imgPath' v-if='event.image_path')
|
img.main(:src='imgPath' v-if='event.image_path')
|
||||||
|
|
||||||
.info
|
.info
|
||||||
div {{event|event_when}}
|
div {{event|when}}
|
||||||
div {{event.place.name}} - {{event.place.address}}
|
div {{event.place.name}} - {{event.place.address}}
|
||||||
|
|
||||||
//- description and tags
|
//- description and tags
|
||||||
|
@ -61,7 +61,8 @@ import { MessageBox } from 'element-ui'
|
||||||
export default {
|
export default {
|
||||||
name: 'Event',
|
name: 'Event',
|
||||||
// transition: null,
|
// transition: null,
|
||||||
// Watch for $route.query.page to call Component methods (asyncData, fetch, validate, layout, etc.)
|
// Watch for $route.query.page to call
|
||||||
|
// Component methods (asyncData, fetch, validate, layout, etc.)
|
||||||
// watchQuery: ['id'],
|
// watchQuery: ['id'],
|
||||||
// Key for <NuxtChild> (transitions)
|
// Key for <NuxtChild> (transitions)
|
||||||
// key: to => to.fullPath,
|
// key: to => to.fullPath,
|
||||||
|
@ -77,8 +78,10 @@ export default {
|
||||||
title: this.event.title,
|
title: this.event.title,
|
||||||
meta: [
|
meta: [
|
||||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||||
{ hid: 'description', name: 'description', content: this.event.description.slice(0, 1000) },
|
{ hid: 'description', name: 'description',
|
||||||
{ hid: 'og-description', name: 'og:description', content: this.event.description.slice(0, 100) },
|
content: this.event.description.slice(0, 1000) },
|
||||||
|
{ hid: 'og-description', name: 'og:description',
|
||||||
|
content: this.event.description.slice(0, 100) },
|
||||||
{ hid: 'og-title', property: 'og:title', content: this.event.title },
|
{ hid: 'og-title', property: 'og:title', content: this.event.title },
|
||||||
{ hid: 'og-url', property: 'og:url', content: `event/${this.event.id}` },
|
{ hid: 'og-url', property: 'og:url', content: `event/${this.event.id}` },
|
||||||
{ property: 'og:type', content: 'event'},
|
{ property: 'og:type', content: 'event'},
|
||||||
|
@ -97,8 +100,10 @@ export default {
|
||||||
},
|
},
|
||||||
async asyncData ( { $axios, params, error }) {
|
async asyncData ( { $axios, params, error }) {
|
||||||
try {
|
try {
|
||||||
const event = await $axios.$get(`/event/${params.id}`)
|
const [ id, start_datetime ] = params.id.split('_')
|
||||||
return { event, id: params.id }
|
const event = await $axios.$get(`/event/${id}`)
|
||||||
|
event.start_datetime = start_datetime*1000
|
||||||
|
return { event, id }
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
error({ statusCode: 404, message: 'Event not found'})
|
error({ statusCode: 404, message: 'Event not found'})
|
||||||
}
|
}
|
||||||
|
@ -108,18 +113,27 @@ export default {
|
||||||
...mapState(['settings']),
|
...mapState(['settings']),
|
||||||
next () {
|
next () {
|
||||||
let found = false
|
let found = false
|
||||||
return this.filteredEvents.find(e => {
|
const event = this.filteredEvents.find(e => {
|
||||||
if (found) return e
|
if (found) return e
|
||||||
if (e.id === this.event.id) found = true
|
if (e.start_datetime === this.event.start_datetime && e.id === this.event.id) found = true
|
||||||
})
|
})
|
||||||
|
if (!event) return false
|
||||||
|
if (event.recurrent) {
|
||||||
|
return `${event.id}_${event.start_datetime/1000}`
|
||||||
|
}
|
||||||
|
return event.id
|
||||||
},
|
},
|
||||||
prev () {
|
prev () {
|
||||||
let prev = false
|
let event = false
|
||||||
this.filteredEvents.find(e => {
|
this.filteredEvents.find(e => {
|
||||||
if (e.id === this.event.id) return true
|
if (e.start_datetime === this.event.start_datetime && e.id === this.event.id) return true
|
||||||
prev = e
|
event = e
|
||||||
})
|
})
|
||||||
return prev
|
if (!event) return false
|
||||||
|
if (event.recurrent) {
|
||||||
|
return `${event.id}_${event.start_datetime/1000}`
|
||||||
|
}
|
||||||
|
return event.id
|
||||||
},
|
},
|
||||||
imgPath () {
|
imgPath () {
|
||||||
return this.event.image_path && '/media/' + this.event.image_path
|
return this.event.image_path && '/media/' + this.event.image_path
|
||||||
|
|
57
pages/user_confirm/_code.vue
Normal file
57
pages/user_confirm/_code.vue
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<template lang="pug">
|
||||||
|
el-card
|
||||||
|
nuxt-link.float-right(to='/')
|
||||||
|
el-button(circle icon='el-icon-close' type='danger' size='small' plain)
|
||||||
|
|
||||||
|
h5 <img src='/favicon.ico'/> {{$t('common.activate_user')}}
|
||||||
|
div(v-if='valid')
|
||||||
|
el-form
|
||||||
|
el-form-item {{$t('common.password')}}
|
||||||
|
el-input(type='password', v-model='new_password')
|
||||||
|
el-button(plain type="success" icon='el-icon-send', @click='change_password') {{$t('common.send')}}
|
||||||
|
|
||||||
|
div(v-else) {{$t('recover.not_valid_code')}}
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Recover',
|
||||||
|
data () {
|
||||||
|
return { new_password: '' }
|
||||||
|
},
|
||||||
|
async asyncData({ params, $axios }) {
|
||||||
|
const code = params.code
|
||||||
|
try {
|
||||||
|
const valid = await $axios.$post('/user/check_recover_code', { recover_code: code })
|
||||||
|
return { valid, code }
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return { valid: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async change_password () {
|
||||||
|
try {
|
||||||
|
const res = await this.$axios.$post('/user/recover_password', { recover_code: this.code, password: this.new_password })
|
||||||
|
Message({
|
||||||
|
showClose: true,
|
||||||
|
type: 'success',
|
||||||
|
message: this.$t('common.password_updated')
|
||||||
|
})
|
||||||
|
this.$router.replace('/login')
|
||||||
|
} catch(e) {
|
||||||
|
Message({
|
||||||
|
showClose: true,
|
||||||
|
type: 'warning',
|
||||||
|
message: e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -3,22 +3,52 @@ import moment from 'dayjs'
|
||||||
import 'dayjs/locale/it'
|
import 'dayjs/locale/it'
|
||||||
|
|
||||||
export default ({ app, store }) => {
|
export default ({ app, store }) => {
|
||||||
moment.locale(store.state.locale)
|
|
||||||
Vue.filter('linkify', value => value.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>'))
|
|
||||||
Vue.filter('datetime', value => moment(value).format('ddd, D MMMM HH:mm'))
|
|
||||||
Vue.filter('short_datetime', value => moment(value).format('D/MM HH:mm'))
|
|
||||||
Vue.filter('hour', value => moment(value).format('HH:mm'))
|
|
||||||
Vue.filter('day', value => moment(value).format('dddd, D MMMM'))
|
|
||||||
Vue.filter('month', value => moment(value).format('MMM'))
|
|
||||||
Vue.filter('event_when', event => {
|
|
||||||
|
|
||||||
|
// replace links with anchors
|
||||||
|
// TODO: remove fb tracking id
|
||||||
|
Vue.filter('linkify', value => value.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>'))
|
||||||
|
|
||||||
|
// Vue.filter('datetime', value => moment(value).locale(store.state.locale).format('ddd, D MMMM HH:mm'))
|
||||||
|
// Vue.filter('short_datetime', value => moment(value).locale(store.state.locale).format('D/MM HH:mm'))
|
||||||
|
// Vue.filter('hour', value => moment(value).locale(store.state.locale).format('HH:mm'))
|
||||||
|
|
||||||
|
// shown in mobile homepage
|
||||||
|
Vue.filter('day', value => moment(value).locale(store.state.locale).format('dddd, D MMMM'))
|
||||||
|
// Vue.filter('month', value => moment(value).locale(store.state.locale).format('MMM'))
|
||||||
|
|
||||||
|
// format event start/end datetime based on page
|
||||||
|
Vue.filter('when', (event, where) => {
|
||||||
|
moment.locale(store.state.locale)
|
||||||
|
|
||||||
|
//{start,end}_datetime are unix timestamp
|
||||||
const start = moment(event.start_datetime)
|
const start = moment(event.start_datetime)
|
||||||
const end = moment(event.end_datetime)
|
const end = moment(event.end_datetime)
|
||||||
|
|
||||||
|
const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm)')}`
|
||||||
|
|
||||||
|
// recurrent event
|
||||||
|
if (event.recurrent && where !== 'home') {
|
||||||
|
const { frequency, days, type } = JSON.parse(event.recurrent)
|
||||||
|
if ( frequency === '1w' || frequency === '2w' ) {
|
||||||
|
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_days`, days.length, {days: days.map(d => moment().day(d-1).format('dddd'))})
|
||||||
|
return `${normal} - ${recurrent}`
|
||||||
|
} else if (frequency === '1m' || frequency === '2m') {
|
||||||
|
const d = type === 'ordinal' ? days : days.map(d => moment().day(d-1).format('dddd'))
|
||||||
|
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_${type}`, days.length, {days: d})
|
||||||
|
return `${normal} - ${recurrent}`
|
||||||
|
}
|
||||||
|
return 'recurrent '
|
||||||
|
}
|
||||||
|
|
||||||
|
// multidate
|
||||||
if (event.multidate) {
|
if (event.multidate) {
|
||||||
return `${start.format('ddd, D MMMM (HH:mm)')} - ${end.format('ddd, D MMMM')}`
|
return `${start.format('ddd, D MMMM (HH:mm)')} - ${end.format('ddd, D MMMM')}`
|
||||||
} else if (event.end_datetime && event.end_datetime !== event.start_datetime)
|
}
|
||||||
return `${start.format('ddd, D MMMM (HH:mm-')}${end.format('HH:mm)')}`
|
|
||||||
else
|
// normal event
|
||||||
return start.format('dddd, D MMMM (HH:mm)')
|
if (event.end_datetime && event.end_datetime !== event.start_datetime) {
|
||||||
|
return `${start.format('ddd, D MMMM (HH:mm-')}${end.format('HH:mm)')}`
|
||||||
|
}
|
||||||
|
return start.format('dddd, D MMMM (HH:mm)')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from 'vue-i18n'
|
||||||
import it from '@/locales/it.js'
|
import it from '../locales/it.js'
|
||||||
import en from '@/locales/en.js'
|
import en from '../locales/en.js'
|
||||||
|
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n)
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,13 @@ const eventController = {
|
||||||
const id = req.params.event_id
|
const id = req.params.event_id
|
||||||
let event = await Event.findByPk(id, {
|
let event = await Event.findByPk(id, {
|
||||||
plain: true,
|
plain: true,
|
||||||
attributes: { exclude: ['createdAt', 'updatedAt'] },
|
attributes: {
|
||||||
|
exclude: ['createdAt', 'updatedAt', 'start_datetime', 'end_datetime'],
|
||||||
|
include: [
|
||||||
|
[Sequelize.literal('start_datetime*1000'), 'start_datetime'],
|
||||||
|
[Sequelize.literal('end_datetime*1000'), 'end_datetime']
|
||||||
|
]
|
||||||
|
},
|
||||||
include: [
|
include: [
|
||||||
{ model: Tag, attributes: ['tag', 'weigth'], through: { attributes: [] } },
|
{ model: Tag, attributes: ['tag', 'weigth'], through: { attributes: [] } },
|
||||||
{ model: Place, attributes: ['name', 'address'] },
|
{ model: Place, attributes: ['name', 'address'] },
|
||||||
|
@ -106,7 +112,6 @@ const eventController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async confirm(req, res) {
|
async confirm(req, res) {
|
||||||
console.error('confirm event')
|
|
||||||
const id = Number(req.params.event_id)
|
const id = Number(req.params.event_id)
|
||||||
const event = await Event.findByPk(id)
|
const event = await Event.findByPk(id)
|
||||||
if (!event) return res.sendStatus(404)
|
if (!event) return res.sendStatus(404)
|
||||||
|
@ -181,7 +186,8 @@ const eventController = {
|
||||||
.year(req.params.year)
|
.year(req.params.year)
|
||||||
.month(req.params.month)
|
.month(req.params.month)
|
||||||
.startOf('month')
|
.startOf('month')
|
||||||
.startOf('isoWeek')
|
.startOf('week')
|
||||||
|
console.error('start ', start)
|
||||||
|
|
||||||
let end = moment()
|
let end = moment()
|
||||||
.year(req.params.year)
|
.year(req.params.year)
|
||||||
|
@ -190,7 +196,7 @@ const eventController = {
|
||||||
|
|
||||||
const shownDays = end.diff(start, 'days')
|
const shownDays = end.diff(start, 'days')
|
||||||
if (shownDays <= 35) end = end.add(1, 'week')
|
if (shownDays <= 35) end = end.add(1, 'week')
|
||||||
end = end.endOf('isoWeek')
|
end = end.endOf('week')
|
||||||
|
|
||||||
let events = await Event.findAll({
|
let events = await Event.findAll({
|
||||||
where: {
|
where: {
|
||||||
|
@ -225,7 +231,7 @@ const eventController = {
|
||||||
const recurrent = JSON.parse(e.recurrent)
|
const recurrent = JSON.parse(e.recurrent)
|
||||||
if (!recurrent.frequency) return false
|
if (!recurrent.frequency) return false
|
||||||
|
|
||||||
let cursor = moment(start).startOf('isoWeek')
|
let cursor = moment(start).startOf('week')
|
||||||
const start_date = moment(e.start_datetime)
|
const start_date = moment(e.start_datetime)
|
||||||
const duration = moment(e.end_datetime).diff(start_date, 's')
|
const duration = moment(e.end_datetime).diff(start_date, 's')
|
||||||
const frequency = recurrent.frequency
|
const frequency = recurrent.frequency
|
||||||
|
@ -234,6 +240,7 @@ const eventController = {
|
||||||
|
|
||||||
// default frequency is '1d' => each day
|
// default frequency is '1d' => each day
|
||||||
const toAdd = { n: 1, unit: 'day'}
|
const toAdd = { n: 1, unit: 'day'}
|
||||||
|
cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
|
||||||
|
|
||||||
// each week or 2 (search for the first specified day)
|
// each week or 2 (search for the first specified day)
|
||||||
if (frequency === '1w' || frequency === '2w') {
|
if (frequency === '1w' || frequency === '2w') {
|
||||||
|
@ -244,23 +251,31 @@ const eventController = {
|
||||||
}
|
}
|
||||||
toAdd.n = Number(frequency[0])
|
toAdd.n = Number(frequency[0])
|
||||||
toAdd.unit = 'week';
|
toAdd.unit = 'week';
|
||||||
cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
|
// cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// each month or 2
|
// each month or 2
|
||||||
// if (frequency === '1m' || frequency === '2m') {
|
if (frequency === '1m' || frequency === '2m') {
|
||||||
// // find first match
|
// find first match
|
||||||
// if (type) {
|
toAdd.n = 1
|
||||||
|
toAdd.unit = 'month'
|
||||||
|
if (type === 'weekday') {
|
||||||
|
|
||||||
// }
|
} else if (type === 'ordinal') {
|
||||||
// }
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add event at specified frequency
|
// add event at specified frequency
|
||||||
while (true) {
|
while (true) {
|
||||||
let first_event_of_week = cursor.clone()
|
let first_event_of_week = cursor.clone()
|
||||||
days.forEach(d => {
|
days.forEach(d => {
|
||||||
cursor.day(d-1)
|
if (type === 'ordinal') {
|
||||||
if (cursor.isAfter(dueTo)) return
|
cursor.date(d)
|
||||||
|
} else {
|
||||||
|
cursor.day(d-1)
|
||||||
|
}
|
||||||
|
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) return
|
||||||
e.start_datetime = cursor.unix()*1000
|
e.start_datetime = cursor.unix()*1000
|
||||||
e.end_datetime = e.start_datetime+(duration*1000)// cursor.clone().hour(end_datetime.hour()).minute(end_datetime.minute()).unix()*1000
|
e.end_datetime = e.start_datetime+(duration*1000)// cursor.clone().hour(end_datetime.hour()).minute(end_datetime.minute()).unix()*1000
|
||||||
events.push( Object.assign({}, e) )
|
events.push( Object.assign({}, e) )
|
||||||
|
|
|
@ -33,7 +33,7 @@ const userController = {
|
||||||
},
|
},
|
||||||
config.secret
|
config.secret
|
||||||
)
|
)
|
||||||
|
res.cookie('auth._token.local', 'Bearer ' + accessToken)
|
||||||
res.json({ token: accessToken })
|
res.json({ token: accessToken })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,9 @@ const userController = {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
try {
|
||||||
req.body.is_active = true
|
req.body.is_active = true
|
||||||
|
req.body.recover_code = crypto.randomBytes(16).toString('hex')
|
||||||
const user = await User.create(req.body)
|
const user = await User.create(req.body)
|
||||||
|
mail.send(user.email, 'user_confirm', { user, config })
|
||||||
res.json(user)
|
res.json(user)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(404).json(e)
|
res.status(404).json(e)
|
||||||
|
|
|
@ -12,13 +12,12 @@ const userController = require('./controller/user')
|
||||||
const settingsController = require('./controller/settings')
|
const settingsController = require('./controller/settings')
|
||||||
|
|
||||||
const storage = require('./storage')
|
const storage = require('./storage')
|
||||||
|
|
||||||
const upload = multer({ storage })
|
const upload = multer({ storage })
|
||||||
|
|
||||||
const api = express.Router()
|
const api = express.Router()
|
||||||
api.use(cookieParser())
|
api.use(cookieParser())
|
||||||
api.use(bodyParser.urlencoded({ extended: false }))
|
api.use(bodyParser.urlencoded({ extended: false }))
|
||||||
api.use(bodyParser.json())
|
api.use(bodyParser.json())
|
||||||
// api.use(settingsController.init)
|
|
||||||
|
|
||||||
const jwt = expressJwt({
|
const jwt = expressJwt({
|
||||||
secret: config.secret,
|
secret: config.secret,
|
||||||
|
|
1
server/emails/user_confirm/html.pug
Normal file
1
server/emails/user_confirm/html.pug
Normal file
|
@ -0,0 +1 @@
|
||||||
|
p !{t('email.user_confirm', { config, user })}
|
1
server/emails/user_confirm/subject.pug
Normal file
1
server/emails/user_confirm/subject.pug
Normal file
|
@ -0,0 +1 @@
|
||||||
|
= `[Gancio] Richiesta password recovery`
|
|
@ -1,7 +1,5 @@
|
||||||
import moment from 'dayjs'
|
import moment from 'dayjs'
|
||||||
import intersection from 'lodash/intersection'
|
import intersection from 'lodash/intersection'
|
||||||
import map from 'lodash/map'
|
|
||||||
import filter from 'lodash/filter'
|
|
||||||
import find from 'lodash/find'
|
import find from 'lodash/find'
|
||||||
|
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
|
@ -147,10 +145,8 @@ export const actions = {
|
||||||
commit('setSettings', settings)
|
commit('setSettings', settings)
|
||||||
|
|
||||||
// apply settings
|
// apply settings
|
||||||
commit('showRecurrentEvents', settings.recurrent_event_visible)
|
commit('showRecurrentEvents', settings.allow_recurrent_event && settings.recurrent_event_visible)
|
||||||
|
|
||||||
const lang = req.acceptsLanguages('en', 'it')
|
|
||||||
commit('setLocale', lang || 'it')
|
|
||||||
},
|
},
|
||||||
async updateEvents({ commit }, page) {
|
async updateEvents({ commit }, page) {
|
||||||
const events = await this.$axios.$get(`/event/${page.month - 1}/${page.year}`)
|
const events = await this.$axios.$get(`/event/${page.month - 1}/${page.year}`)
|
||||||
|
|
Loading…
Reference in a new issue