mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 16:42:22 +01:00
s/cohort/collection
This commit is contained in:
parent
c660bd8eb4
commit
da0216a606
8 changed files with 157 additions and 133 deletions
|
@ -1,41 +1,41 @@
|
|||
<template lang='pug'>
|
||||
v-container
|
||||
v-card-title {{$t('common.blobs')}}
|
||||
v-card-title {{$t('common.collections')}}
|
||||
v-spacer
|
||||
v-text-field(v-model='search'
|
||||
:append-icon='mdiMagnify' outlined rounded
|
||||
label='Search'
|
||||
single-line hide-details)
|
||||
v-card-subtitle(v-html="$t('admin.blobs_description')")
|
||||
v-card-subtitle(v-html="$t('admin.collections_description')")
|
||||
|
||||
v-btn(color='primary' text @click='newCohort') <v-icon v-text='mdiPlus'></v-icon> {{$t('admin.new_blob')}}
|
||||
v-btn(color='primary' text @click='newCollection') <v-icon v-text='mdiPlus'></v-icon> {{$t('admin.new_collection')}}
|
||||
|
||||
v-dialog(v-model='dialog' width='800' destroy-on-close :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||
v-card(color='secondary')
|
||||
v-card-title {{$t('admin.edit_blob')}}
|
||||
v-card-title {{$t('admin.edit_collection')}}
|
||||
v-card-text
|
||||
v-form(v-model='valid' ref='form')
|
||||
v-text-field(
|
||||
v-if='!cohort.id'
|
||||
v-if='!collection.id'
|
||||
:rules="[$validators.required('common.name')]"
|
||||
:label="$t('common.name')"
|
||||
v-model='cohort.name'
|
||||
v-model='collection.name'
|
||||
:placeholder='$t("common.name")')
|
||||
template(v-slot:append-outer v-if='!cohort.id')
|
||||
v-btn(text @click='saveCohort' color='primary' :loading='loading'
|
||||
:disabled='!valid || loading || !!cohort.id') {{$t('common.save')}}
|
||||
h3(v-else class='text-h5' v-text='cohort.name')
|
||||
template(v-slot:append-outer v-if='!collection.id')
|
||||
v-btn(text @click='saveCollection' color='primary' :loading='loading'
|
||||
:disabled='!valid || loading || !!collection.id') {{$t('common.save')}}
|
||||
h3(v-else class='text-h5' v-text='collection.name')
|
||||
|
||||
v-row
|
||||
v-col(cols=5)
|
||||
v-autocomplete(v-model='filterTags'
|
||||
cache-items
|
||||
:prepend-icon="mdiTagMultiple"
|
||||
|
||||
chips small-chips multiple deletable-chips hide-no-data hide-selected persistent-hint
|
||||
:disabled="!cohort.id"
|
||||
:disabled="!collection.id"
|
||||
placeholder='Tutte'
|
||||
@input.native='searchTags'
|
||||
@focus='searchTags'
|
||||
:delimiters="[',', ';']"
|
||||
:items="tags"
|
||||
:label="$t('common.tags')")
|
||||
|
@ -52,7 +52,7 @@ v-container
|
|||
clearable
|
||||
return-object
|
||||
item-text='name'
|
||||
:disabled="!cohort.id"
|
||||
:disabled="!collection.id"
|
||||
@input.native="searchPlaces"
|
||||
:delimiters="[',', ';']"
|
||||
:items="places"
|
||||
|
@ -68,7 +68,7 @@ v-container
|
|||
//- v-list-item-subtitle(v-text='item.address')
|
||||
|
||||
v-col(cols=2)
|
||||
v-btn(color='primary' text @click='addFilter' :disabled='!cohort.id || !filterPlaces.length && !filterTags.length') add <v-icon v-text='mdiPlus'></v-icon>
|
||||
v-btn(color='primary' text @click='addFilter' :disabled='!collection.id || !filterPlaces.length && !filterTags.length') add <v-icon v-text='mdiPlus'></v-icon>
|
||||
|
||||
|
||||
v-data-table(
|
||||
|
@ -91,17 +91,17 @@ v-container
|
|||
|
||||
v-card-text
|
||||
v-data-table(
|
||||
:headers='cohortHeaders'
|
||||
:items='cohorts'
|
||||
:hide-default-footer='cohorts.length<5'
|
||||
:headers='collectionHeaders'
|
||||
:items='collections'
|
||||
:hide-default-footer='collections.length<5'
|
||||
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||
:search='search')
|
||||
template(v-slot:item.filters='{item}')
|
||||
span {{cohortFilters(item)}}
|
||||
span {{collectionFilters(item)}}
|
||||
template(v-slot:item.actions='{item}')
|
||||
v-btn(@click='editCohort(item)' color='primary' icon)
|
||||
v-btn(@click='editCollection(item)' color='primary' icon)
|
||||
v-icon(v-text='mdiPencil')
|
||||
v-btn(@click='removeCohort(item)' color='error' icon)
|
||||
v-btn(@click='removeCollection(item)' color='error' icon)
|
||||
v-icon(v-text='mdiDeleteForever')
|
||||
|
||||
</template>
|
||||
|
@ -118,16 +118,16 @@ export default {
|
|||
dialog: false,
|
||||
valid: false,
|
||||
search: '',
|
||||
cohort: { name: '', id: null },
|
||||
collection: { name: '', id: null },
|
||||
filterTags: [],
|
||||
filterPlaces: [],
|
||||
tags: [],
|
||||
places: [],
|
||||
cohorts: [],
|
||||
collections: [],
|
||||
filters: [],
|
||||
tagName: '',
|
||||
placeName: '',
|
||||
cohortHeaders: [
|
||||
collectionHeaders: [
|
||||
{ value: 'name', text: 'Name' },
|
||||
{ value: 'filters', text: 'Filters' },
|
||||
{ value: 'actions', text: 'Actions', align: 'right' }
|
||||
|
@ -140,7 +140,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async fetch () {
|
||||
this.cohorts = await this.$axios.$get('/cohorts?withFilters=true')
|
||||
this.collections = await this.$axios.$get('/collections?withFilters=true')
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -150,8 +150,8 @@ export default {
|
|||
searchPlaces: debounce(async function (ev) {
|
||||
this.places = await this.$axios.$get(`/place?search=${ev.target.value}`)
|
||||
}, 100),
|
||||
cohortFilters (cohort) {
|
||||
return cohort.filters.map(f => {
|
||||
collectionFilters (collection) {
|
||||
return collection.filters.map(f => {
|
||||
return '(' + f.tags?.join(', ') + f.places?.map(p => p.name).join(', ') + ')'
|
||||
}).join(' - ')
|
||||
},
|
||||
|
@ -159,28 +159,28 @@ export default {
|
|||
this.loading = true
|
||||
const tags = this.filterTags
|
||||
const places = this.filterPlaces.map(p => ({ id: p.id, name: p.name }))
|
||||
const filter = await this.$axios.$post('/filter', { cohortId: this.cohort.id, tags, places })
|
||||
const filter = await this.$axios.$post('/filter', { collectionId: this.collection.id, tags, places })
|
||||
this.$fetch()
|
||||
this.filters.push(filter)
|
||||
this.filterTags = []
|
||||
this.filterPlaces = []
|
||||
this.loading = false
|
||||
},
|
||||
async editCohort (cohort) {
|
||||
this.cohort = { ...cohort }
|
||||
this.filters = await this.$axios.$get(`/filter/${cohort.id}`)
|
||||
async editCollection (collection) {
|
||||
this.collection = { ...collection }
|
||||
this.filters = await this.$axios.$get(`/filter/${collection.id}`)
|
||||
this.dialog = true
|
||||
},
|
||||
newCohort () {
|
||||
this.cohort = { name: '', id: null }
|
||||
newCollection () {
|
||||
this.collection = { name: '', id: null }
|
||||
this.filters = []
|
||||
this.dialog = true
|
||||
},
|
||||
async saveCohort () {
|
||||
async saveCollection () {
|
||||
if (!this.$refs.form.validate()) return
|
||||
this.loading = true
|
||||
this.cohort.name = this.cohort.name.trim()
|
||||
this.cohort = await this.$axios.$post('/cohorts', this.cohort)
|
||||
this.collection.name = this.collection.name.trim()
|
||||
this.collection = await this.$axios.$post('/collections', this.collection)
|
||||
this.$fetch()
|
||||
this.loading = false
|
||||
},
|
||||
|
@ -195,12 +195,12 @@ export default {
|
|||
this.loading = false
|
||||
}
|
||||
},
|
||||
async removeCohort (cohort) {
|
||||
const ret = await this.$root.$confirm('admin.delete_cohort_confirm', { cohort: cohort.name })
|
||||
async removeCollection (collection) {
|
||||
const ret = await this.$root.$confirm('admin.delete_collection_confirm', { collection: collection.name })
|
||||
if (!ret) { return }
|
||||
try {
|
||||
await this.$axios.$delete(`/cohort/${cohort.id}`)
|
||||
this.cohorts = this.cohorts.filter(c => c.id !== cohort.id)
|
||||
await this.$axios.$delete(`/collection/${collection.id}`)
|
||||
this.collections = this.collections.filter(c => c.id !== collection.id)
|
||||
} catch (e) {
|
||||
const err = get(e, 'response.data.errors[0].message', e)
|
||||
this.$root.$message(this.$t(err), { color: 'error' })
|
|
@ -87,7 +87,7 @@
|
|||
"import": "Import",
|
||||
"max_events": "N. max events",
|
||||
"label": "Label",
|
||||
"blobs": "Blobs",
|
||||
"collections": "Collections",
|
||||
"close": "Close"
|
||||
},
|
||||
"login": {
|
||||
|
@ -235,9 +235,9 @@
|
|||
"admin_email": "Admin e-mail",
|
||||
"widget": "Widget",
|
||||
"wrong_domain_warning": "The baseurl configured in config.json <b>({baseurl})</b> differs from the one you're visiting <b>({url})</b>",
|
||||
"new_blob": "New blob",
|
||||
"blobs_description": "Blobs are groupings of events by tags and places. They will be displayed on the home page",
|
||||
"edit_blob": "Edit Blob"
|
||||
"new_collection": "New collection",
|
||||
"collections_description": "Collections are groupings of events by tags and places. They will be displayed on the home page",
|
||||
"edit_collection": "Edit Collection"
|
||||
},
|
||||
"auth": {
|
||||
"not_confirmed": "Not confirmed yet…",
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
"import": "Importa",
|
||||
"max_events": "N. massimo eventi",
|
||||
"label": "Etichetta",
|
||||
"blobs": "Bolle"
|
||||
"collections": "Bolle"
|
||||
},
|
||||
"login": {
|
||||
"description": "Entrando puoi pubblicare nuovi eventi.",
|
||||
|
@ -232,10 +232,10 @@
|
|||
"smtp_test_success": "Una mail di test è stata inviata all'indirizzo {admin_email}, controlla la tua casella di posta",
|
||||
"smtp_test_button": "Invia una mail di prova",
|
||||
"admin_email": "E-mail dell'admin",
|
||||
"new_blob": "Crea bolla",
|
||||
"new_collection": "Crea bolla",
|
||||
"wrong_domain_warning": "Il \"baseurl\" configurato in config.json <b>({baseurl})</b> è diverso da quello che stai visitando <b>({url})</b>",
|
||||
"blobs_description": "Le bolle sono raggruppamenti di eventi per tag e posti.",
|
||||
"edit_blob": "Modifica bolla"
|
||||
"collections_description": "Le bolle sono raggruppamenti di eventi per tag e posti.",
|
||||
"edit_collection": "Modifica bolla"
|
||||
},
|
||||
"auth": {
|
||||
"not_confirmed": "Non ancora confermato…",
|
||||
|
|
43
pages/collection/_collection.vue
Normal file
43
pages/collection/_collection.vue
Normal file
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<v-container class='px-0' fluid>
|
||||
|
||||
<h1 class='d-block text-h3 font-weight-black text-center align-center text-uppercase mt-10 mb-12 mx-auto w-100 text-underline'><u>{{collection}}</u></h1>
|
||||
|
||||
<!-- Events -->
|
||||
<div class='mb-2 mt-1 pl-1 pl-sm-2' id="events">
|
||||
<Event :event='event' v-for='(event, idx) in events' :lazy='idx>2' :key='event.id'></Event>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
import Event from '@/components/Event'
|
||||
|
||||
export default {
|
||||
name: 'Collection',
|
||||
components: { Event },
|
||||
head () {
|
||||
const title = `${this.settings.title} - ${this.collection}`
|
||||
return {
|
||||
title,
|
||||
link: [
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/collection/${this.collection}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/collection/${this.collection}` }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: mapState(['settings']),
|
||||
async asyncData ({ $axios, params, error }) {
|
||||
try {
|
||||
const collection = params.collection
|
||||
const events = await $axios.$get(`/collections/${collection}`)
|
||||
return { events, collection }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
error({ statusCode: 400, message: 'Error!' })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<v-container class='px-0' fluid>
|
||||
|
||||
<h1 class='d-block text-h3 font-weight-black text-center align-center text-uppercase mt-10 mb-12 mx-auto w-100 text-underline'><u>{{cohort}}</u></h1>
|
||||
|
||||
<!-- Events -->
|
||||
<div class='mb-2 mt-1 pl-1 pl-sm-2' id="events">
|
||||
<Event :event='event' v-for='(event, idx) in events' :lazy='idx>2' :key='event.id'></Event>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import Event from '@/components/Event'
|
||||
|
||||
export default {
|
||||
name: 'Tag',
|
||||
components: { Event },
|
||||
async asyncData ({ $axios, params, error }) {
|
||||
try {
|
||||
const cohort = params.cohort
|
||||
const events = await $axios.$get(`/cohorts/${cohort}`)
|
||||
return { events, cohort }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
error({ statusCode: 400, message: 'Error!' })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
|
@ -1,38 +1,42 @@
|
|||
const Cohort = require('../models/cohort')
|
||||
const Collection = require('../models/collection')
|
||||
const Filter = require('../models/filter')
|
||||
const Event = require('../models/event')
|
||||
const Tag = require('../models/tag')
|
||||
const Place = require('../models/place')
|
||||
const log = require('../../log')
|
||||
const dayjs = require('dayjs')
|
||||
|
||||
const { col: Col } = require('../../helpers')
|
||||
const { Op, Sequelize } = require('sequelize')
|
||||
|
||||
const cohortController = {
|
||||
const collectionController = {
|
||||
|
||||
async getAll (req, res) {
|
||||
const withFilters = req.query.withFilters
|
||||
let cohorts
|
||||
let collections
|
||||
if (withFilters) {
|
||||
cohorts = await Cohort.findAll({ include: [Filter] })
|
||||
collections = await Collection.findAll({ include: [Filter] })
|
||||
|
||||
} else {
|
||||
cohorts = await Cohort.findAll()
|
||||
collections = await Collection.findAll()
|
||||
}
|
||||
|
||||
return res.json(cohorts)
|
||||
return res.json(collections)
|
||||
},
|
||||
|
||||
// return events from cohort
|
||||
// return events from collection
|
||||
async getEvents (req, res) {
|
||||
const format = req.params.format || 'json'
|
||||
const name = req.params.name
|
||||
|
||||
const cohort = await Cohort.findOne({ where: { name } })
|
||||
if (!cohort) {
|
||||
const collection = await Collection.findOne({ where: { name } })
|
||||
if (!collection) {
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
const filters = await Filter.findAll({ where: { cohortId: cohort.id } })
|
||||
const filters = await Filter.findAll({ where: { collectionId: collection.id } })
|
||||
|
||||
if (!filters.length) {
|
||||
return res.json([])
|
||||
}
|
||||
const start = dayjs().unix()
|
||||
const where = {
|
||||
// do not include parent recurrent event
|
||||
|
@ -47,19 +51,11 @@ const cohortController = {
|
|||
// }
|
||||
}
|
||||
|
||||
// if (!show_recurrent) {
|
||||
// where.parentId = null
|
||||
// }
|
||||
|
||||
// if (end) {
|
||||
// where.start_datetime = { [Op.lte]: end }
|
||||
// }
|
||||
|
||||
const replacements = []
|
||||
const ors = []
|
||||
filters.forEach(f => {
|
||||
if (f.tags && f.tags.length) {
|
||||
const tags = Sequelize.fn('EXISTS', Sequelize.literal('SELECT 1 FROM event_tags WHERE "event_tags"."eventId"="event".id AND "tagTag" in (?)'))
|
||||
const tags = Sequelize.fn('EXISTS', Sequelize.literal(`SELECT 1 FROM event_tags WHERE ${Col('event_tags.eventId')}=event.id AND ${Col('tagTag')} in (?)`))
|
||||
replacements.push(f.tags)
|
||||
if (f.places && f.places.length) {
|
||||
ors.push({ [Op.and]: [ { placeId: f.places.map(p => p.id) },tags] })
|
||||
|
@ -71,21 +67,7 @@ const cohortController = {
|
|||
}
|
||||
})
|
||||
|
||||
// if (tags && places) {
|
||||
// where[Op.or] = {
|
||||
// placeId: places ? places.split(',') : [],
|
||||
// // '$tags.tag$': Sequelize.literal(`EXISTS (SELECT 1 FROM event_tags WHERE tagTag in ( ${Sequelize.QueryInterface.escape(tags)} ) )`)
|
||||
// }
|
||||
// } else if (tags) {
|
||||
// where[Op.and] = Sequelize.literal(`EXISTS (SELECT 1 FROM event_tags WHERE event_tags.eventId=event.id AND tagTag in (?))`)
|
||||
// replacements.push(tags)
|
||||
// } else if (places) {
|
||||
// where.placeId = places.split(',')
|
||||
// }
|
||||
|
||||
if (ors.length) {
|
||||
where[Op.or] = ors
|
||||
}
|
||||
where[Op.and] = { [Op.or]: ors }
|
||||
|
||||
const events = await Event.findAll({
|
||||
where,
|
||||
|
@ -96,7 +78,7 @@ const cohortController = {
|
|||
include: [
|
||||
{
|
||||
model: Tag,
|
||||
order: [Sequelize.literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
|
||||
// order: [Sequelize.literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
|
||||
attributes: ['tag'],
|
||||
through: { attributes: [] }
|
||||
},
|
||||
|
@ -120,43 +102,43 @@ const cohortController = {
|
|||
},
|
||||
|
||||
async add (req, res) {
|
||||
const cohortDetail = {
|
||||
const collectionDetail = {
|
||||
name: req.body.name,
|
||||
isActor: true,
|
||||
isTop: true
|
||||
}
|
||||
|
||||
// TODO: validation
|
||||
log.info('Create cohort: ' + req.body.name)
|
||||
const cohort = await Cohort.create(cohortDetail)
|
||||
res.json(cohort)
|
||||
log.info('Create collection: ' + req.body.name)
|
||||
const collection = await Collection.create(collectionDetail)
|
||||
res.json(collection)
|
||||
},
|
||||
|
||||
async remove (req, res) {
|
||||
const cohort_id = req.params.id
|
||||
log.info('Remove cohort', cohort_id)
|
||||
const collection_id = req.params.id
|
||||
log.info('Remove collection', collection_id)
|
||||
try {
|
||||
const cohort = await Cohort.findByPk(cohort_id)
|
||||
await cohort.destroy()
|
||||
const collection = await Collection.findByPk(collection_id)
|
||||
await collection.destroy()
|
||||
res.sendStatus(200)
|
||||
} catch (e) {
|
||||
log.error('Remove cohort failed:', e)
|
||||
log.error('Remove collection failed:', e)
|
||||
res.sendStatus(404)
|
||||
}
|
||||
},
|
||||
|
||||
async getFilters (req, res) {
|
||||
const cohortId = req.params.cohort_id
|
||||
const filters = await Filter.findAll({ where: { cohortId } })
|
||||
const collectionId = req.params.collection_id
|
||||
const filters = await Filter.findAll({ where: { collectionId } })
|
||||
return res.json(filters)
|
||||
},
|
||||
|
||||
async addFilter (req, res) {
|
||||
const cohortId = req.body.cohortId
|
||||
const collectionId = req.body.collectionId
|
||||
const tags = req.body.tags
|
||||
const places = req.body.places
|
||||
try {
|
||||
const filter = await Filter.create({ cohortId, tags, places })
|
||||
const filter = await Filter.create({ collectionId, tags, places })
|
||||
return res.json(filter)
|
||||
} catch (e) {
|
||||
log.error(String(e))
|
||||
|
@ -183,4 +165,4 @@ const cohortController = {
|
|||
|
||||
|
||||
|
||||
module.exports = cohortController
|
||||
module.exports = collectionController
|
|
@ -1,9 +1,9 @@
|
|||
const { Model, DataTypes } = require('sequelize')
|
||||
const sequelize = require('./index').sequelize
|
||||
|
||||
class Cohort extends Model {}
|
||||
class Collection extends Model {}
|
||||
|
||||
Cohort.init({
|
||||
Collection.init({
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
|
@ -21,7 +21,7 @@ Cohort.init({
|
|||
isTop: {
|
||||
type: DataTypes.BOOLEAN
|
||||
}
|
||||
}, { sequelize, modelName: 'cohort', timestamps: false })
|
||||
}, { sequelize, modelName: 'collection', timestamps: false })
|
||||
|
||||
|
||||
module.exports = Cohort
|
||||
module.exports = Collection
|
30
server/migrations/20220617203517-collection.js
Normal file
30
server/migrations/20220617203517-collection.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
return Promise.all(
|
||||
[
|
||||
await queryInterface.renameTable('cohorts', 'collections'),
|
||||
await queryInterface.renameColumn('filters', 'cohortId', 'collectionId'),
|
||||
await queryInterface.changeColumn('filters', 'collectionId', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
references: {
|
||||
model: 'collections',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
}),
|
||||
])
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add reverting commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue