stream of consciousness
This commit is contained in:
parent
8c3642e868
commit
5883589e19
35 changed files with 347 additions and 409 deletions
|
@ -1 +1,2 @@
|
|||
**/node_modules
|
||||
client/node_modules
|
||||
node_modules
|
||||
|
|
3
.env
3
.env
|
@ -1,5 +1,8 @@
|
|||
BASE_URL=http://localhost:12300
|
||||
|
||||
TITLE=Gancio
|
||||
DESCRIPTION=diocane
|
||||
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
|
||||
SMTP_HOST=mail.example.com
|
||||
|
|
|
@ -6,7 +6,7 @@ WORKDIR /usr/src/app
|
|||
COPY package.json .
|
||||
|
||||
# install backend dependencies
|
||||
RUN yarn install
|
||||
RUN yarn
|
||||
|
||||
# copy source
|
||||
COPY . .
|
||||
|
|
23
app/api.js
23
app/api.js
|
@ -1,6 +1,5 @@
|
|||
const express = require('express')
|
||||
const { isAuth, isAdmin } = require('./auth')
|
||||
|
||||
const { fillUser, isAuth, isAdmin } = require('./auth')
|
||||
const eventController = require('./controller/event')
|
||||
const exportController = require('./controller/export')
|
||||
// const botController = require('./controller/bot')
|
||||
|
@ -12,8 +11,7 @@ const api = express.Router()
|
|||
// USER API
|
||||
const userController = require('./controller/user')
|
||||
|
||||
api.route('/login')
|
||||
.post(userController.login)
|
||||
api.post('/login', userController.login)
|
||||
|
||||
api.route('/user')
|
||||
.post(userController.register)
|
||||
|
@ -25,23 +23,22 @@ api.put('/tag', isAuth, isAdmin, eventController.updateTag)
|
|||
api.put('/place', isAuth, isAdmin, eventController.updatePlace)
|
||||
|
||||
api.route('/user/event')
|
||||
.post(isAuth, upload.single('image'), userController.addEvent)
|
||||
.post(fillUser, upload.single('image'), userController.addEvent)
|
||||
.get(isAuth, userController.getMyEvents)
|
||||
.put(isAuth, upload.single('image'), userController.updateEvent)
|
||||
|
||||
api.route('/user/event/:id')
|
||||
.delete(isAuth, userController.delEvent)
|
||||
api.delete('/user/event/:id', isAuth, userController.delEvent)
|
||||
|
||||
api.get('/event/meta', eventController.getMeta)
|
||||
api.route('/event/:event_id')
|
||||
.get(eventController.get)
|
||||
api.get('/event/unconfirmed', isAuth, isAdmin, eventController.getUnconfirmed)
|
||||
api.post('/event/reminder', eventController.addReminder)
|
||||
|
||||
api.get('/event/:event_id', eventController.get)
|
||||
api.get('/event/confirm/:event_id', isAuth, isAdmin, eventController.confirm)
|
||||
|
||||
// api.get('/export/feed', exportController.feed)
|
||||
// api.get('/export/ics', exportController.ics)
|
||||
api.get('/export/:type', exportController.export)
|
||||
|
||||
api.route('/event/:year/:month')
|
||||
.get(eventController.getAll)
|
||||
api.get('/event/:year/:month', eventController.getAll)
|
||||
|
||||
api.post('/user/getauthurl', isAuth, userController.getAuthURL)
|
||||
api.post('/user/code', isAuth, userController.code)
|
||||
|
|
10
app/auth.js
10
app/auth.js
|
@ -3,6 +3,16 @@ const config = require('./config')
|
|||
const User = require('./models/user')
|
||||
|
||||
const Auth = {
|
||||
async fillUser (req, res, next) {
|
||||
const token = req.body.token || req.params.token || req.headers['x-access-token']
|
||||
console.log('[AUTH] ', token)
|
||||
if (!token) next()
|
||||
jwt.verify(token, config.secret, async (err, decoded) => {
|
||||
if (err) next()
|
||||
req.user = await User.findOne({ where: { email: decoded.email, is_active: true } })
|
||||
next()
|
||||
})
|
||||
},
|
||||
async isAuth (req, res, next) {
|
||||
const token = req.body.token || req.params.token || req.headers['x-access-token']
|
||||
console.log('[AUTH] ', token)
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/* backend configuration */
|
||||
let db = {}
|
||||
let apiurl
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
db = {
|
||||
host: process.env.DB_HOST,
|
||||
|
@ -7,11 +10,13 @@ if (process.env.NODE_ENV === 'production') {
|
|||
database: process.env.DB_NAME,
|
||||
dialect: 'postgres'
|
||||
}
|
||||
apiurl = process.env.BASE_URL + '/api'
|
||||
} else {
|
||||
db = {
|
||||
dialect: 'sqlite',
|
||||
storage: './db.sqlite'
|
||||
}
|
||||
apiurl = 'http://localhost:9000'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -21,7 +26,7 @@ module.exports = {
|
|||
description: process.env.DESCRIPTION || 'A calendar for radical communities',
|
||||
|
||||
baseurl: process.env.BASE_URL || 'http://localhost:8080',
|
||||
apiurl: process.env.API_URL || 'http://localhost:9000',
|
||||
apiurl,
|
||||
db,
|
||||
admin: process.env.ADMIN_EMAIL,
|
||||
|
||||
|
|
|
@ -7,64 +7,65 @@ const moment = require('moment')
|
|||
moment.locale('it')
|
||||
const botController = {
|
||||
bots: [],
|
||||
async initialize () {
|
||||
console.log('initialize bots')
|
||||
const botUsers = await User.findAll({ where: { mastodon_auth: { [Op.ne]: null } } })
|
||||
console.log(botUsers)
|
||||
botController.bots = botUsers.map(user => {
|
||||
console.log('initialize bot ', user.name)
|
||||
console.log('.. ', user.mastodon_auth)
|
||||
const { client_id, client_secret, access_token } = user.mastodon_auth
|
||||
const bot = new Mastodon({ access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
||||
const listener = bot.stream('streaming/direct')
|
||||
listener.on('message', botController.message)
|
||||
listener.on('error', botController.error)
|
||||
return { email: user.email, bot }
|
||||
})
|
||||
console.log(botController.bots)
|
||||
},
|
||||
add (user, token) {
|
||||
const bot = new Mastodon({ access_token: user.mastodon_auth.access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
||||
const listener = bot.stream('streaming/direct')
|
||||
listener.on('message', botController.message)
|
||||
listener.on('error', botController.error)
|
||||
botController.bots.push({ email: user.email, bot })
|
||||
},
|
||||
// async initialize () {
|
||||
// console.log('initialize bots')
|
||||
// const botUsers = await User.findAll({ where: { mastodon_auth: { [Op.ne]: null } } })
|
||||
// console.log(botUsers)
|
||||
// botController.bots = botUsers.map(user => {
|
||||
// console.log('initialize bot ', user.name)
|
||||
// console.log('.. ', user.mastodon_auth)
|
||||
// const { client_id, client_secret, access_token } = user.mastodon_auth
|
||||
// const bot = new Mastodon({ access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
||||
// const listener = bot.stream('streaming/direct')
|
||||
// listener.on('message', botController.message)
|
||||
// listener.on('error', botController.error)
|
||||
// return { email: user.email, bot }
|
||||
// })
|
||||
// console.log(botController.bots)
|
||||
// },
|
||||
// add (user, token) {
|
||||
// const bot = new Mastodon({ access_token: user.mastodon_auth.access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
||||
// const listener = bot.stream('streaming/direct')
|
||||
// listener.on('message', botController.message)
|
||||
// listener.on('error', botController.error)
|
||||
// botController.bots.push({ email: user.email, bot })
|
||||
// },
|
||||
post (user, event) {
|
||||
const { bot } = botController.bots.filter(b => b.email === user.email)[0]
|
||||
const { client_id, client_secret, access_token } = user.mastodon_auth
|
||||
const bot = new Mastodon({ access_token, api_url: `https://${user.mastodon_instance}/api/v1/` })
|
||||
const status = `${event.title} @ ${event.place.name} ${moment(event.start_datetime).format('ddd, D MMMM HH:mm')} -
|
||||
${event.description} - ${event.tags.map(t => '#' + t.tag).join(' ')} ${config.baseurl}/event/${event.id}`
|
||||
return bot.post('/statuses', { status, visibility: 'private' })
|
||||
},
|
||||
async message (msg) {
|
||||
console.log(msg)
|
||||
console.log(msg.data.accounts)
|
||||
const replyid = msg.data.in_reply_to_id || msg.data.last_status.in_reply_to_id
|
||||
if (!replyid) return
|
||||
// const event = await Event.findOne({ where: { activitypub_id: replyid } })
|
||||
// if (!event) {
|
||||
// check for comment..
|
||||
// const comment = await Comment.findOne( {where: { }})
|
||||
// }
|
||||
// const comment = await Comment.create({activitypub_id: msg.data.last_status.id, text: msg.data.last_status.content, author: msg.data.accounts[0].username })
|
||||
// event.addComment(comment)
|
||||
// console.log(event)
|
||||
// const comment = await Comment.findOne( { where: {activitypub_id: msg.data.in_reply_to}} )
|
||||
// console.log('dentro message ', data)
|
||||
|
||||
// add comment to specified event
|
||||
// let comment
|
||||
// if (!event) {
|
||||
// const comment = await Comment.findOne({where: {activitypub_id: req.body.id}, include: Event})
|
||||
// event = comment.event
|
||||
// }
|
||||
// const comment = new Comment(req.body)
|
||||
// event.addComment(comment)
|
||||
},
|
||||
error (err) {
|
||||
console.log('error ', err)
|
||||
}
|
||||
// async message (msg) {
|
||||
// console.log(msg)
|
||||
// console.log(msg.data.accounts)
|
||||
// const replyid = msg.data.in_reply_to_id || msg.data.last_status.in_reply_to_id
|
||||
// if (!replyid) return
|
||||
// const event = await Event.findOne({ where: { activitypub_id: replyid } })
|
||||
// if (!event) {
|
||||
// check for comment..
|
||||
// const comment = await Comment.findOne( {where: { }})
|
||||
// }
|
||||
// const comment = await Comment.create({activitypub_id: msg.data.last_status.id, text: msg.data.last_status.content, author: msg.data.accounts[0].username })
|
||||
// event.addComment(comment)
|
||||
// console.log(event)
|
||||
// const comment = await Comment.findOne( { where: {activitypub_id: msg.data.in_reply_to}} )
|
||||
// console.log('dentro message ', data)
|
||||
|
||||
// add comment to specified event
|
||||
// let comment
|
||||
// if (!event) {
|
||||
// const comment = await Comment.findOne({where: {activitypub_id: req.body.id}, include: Event})
|
||||
// event = comment.event
|
||||
// }
|
||||
// const comment = new Comment(req.body)
|
||||
// event.addComment(comment)
|
||||
// },
|
||||
// error (err) {
|
||||
// console.log('error ', err)
|
||||
// }
|
||||
}
|
||||
|
||||
setTimeout(botController.initialize, 2000)
|
||||
// setTimeout(botController.initialize, 2000)
|
||||
module.exports = botController
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { User, Event, Comment, Tag, Place } = require('../model')
|
||||
const { User, Event, Comment, Tag, Place, MailReminder } = require('../model')
|
||||
const moment = require('moment')
|
||||
const Sequelize = require('sequelize')
|
||||
|
||||
|
@ -32,22 +32,51 @@ const eventController = {
|
|||
res.send(404)
|
||||
}
|
||||
},
|
||||
|
||||
async updatePlace (req, res) {
|
||||
const place = await Place.findByPk(req.body.id)
|
||||
await place.update(req.body)
|
||||
res.json(place)
|
||||
},
|
||||
|
||||
async get (req, res) {
|
||||
const id = req.params.event_id
|
||||
const event = await Event.findByPk(id, { include: [User, Tag, Comment, Place] })
|
||||
res.json(event)
|
||||
},
|
||||
|
||||
async confirm (req, res) {
|
||||
const id = req.params.event_id
|
||||
const event = await Event.findByPk(id)
|
||||
try {
|
||||
await event.update({ is_visible: true })
|
||||
res.send(200)
|
||||
} catch (e) {
|
||||
res.send(404)
|
||||
}
|
||||
},
|
||||
|
||||
async getUnconfirmed (req, res) {
|
||||
const events = await Event.findAll({
|
||||
where: {
|
||||
is_visible: false
|
||||
},
|
||||
order: [['start_datetime', 'ASC']],
|
||||
include: [Tag, Place]
|
||||
})
|
||||
res.json(events)
|
||||
},
|
||||
|
||||
async addReminder (req, res) {
|
||||
await MailReminder.create(req.body.reminder)
|
||||
res.json(200)
|
||||
},
|
||||
async getAll (req, res) {
|
||||
const start = moment().year(req.params.year).month(req.params.month).startOf('month').subtract(1, 'week')
|
||||
const end = moment().year(req.params.year).month(req.params.month).endOf('month').add(1, 'week')
|
||||
const events = await Event.findAll({
|
||||
where: {
|
||||
is_visible: true,
|
||||
[Sequelize.Op.and]: [
|
||||
{ start_datetime: { [Sequelize.Op.gte]: start } },
|
||||
{ start_datetime: { [Sequelize.Op.lte]: end } }
|
||||
|
|
|
@ -15,10 +15,9 @@ const userController = {
|
|||
res.status(404).json({ success: false, message: 'AUTH_FAIL' })
|
||||
} else if (user) {
|
||||
if (!user.is_active) {
|
||||
res.status(403).json({ success: false, message: 'NOT)CONFIRMED' })
|
||||
}
|
||||
res.status(403).json({ success: false, message: 'NOT_CONFIRMED' })
|
||||
// check if password matches
|
||||
else if (!await user.comparePassword(req.body.password)) {
|
||||
} else if (!await user.comparePassword(req.body.password)) {
|
||||
res.status(403).json({ success: false, message: 'AUTH_FAIL' })
|
||||
} else {
|
||||
// if user is found and password is right
|
||||
|
@ -48,21 +47,26 @@ const userController = {
|
|||
await event.destroy()
|
||||
res.sendStatus(200)
|
||||
} else {
|
||||
res.sendStatus(404)
|
||||
res.sendStatus(403)
|
||||
}
|
||||
},
|
||||
|
||||
// ADD EVENT
|
||||
async addEvent (req, res) {
|
||||
const body = req.body
|
||||
|
||||
// remove description tag and create anchor tag
|
||||
const description = body.description
|
||||
.replace(/(<([^>]+)>)/ig, '')
|
||||
.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>')
|
||||
|
||||
const eventDetails = {
|
||||
title: body.title,
|
||||
description,
|
||||
multidate: body.multidate,
|
||||
start_datetime: body.start_datetime,
|
||||
end_datetime: body.end_datetime
|
||||
end_datetime: body.end_datetime,
|
||||
is_visible: req.user ? true : false
|
||||
}
|
||||
|
||||
if (req.file) {
|
||||
|
@ -88,14 +92,18 @@ const userController = {
|
|||
const tags = await Tag.findAll({ where: { tag: body.tags } })
|
||||
await event.addTags(tags)
|
||||
}
|
||||
await req.user.addEvent(event)
|
||||
if (req.user) await req.user.addEvent(event)
|
||||
event = await Event.findByPk(event.id, { include: [User, Tag, Place] })
|
||||
|
||||
// check if bot exists
|
||||
if (req.user.mastodon_auth) {
|
||||
if (req.user && req.user.mastodon_auth) {
|
||||
const post = await bot.post(req.user, event)
|
||||
event.activitypub_id = post.id
|
||||
event.save()
|
||||
}
|
||||
|
||||
mail.send(config.admin, 'event', { event })
|
||||
|
||||
return res.json(event)
|
||||
},
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const User = require('./models/user')
|
||||
const { Event, Comment, Tag, Place } = require('./models/event')
|
||||
const { Event, Comment, Tag, Place, MailReminder } = require('./models/event')
|
||||
|
||||
module.exports = { User, Event, Comment, Tag, Place }
|
||||
module.exports = { User, Event, Comment, Tag, Place, MailReminder }
|
||||
|
|
|
@ -4,12 +4,13 @@ const User = require('./user')
|
|||
|
||||
const Event = db.define('event', {
|
||||
title: Sequelize.STRING,
|
||||
description: Sequelize.STRING,
|
||||
description: Sequelize.TEXT,
|
||||
multidate: Sequelize.BOOLEAN,
|
||||
start_datetime: { type: Sequelize.DATE, index: true },
|
||||
end_datetime: { type: Sequelize.DATE, index: true },
|
||||
image_path: Sequelize.STRING,
|
||||
activitypub_id: { type: Sequelize.INTEGER, index: true }
|
||||
activitypub_id: { type: Sequelize.INTEGER, index: true },
|
||||
is_visible: Sequelize.BOOLEAN
|
||||
})
|
||||
|
||||
const Tag = db.define('tag', {
|
||||
|
@ -23,11 +24,11 @@ const Comment = db.define('comment', {
|
|||
text: Sequelize.STRING
|
||||
})
|
||||
|
||||
const MailSubscription = db.define('subscription', {
|
||||
const MailReminder = db.define('reminder', {
|
||||
filters: Sequelize.JSON,
|
||||
mail: Sequelize.TEXT,
|
||||
mail: Sequelize.STRING,
|
||||
send_on_add: Sequelize.BOOLEAN,
|
||||
send_reminder: Sequelize.INTEGER
|
||||
send_reminder: Sequelize.BOOLEAN
|
||||
})
|
||||
|
||||
const Place = db.define('place', {
|
||||
|
@ -47,4 +48,4 @@ Event.belongsTo(Place)
|
|||
User.hasMany(Event)
|
||||
Place.hasMany(Event)
|
||||
|
||||
module.exports = { Event, Comment, Tag, Place, MailSubscription }
|
||||
module.exports = { Event, Comment, Tag, Place, MailReminder }
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
VUE_BASE_URL=https://localhost:8080/
|
||||
VUE_INSTANCE_API=https://localhost:9000/
|
||||
VUE_APP_TITLE=Eventi
|
|
@ -1,4 +1,17 @@
|
|||
# Gancio
|
||||
an event manager for local communities
|
||||
|
||||
##Install
|
||||
We provide a docker way to run **gancio**.
|
||||
|
||||
```
|
||||
git clone
|
||||
```
|
||||
|
||||
##Development
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
## Project setup
|
||||
```
|
||||
|
@ -13,9 +26,4 @@ yarn run serve
|
|||
### Compiles and minifies for production
|
||||
```
|
||||
yarn run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn run lint
|
||||
```
|
||||
```
|
|
@ -5,6 +5,7 @@
|
|||
transition(name="fade" mode="out-in")
|
||||
router-view(name='modal')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import api from '@/api'
|
||||
|
@ -14,7 +15,6 @@ import Login from '@/components/Login'
|
|||
import Settings from '@/components/Settings'
|
||||
import newEvent from '@/components/newEvent'
|
||||
import eventDetail from '@/components/EventDetail'
|
||||
import Timeline from '@/components/Timeline'
|
||||
import Home from '@/components/Home'
|
||||
import Nav from '@/components/Nav'
|
||||
|
||||
|
@ -30,7 +30,7 @@ export default {
|
|||
|
||||
<style>
|
||||
#logo {
|
||||
max-height: 60px;
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
|
|
|
@ -44,6 +44,9 @@ export default {
|
|||
login: (email, password) => post('/login', { email, password }),
|
||||
register: user => post('/user', user),
|
||||
getAllEvents: (month, year) => get(`/event/${year}/${month}/`),
|
||||
getUnconfirmedEvents: () => get('/event/unconfirmed'),
|
||||
confirmEvent: id => get(`/event/confirm/${id}`),
|
||||
emailReminder: reminder => post('/event/reminder', reminder),
|
||||
addEvent: event => post('/user/event', event),
|
||||
updateEvent: event => put('/user/event', event),
|
||||
updatePlace: place => put('/place', place),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template lang="pug">
|
||||
b-modal(@hidden='$router.replace("/")' :title='$t("Admin")' :visible='true' size='lg')
|
||||
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("Admin")' :visible='true' size='lg')
|
||||
el-tabs
|
||||
//- USERS
|
||||
el-tab-pane.pt-1
|
||||
template(slot='label')
|
||||
v-icon(name='users')
|
||||
|
@ -10,12 +11,14 @@
|
|||
template(slot='action' slot-scope='data')
|
||||
el-button.mr-1(size='mini' :type='data.item.is_active?"warning":"success"' @click='toggle(data.item)') {{data.item.is_active?$t('Deactivate'):$t('Activate')}}
|
||||
el-button(size='mini' :type='data.item.is_admin?"danger":"warning"' @click='toggleAdmin(data.item)') {{data.item.is_admin?$t('Remove Admin'):$t('Admin')}}
|
||||
b-pagination(:per-page='5' v-model='userPage' :total-rows='users.length')
|
||||
el-pagination(:page-size='perPage' :currentPage.sync='userPage' :total='users.length')
|
||||
|
||||
//- PLACES
|
||||
el-tab-pane.pt-1
|
||||
template(slot='label')
|
||||
v-icon(name='map-marker-alt')
|
||||
span.ml-1 {{$t('Places')}}
|
||||
p You can change place's name or address
|
||||
p {{$t('admin_place_explanation')}}
|
||||
el-form.mb-2(:inline='true' label-width='120px')
|
||||
el-form-item(:label="$t('Name')")
|
||||
el-input.mr-1(:placeholder='$t("Name")' v-model='place.name')
|
||||
|
@ -25,22 +28,45 @@
|
|||
b-table(selectable :items='places' :fields='placeFields' striped hover
|
||||
small selectedVariant='success' primary-key='id'
|
||||
select-mode="single" @row-selected='placeSelected'
|
||||
:per-page='5' :current-page='placePage')
|
||||
b-pagination(:per-page='5' v-model='placePage' :total-rows='places.length')
|
||||
:per-page='perPage' :current-page='placePage')
|
||||
el-pagination(:page-size='perPage' :currentPage.sync='placePage' :total='places.length')
|
||||
|
||||
//- EVENTS
|
||||
el-tab-pane.pt-1
|
||||
template(slot='label')
|
||||
v-icon(name='calendar')
|
||||
span.ml-1 {{$t('Events')}}
|
||||
p {{$t('event_confirm_explanation')}}
|
||||
el-table(:data='paginatedEvents' small primary-key='id')
|
||||
el-table-column(:label='$t("Name")')
|
||||
template(slot-scope='data') {{data.row.title}}
|
||||
el-table-column(:label='$t("Where")')
|
||||
template(slot-scope='data') {{data.row.place.name}}
|
||||
el-table-column(:label='$t("Confirm")')
|
||||
template(slot-scope='data')
|
||||
el-button(type='primary' @click='confirm(data.row.id)' size='mini') {{$t('Confirm')}}
|
||||
el-button(type='success' @click='preview(data.row.id)' size='mini') {{$t('Preview')}}
|
||||
|
||||
el-pagination(:page-size='perPage' :currentPage.sync='eventPage' :total='events.length')
|
||||
|
||||
//- TAGS
|
||||
el-tab-pane.pt-1
|
||||
template(slot='label')
|
||||
v-icon(name='tag')
|
||||
span {{$t('Tags')}}
|
||||
p You can choose colors of your tags
|
||||
el-table(:data='tags' striped small hover :per-page='10' :current-page='tagPage')
|
||||
p Select a tag to change it's color
|
||||
el-tag(v-if='tag.tag' :color='tag.color || "grey"' size='mini') {{tag.tag}}
|
||||
el-form.mb-2(:inline='true' label-width='120px')
|
||||
el-form-item(:label="$t('Color')")
|
||||
el-color-picker(v-model='tag.color' @change='updateColor')
|
||||
el-table(:data='paginatedTags' striped small hover
|
||||
highlight-current-row @current-change="tagSelected")
|
||||
el-table-column(label='Tag')
|
||||
template(slot-scope='data')
|
||||
el-tag(:color='data.row.color' size='mini') {{data.row.tag}}
|
||||
el-table-column(label='Color')
|
||||
template(slot-scope='data')
|
||||
el-color-picker(v-model='data.row.color' @change='updateColor(data.row)')
|
||||
b-pagination(:per-page='10' v-model='tagPage' :total-rows='tags.length')
|
||||
el-tag(:color='data.row.color || "grey"' size='mini') {{data.row.tag}}
|
||||
el-pagination(:page-size='perPage' :currentPage.sync='tagPage' :total='tags.length')
|
||||
|
||||
//- SETTINGS
|
||||
el-tab-pane.pt-1
|
||||
template(slot='label')
|
||||
v-icon(name='tools')
|
||||
|
@ -55,31 +81,54 @@ export default {
|
|||
name: 'Admin',
|
||||
data () {
|
||||
return {
|
||||
perPage: 10,
|
||||
users: [],
|
||||
userFields: ['email', 'action'],
|
||||
placeFields: ['name', 'address'],
|
||||
placePage: 1,
|
||||
userPage: 1,
|
||||
eventPage: 1,
|
||||
tagPage: 1,
|
||||
tagFields: ['tag', 'color'],
|
||||
description: '',
|
||||
place: {name: '', address: '' }
|
||||
place: {name: '', address: '' },
|
||||
tag: {name: '', color: ''},
|
||||
events: [],
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
this.users = await api.getUsers()
|
||||
this.events = await api.getUnconfirmedEvents()
|
||||
},
|
||||
computed: {
|
||||
...mapState(['tags', 'places']),
|
||||
paginatedEvents () {
|
||||
console.log(this.events)
|
||||
return this.events.slice((this.eventPage-1) * this.perPage,
|
||||
this.eventPage * this.perPage)
|
||||
},
|
||||
paginatedTags () {
|
||||
return this.tags.slice((this.tagPage-1) * this.perPage,
|
||||
this.tagPage * this.perPage)
|
||||
}
|
||||
},
|
||||
computed: mapState(['tags', 'places']),
|
||||
methods: {
|
||||
placeSelected (items) {
|
||||
console.log('dentro place selected ', items, items.length)
|
||||
if (items.length === 0 ) {
|
||||
this.place.name = this.place.address = ''
|
||||
return
|
||||
}
|
||||
const item = items[0]
|
||||
this.place.name = item.name
|
||||
this.place.address = item.address
|
||||
this.place.id = item.id
|
||||
},
|
||||
tagSelected (tag) {
|
||||
this.tag = tag
|
||||
},
|
||||
async savePlace () {
|
||||
const place = await api.updatePlace(this.place)
|
||||
|
||||
},
|
||||
async toggle(user) {
|
||||
user.is_active = !user.is_active
|
||||
|
@ -89,8 +138,23 @@ export default {
|
|||
user.is_admin = !user.is_admin
|
||||
const newuser = await api.updateUser(user)
|
||||
},
|
||||
async updateColor(tag) {
|
||||
const newTag = await api.updateTag(tag)
|
||||
async updateColor () {
|
||||
const newTag = await api.updateTag(this.tag)
|
||||
},
|
||||
preview (id) {
|
||||
this.$router.push(`/event/${id}`)
|
||||
},
|
||||
async confirm (id) {
|
||||
console.log('dentro confirm', id)
|
||||
try {
|
||||
await api.confirmEvent(id)
|
||||
this.$message({
|
||||
message: this.$t('event_confirmed'),
|
||||
type: 'success'
|
||||
})
|
||||
this.events = this.events.filter(e => e.id !== id)
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,4 +84,8 @@ export default {
|
|||
margin-bottom: 0em;
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
#calendar a {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,20 +6,22 @@
|
|||
div <v-icon name='clock'/> {{event.start_datetime|datetime}}
|
||||
span <v-icon name='map-marker-alt'/> {{event.place.name}}
|
||||
br
|
||||
el-tag.mr-1(:color='tag.color' v-for='tag in event.tags'
|
||||
size='mini' @click.stop='addSearchTag(tag)') {{tag.tag}}
|
||||
el-tag.mr-1(:color='tag.color' v-for='tag in event.tags' :key='tag.tag'
|
||||
size='small' @click.stop='addSearchTag(tag)') {{tag.tag}}
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import api from '@/api'
|
||||
import filters from '@/filters'
|
||||
|
||||
console.log(process.env)
|
||||
|
||||
export default {
|
||||
props: ['event'],
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
imgPath () {
|
||||
return this.event.image_path && this.event.image_path
|
||||
return this.event.image_path && process.env.VUE_APP_API + '/' + this.event.image_path
|
||||
},
|
||||
mine () {
|
||||
return this.event.userId === this.user.id
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<template lang="pug">
|
||||
b-modal#eventDetail(hide-body hide-header hide-footer @hidden='$router.replace("/")' size='lg' :visible='true')
|
||||
b-card(bg-variant='dark' href='#' text-variant='white'
|
||||
no-body, :img-src='imgPath')
|
||||
b-card-header
|
||||
b-modal#eventDetail(ref='eventDetail' hide-body hide-header hide-footer @hidden='$router.replace("/")' size='lg' :visible='true')
|
||||
b-card(no-body, :img-src='imgPath' v-loading='loading')
|
||||
el-button.close_button(circle icon='el-icon-close' type='success'
|
||||
@click='$refs.eventDetail.hide()')
|
||||
b-card-header
|
||||
h3 {{event.title}}
|
||||
v-icon(name='clock')
|
||||
span {{event.start_datetime|datetime}}
|
||||
|
@ -14,11 +15,13 @@
|
|||
pre(v-html='event.description')
|
||||
br
|
||||
el-tag.mr-1(:color='tag.color' v-for='tag in event.tags'
|
||||
size='mini') {{tag.tag}}
|
||||
size='mini' :key='tag.tag') {{tag.tag}}
|
||||
.ml-auto(v-if='mine')
|
||||
hr
|
||||
el-button(plain type='danger' @click.prevent='remove' icon='el-icon-remove') {{$t('Remove')}}
|
||||
el-button(plain type='primary' @click='$router.replace("/edit/"+event.id)') <v-icon color='orange' name='edit'/> {{$t('Edit')}}
|
||||
|
||||
//- COMMENTS ...
|
||||
//- b-navbar(type="dark" variant="dark" toggleable='lg')
|
||||
//- template(slot='footer')
|
||||
//- b-navbar-nav
|
||||
|
@ -31,6 +34,7 @@
|
|||
//- b-card-footer(v-for='comment in event.comments')
|
||||
strong {{comment.author}}
|
||||
div(v-html='comment.text')
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
|
@ -38,10 +42,11 @@ import api from '@/api'
|
|||
import filters from '@/filters'
|
||||
|
||||
export default {
|
||||
name: 'EventDetail',
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
imgPath () {
|
||||
return this.event.image_path && this.event.image_path
|
||||
return this.event.image_path && process.env.VUE_APP_API + '/' + this.event.image_path
|
||||
},
|
||||
mine () {
|
||||
return this.event.userId === this.user.id || this.user.is_admin
|
||||
|
@ -49,7 +54,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
event: { comments: [], place: {}},
|
||||
event: { comments: [], place: {}, title: ''},
|
||||
id: null,
|
||||
loading: true,
|
||||
}
|
||||
|
@ -58,7 +63,7 @@ export default {
|
|||
this.id = this.$route.params.id
|
||||
this.load()
|
||||
},
|
||||
filters: filters,
|
||||
filters,
|
||||
methods: {
|
||||
...mapActions(['delEvent']),
|
||||
async load () {
|
||||
|
@ -79,5 +84,23 @@ export default {
|
|||
padding: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#eventDetail .close_button:hover {
|
||||
background-color: rgba(200, 100, 100, 0.4);
|
||||
}
|
||||
|
||||
#eventDetail .card {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
#eventDetail .close_button {
|
||||
background-color: rgba(100, 100, 100, 0.4);
|
||||
color: red;
|
||||
font-size: 20px;
|
||||
border: none;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
p {{$t('export_intro')}}
|
||||
|
||||
li(v-if='filters.tags.length') {{$t('Tags')}}:
|
||||
el-tag.ml-1(color='#409EFF' size='mini' v-for='tag in filters.tags') {{tag}}
|
||||
el-tag.ml-1(color='#409EFF' size='mini' v-for='tag in filters.tags' :key='tag.tag') {{tag}}
|
||||
li(v-if='filters.places.length') {{$t('Places')}}:
|
||||
el-tag.ml-1(color='#409EFF' size='mini' v-for='place in filters.places') {{place}}
|
||||
el-tag.ml-1(color='#409EFF' size='mini' v-for='place in filters.places' :key='place.id') {{place}}
|
||||
el-tabs.mt-2(tabPosition='left' v-model='type')
|
||||
|
||||
el-tab-pane.pt-1(label='email' name='email')
|
||||
p(v-html='$t(`export_email_explanation`)')
|
||||
b-form
|
||||
el-switch(v-model='mail.sendOnInsert' :active-text="$t('notify_on_insert')")
|
||||
el-switch(v-model='reminder.send_on_insert' :active-text="$t('notify_on_insert')")
|
||||
br
|
||||
el-switch.mt-2(v-model='mail.reminder' :active-text="$t('send_reminder')")
|
||||
el-input.mt-2(v-model='mail.mail' :placeholder="$t('Insert your address')")
|
||||
el-switch.mt-2(v-model='reminder.send_reminder' :active-text="$t('send_reminder')")
|
||||
el-input.mt-2(v-model='reminder.mail' :placeholder="$t('Insert your address')")
|
||||
el-button.mt-2.float-right(type='success' @click='activate_email') {{$t('Send')}}
|
||||
|
||||
el-tab-pane.pt-1(label='feed rss' name='feed')
|
||||
|
@ -31,7 +31,7 @@
|
|||
p(v-html='$t(`export_list_explanation`)')
|
||||
b-card.mb-1(no-body header='Eventi')
|
||||
b-list-group#list(flush)
|
||||
b-list-group-item.flex-column.align-items-start(v-for="event in filteredEvents"
|
||||
b-list-group-item.flex-column.align-items-start(v-for="event in filteredEvents" :key='event.id'
|
||||
:to='`/event/${event.id}`')
|
||||
//- b-media
|
||||
img(v-if='event.image_path' slot="aside" :src="imgPath(event)" alt="Meia Aside" style='max-height: 60px')
|
||||
|
@ -39,7 +39,7 @@
|
|||
strong.mb-1 {{event.title}}
|
||||
br
|
||||
small.float-right {{event.place.name}}
|
||||
el-tag.mr-1(:color='tag.color' size='mini' v-for='tag in event.tags') {{tag.tag}}
|
||||
el-tag.mr-1(:color='tag.color' size='mini' v-for='tag in event.tags' :key='tag.tag') {{tag.tag}}
|
||||
el-input.mb-1(type='textarea' v-model='script')
|
||||
el-button.float-right(plain type="primary" icon='el-icon-document' v-clipboard:copy="script") Copy
|
||||
|
||||
|
@ -65,7 +65,7 @@ export default {
|
|||
return {
|
||||
type: 'feed',
|
||||
link: '',
|
||||
mail: {},
|
||||
reminder: { send_on_insert: true, send_reminder: false },
|
||||
export_list: true,
|
||||
script: `<iframe>Ti piacerebbe</iframe>`,
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
return `${process.env.BASE_URL}/api/export/${this.type}${query}`
|
||||
return `${process.env.VUE_APP_API}/api/export/${this.type}${query}`
|
||||
},
|
||||
imgPath (event) {
|
||||
return event.image_path && event.image_path
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<template lang="pug">
|
||||
b-container
|
||||
b-card-group(columns)
|
||||
b-form-group.mt-1
|
||||
div.mt-1
|
||||
Search#search
|
||||
Calendar
|
||||
Event.item(v-for='event in filteredEvents'
|
||||
:key='event.id'
|
||||
:event='event')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import filters from '@/filters.js'
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
b-navbar-nav
|
||||
b-nav-item(v-if='!logged' to='/login' v-b-tooltip :title='$t("Login")') <v-icon color='lightgreen' name='lock' />
|
||||
span.d-md-none {{$t('User')}}
|
||||
b-nav-item(v-if='logged' to='/new_event' v-b-tooltip :title='$t("Add Event")' ) <v-icon color='lightgreen' name='plus'/>
|
||||
b-nav-item(to='/new_event' v-b-tooltip :title='$t("Add Event")' ) <v-icon color='lightgreen' name='plus'/>
|
||||
span.d-md-none {{$t('Add Event')}}
|
||||
b-nav-item(v-if='logged' to='/settings' v-b-tooltip :title='$t("Settings")') <v-icon color='orange' name='cog'/>
|
||||
span.d-md-none {{$t('Settings')}}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<template lang='pug'>
|
||||
b-modal(hide-header hide-footer
|
||||
@hide='$router.replace("/")' :visible='true' @shown='$refs.email.focus()')
|
||||
h4.text-center.center {{$t('Register')}}
|
||||
b-modal(hide-footer
|
||||
@hidden='$router.replace("/")' :title="$t('Register')" :visible='true' @shown='$refs.email.focus()')
|
||||
b-form
|
||||
p.text-muted(v-html="$t('register_explanation')")
|
||||
b-input-group.mb-1
|
||||
|
@ -46,7 +45,7 @@ export default {
|
|||
this.$message({
|
||||
message: this.$t('registration_complete'),
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
el-option(v-for='tag in tags' :key='tag.tag'
|
||||
:label='tag.tag' :value='tag.tag')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapActions} from 'vuex'
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template lang="pug">
|
||||
b-card.column.pl-1(bg-variant='dark' text-variant='white' no-body)
|
||||
b-card-header
|
||||
strong Public events
|
||||
b-btn.float-right(v-if='logged' variant='success' size='sm' to='/newEvent') <v-icon name="plus"/> Add Event
|
||||
event(v-for='event in events', :event='event' :key='event.id')
|
||||
</template>
|
||||
<script>
|
||||
import api from '@/api'
|
||||
import event from './Event'
|
||||
import { mapState } from 'vuex';
|
||||
// <template lang="pug">
|
||||
// b-card.column.pl-1(bg-variant='dark' text-variant='white' no-body)
|
||||
// b-card-header
|
||||
// strong Public events
|
||||
// b-btn.float-right(v-if='logged' variant='success' size='sm' to='/newEvent') <v-icon name="plus"/> Add Event
|
||||
// event(v-for='event in events', :event='event' :key='event.id')
|
||||
// </template>
|
||||
// <script>
|
||||
// import api from '@/api'
|
||||
// import event from './Event'
|
||||
// import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {event},
|
||||
computed: mapState(['events', 'logged'])
|
||||
}
|
||||
</script>
|
||||
// export default {
|
||||
// components: {event},
|
||||
// computed: mapState(['events', 'logged'])
|
||||
// }
|
||||
// </script>
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
<template lang='pug'>
|
||||
div(style="position: relative")
|
||||
|
||||
b-input-group
|
||||
input.form-control(type="search"
|
||||
ref="input"
|
||||
:placeholder="placeholder"
|
||||
v-model="search"
|
||||
@input="update"
|
||||
autocomplete="off"
|
||||
@keydown.backspace="backspace"
|
||||
@keydown.up.prevent="up"
|
||||
@keydown.down.prevent="down"
|
||||
@keydown.enter="hit"
|
||||
@keydown.esc="reset(true)"
|
||||
@blur="focus = false"
|
||||
@focus="focus = true")
|
||||
|
||||
div
|
||||
b-badge.mr-1(@click="removeSelected(sel)"
|
||||
v-for="sel in selectedLabel"
|
||||
:key="sel") <v-icon color='orange' name='times' /> {{sel}}
|
||||
|
||||
b-list-group.groupMenu(v-show='showDropdown')
|
||||
b-list-group-item(:key="$index" v-for="(item, $index) in matched"
|
||||
href='#'
|
||||
:class="{'active': isActive($index)}"
|
||||
@mousedown.prevent="hit"
|
||||
@mousemove="setActive($index)")
|
||||
slot(:name="templateName") {{textField ? item[textField] : item}}
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
twoWay : true,
|
||||
type: [String, Array, Set],
|
||||
default: ''
|
||||
},
|
||||
data: {
|
||||
type: Array
|
||||
},
|
||||
template: {
|
||||
type: String
|
||||
},
|
||||
templateName: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
valueField: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
textField: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
showClear: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
matchCase: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
matchStart: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
onHit: {
|
||||
type: Function,
|
||||
default () {
|
||||
this.reset()
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
type: String
|
||||
},
|
||||
updateOnMatchOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
maxMatch: {
|
||||
type: Number,
|
||||
default: 4
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
focus: false,
|
||||
noResults: true,
|
||||
current: 0,
|
||||
search: '',
|
||||
selected: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
if (!newValue) {
|
||||
this.search = '';
|
||||
if (this.multiple) this.$emit('input', []) // this.selected = [];
|
||||
} else {
|
||||
if (!this.multiple) {
|
||||
this.search = newValue
|
||||
} else {
|
||||
this.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showDropdown () {
|
||||
return this.focus
|
||||
},
|
||||
selectedValues () {
|
||||
return this.selected.map(s => this.valueField ? (s[this.valueField] || s) : s);
|
||||
},
|
||||
selectedLabel () {
|
||||
return this.selected.map(s => this.textField ? s[this.textField] || s: s);
|
||||
},
|
||||
matched () {
|
||||
if (this.data) {
|
||||
return this.data.filter(value => {
|
||||
if(this.textField) value = value[this.textField];
|
||||
if (this.multiple && this.selectedLabel.includes(value)) return false;
|
||||
value = this.matchCase ? value : value.toLowerCase()
|
||||
const query = this.matchCase ? this.search : this.search.toLowerCase()
|
||||
return this.matchStart ? value.indexOf(query) === 0 : value.indexOf(query) !== -1
|
||||
}).slice(0, this.maxMatch)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update (e, value) {
|
||||
if (this.multiple && this.search[this.search.length-1] === ',') {
|
||||
this.search = this.search.substr(0, this.search.length-1)
|
||||
this.hit(e)
|
||||
return
|
||||
}
|
||||
if (!this.updateOnMatchOnly && !this.multiple) this.$emit('input', this.search);
|
||||
this.focus = true;
|
||||
if (!this.matched.length) {
|
||||
this.focus = false;
|
||||
this.current = 0
|
||||
return;
|
||||
}
|
||||
|
||||
// current selected item has to be in the match
|
||||
if (this.matched.length <= this.current) {
|
||||
this.current = this.matched.length-1;
|
||||
}
|
||||
},
|
||||
backspace () {
|
||||
if (this.search) return
|
||||
this.selected.splice(-1, 1)
|
||||
this.$emit('input', this.selected.length ? this.selectedValues : '');
|
||||
},
|
||||
reset (esc=false) {
|
||||
this.search = '';
|
||||
this.current = 0;
|
||||
this.$refs.input.focus();
|
||||
if (esc) {
|
||||
this.focus = false
|
||||
} else {
|
||||
this.selected = [];
|
||||
this.$emit('input', '');
|
||||
}
|
||||
},
|
||||
setActive (index) {
|
||||
this.current = index
|
||||
},
|
||||
isActive (index) {
|
||||
return this.current === index
|
||||
},
|
||||
|
||||
removeSelected (label) {
|
||||
this.selected = this.selected.filter( s => (this.textField ? s[this.textField] || s: s) !== label);
|
||||
this.$emit('input', this.selected.length ? this.selectedValues : []);
|
||||
},
|
||||
|
||||
// click or enter on curren item
|
||||
hit (e) {
|
||||
e.preventDefault();
|
||||
let code = '';
|
||||
let item = '';
|
||||
|
||||
if (this.matched.length !== 0 && this.focus) {
|
||||
item = this.matched[this.current];
|
||||
code = this.textField ? item[this.textField] : item;
|
||||
// code = this.valueField ? item[this.valueField] : item;
|
||||
} else {
|
||||
code = this.search;
|
||||
}
|
||||
if (this.multiple) {
|
||||
if (code) {
|
||||
this.selected.push(code);
|
||||
this.search = '';
|
||||
this.$emit('input', this.selected);
|
||||
this.focus = false;
|
||||
// this.update();
|
||||
}
|
||||
} else {
|
||||
this.$emit('input', code);
|
||||
this.current = 0;
|
||||
this.focus = false;
|
||||
this.search = code
|
||||
}
|
||||
this.$emit('enter')
|
||||
},
|
||||
|
||||
// manage up/down arrow key
|
||||
up () {
|
||||
if (this.current > 0) this.current--
|
||||
},
|
||||
down () {
|
||||
if (this.current < this.matched.length - 1) this.current++
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.groupMenu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.badge {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,4 @@
|
|||
<template lang="pug">
|
||||
//- el-dialog(@close='$router.replace("/")' :title="edit?$t('Edit event'):$t('New event')" center :close-on-press-escape='false' :visible='true')
|
||||
b-modal(@hidden='$router.replace("/")' :title="edit?$t('Edit event'):$t('New event')" size='md' :visible='true' hide-footer)
|
||||
b-container
|
||||
el-tabs.mb-2(v-model='activeTab' v-loading='sending')
|
||||
|
@ -10,7 +9,7 @@
|
|||
el-form(label-width='120px')
|
||||
el-form-item(:label='$t("Where")')
|
||||
el-select(v-model='event.place.name' @change='placeChoosed' filterable allow-create default-first-option)
|
||||
el-option(v-for='place in places_name' :label='place' :value='place')
|
||||
el-option(v-for='place in places_name' :label='place' :value='place' :key='place.id')
|
||||
el-form-item(:label='$t("Address")')
|
||||
el-input(ref='address' v-model='event.place.address' @keydown.native.enter='next')
|
||||
el-button.float-right(@click='next' :disabled='!couldProceed') {{$t('Next')}}
|
||||
|
@ -39,10 +38,9 @@
|
|||
el-input.mb-3(v-model='event.description' type='textarea' :rows='3')
|
||||
span {{$t('tag_explanation')}}
|
||||
br
|
||||
//- typeahead(v-model="event.tags" :data='tags' multiple)
|
||||
el-select(v-model='event.tags' multiple filterable allow-create
|
||||
default-first-option placeholder='Tag')
|
||||
el-option(v-for='tag in tags' :key='tag'
|
||||
el-option(v-for='tag in tags' :key='tag.tag'
|
||||
:label='tag' :value='tag')
|
||||
|
||||
el-button.float-right(@click='next' :disabled='!couldProceed') {{$t('Next')}}
|
||||
|
@ -80,6 +78,12 @@ export default {
|
|||
}
|
||||
},
|
||||
name: 'newEvent',
|
||||
watch: {
|
||||
'time.start' (value) {
|
||||
let [h, m] = value.split(':')
|
||||
this.time.end = (Number(h)+1) + ':' + m
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
if (this.$route.params.id) {
|
||||
this.id = this.$route.params.id
|
||||
|
|
|
@ -53,18 +53,25 @@ const it = {
|
|||
Places: 'Luoghi',
|
||||
Tags: 'Etichette',
|
||||
Name: 'Nome',
|
||||
Preview: 'Visualizza',
|
||||
Save: 'Salva',
|
||||
Address: 'Indirizzo',
|
||||
Remove: 'Elimina',
|
||||
Password: 'Password',
|
||||
Email: 'Email',
|
||||
User: 'Utente',
|
||||
Confirm: 'Conferma',
|
||||
Events: 'Eventi',
|
||||
Color: 'Colore',
|
||||
Edit: 'Modifica',
|
||||
Admin: 'Amministra',
|
||||
Today: 'Oggi',
|
||||
Export: 'Esporta',
|
||||
send_reminder: 'Ricordamelo il giorno prima',
|
||||
event_confirmed: 'Evento confermato!',
|
||||
notify_on_insert: `Notifica all'inserimento`,
|
||||
'event_confirm_explanation': 'Puoi approvare gli eventi inseriti da utenti non registrati',
|
||||
'admin_place_explanation': 'Puoi modificare i luoghi inseriti',
|
||||
'Edit event': 'Modifica evento',
|
||||
'New event': 'Nuovo evento',
|
||||
'Insert your address': 'Inserisci il tuo indirizzo',
|
||||
|
|
|
@ -5,13 +5,12 @@ import VCalendar from 'v-calendar'
|
|||
|
||||
import 'vue-awesome/icons'
|
||||
import Icon from 'vue-awesome/components/Icon'
|
||||
import Typeahead from '@/components/Typeahead'
|
||||
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
|
||||
import 'v-calendar/lib/v-calendar.min.css'
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
// import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
|
@ -35,7 +34,6 @@ Vue.use(VCalendar, {
|
|||
Vue.use(BootstrapVue)
|
||||
Vue.use(VueI18n)
|
||||
Vue.use(VueClipboard)
|
||||
Vue.component('typeahead', Typeahead)
|
||||
Vue.component('v-icon', Icon)
|
||||
|
||||
const messages = {
|
||||
|
|
|
@ -113,7 +113,9 @@ export default new Vuex.Store({
|
|||
},
|
||||
async addEvent ({ commit }, formData) {
|
||||
const event = await api.addEvent(formData)
|
||||
commit('addEvent', event)
|
||||
if (this.state.logged) {
|
||||
commit('addEvent', event)
|
||||
}
|
||||
},
|
||||
async updateEvent ({ commit }, formData) {
|
||||
const event = await api.updateEvent(formData)
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
process.env.VUE_APP_API = process.env.NODE_ENV === 'production' ? process.env.BASE_URL || 'http://localhost:9000' : 'http://localhost:9000'
|
||||
process.env.VUE_APP_TITLE = process.env.TITLE || 'Gancio'
|
||||
process.env.VUE_APP_DESCRIPTION = process.env.DESCRIPTION || 'Event manager for radical movements'
|
||||
|
||||
module.exports = {
|
||||
publicPath: process.env.BASE_URL,
|
||||
devServer: {
|
||||
disableHostCheck: true
|
||||
},
|
||||
|
|
|
@ -64,16 +64,17 @@
|
|||
"@babel/traverse" "^7.1.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.2.3":
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.2.3.tgz#f6e719abb90cb7f4a69591e35fd5eb89047c4a7c"
|
||||
integrity sha512-xO/3Gn+2C7/eOUeb0VRnSP1+yvWHNxlpAot1eMhtoKDCN7POsyQP5excuT5UsV5daHxMWBeIIOeI5cmB8vMRgQ==
|
||||
"@babel/helper-create-class-features-plugin@^7.3.0", "@babel/helper-create-class-features-plugin@^7.3.4":
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.4.tgz#092711a7a3ad8ea34de3e541644c2ce6af1f6f0c"
|
||||
integrity sha512-uFpzw6L2omjibjxa8VGZsJUPL5wJH0zzGKpoz0ccBkzIa6C8kWNUbiBmQ0rgOKWlHJ6qzmfa6lTiGchiV8SC+g==
|
||||
dependencies:
|
||||
"@babel/helper-function-name" "^7.1.0"
|
||||
"@babel/helper-member-expression-to-functions" "^7.0.0"
|
||||
"@babel/helper-optimise-call-expression" "^7.0.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/helper-replace-supers" "^7.2.3"
|
||||
"@babel/helper-replace-supers" "^7.3.4"
|
||||
"@babel/helper-split-export-declaration" "^7.0.0"
|
||||
|
||||
"@babel/helper-define-map@^7.1.0":
|
||||
version "7.1.0"
|
||||
|
|
|
@ -16,6 +16,8 @@ services:
|
|||
build: .
|
||||
ports:
|
||||
- '12300:12300'
|
||||
volumes:
|
||||
- ./uploads:/usr/src/app/uploads
|
||||
|
||||
env_file: .env
|
||||
environment:
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"pg": "^7.8.1",
|
||||
"pug": "^2.0.3",
|
||||
"sequelize": "^4.41.0",
|
||||
"sharp": "^0.21.3",
|
||||
"sqlite3": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -3,7 +3,7 @@ rss(version='2.0', xmlns:atom='<a href="http://www.w3.org/2005/Atom" rel="nofoll
|
|||
channel
|
||||
title #{config.title}
|
||||
link <a href="#{config.baseurl}" rel="nofollow">#{config.baseurl}</a>
|
||||
atom:link(href='<a href="#{config.apiurl}/export/feed/rss" rel="nofollow">#{config.apiurl}/export/feed/rss</a>', rel='self', type='application/rss+xml')
|
||||
<atom:link href="#{config.apiurl}/export/feed/rss" rel='self' type='application/rss+xml' />
|
||||
description #{config.description}
|
||||
language #{config.locale}
|
||||
//- if events.length
|
||||
|
@ -17,6 +17,7 @@ rss(version='2.0', xmlns:atom='<a href="http://www.w3.org/2005/Atom" rel="nofoll
|
|||
| <h4>#{event.title}</h4>
|
||||
| <strong>#{event.place.name} - #{event.place.address}</strong>
|
||||
| #{moment(event.start_datetime).format("ddd, D MMMM HH:mm")}<br/>
|
||||
| <img src="#{config.apiurl}/#{event.image_path}"/>
|
||||
| <pre>!{event.description}</pre>
|
||||
| ]]>
|
||||
pubDate= new Date(event.createdAt).toUTCString()
|
||||
|
|
Loading…
Reference in a new issue