update
This commit is contained in:
parent
1bb080f7f1
commit
0c881c919b
15 changed files with 260 additions and 83 deletions
|
@ -9,6 +9,7 @@
|
||||||
"@types/html-to-text": "^5.1.1",
|
"@types/html-to-text": "^5.1.1",
|
||||||
"@types/ioredis": "^4.17.0",
|
"@types/ioredis": "^4.17.0",
|
||||||
"@types/jest": "^26.0.4",
|
"@types/jest": "^26.0.4",
|
||||||
|
"@types/moment": "^2.13.0",
|
||||||
"@types/mongoose": "^5.7.28",
|
"@types/mongoose": "^5.7.28",
|
||||||
"@types/multer": "^1.4.3",
|
"@types/multer": "^1.4.3",
|
||||||
"@types/multer-s3": "^2.7.7",
|
"@types/multer-s3": "^2.7.7",
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
"html-to-text": "^5.1.1",
|
"html-to-text": "^5.1.1",
|
||||||
"ioredis": "^4.17.3",
|
"ioredis": "^4.17.3",
|
||||||
"jest": "^26.1.0",
|
"jest": "^26.1.0",
|
||||||
|
"moment": "^2.27.0",
|
||||||
"mongoose": "^5.9.20",
|
"mongoose": "^5.9.20",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"multer-s3": "^2.9.0",
|
"multer-s3": "^2.9.0",
|
||||||
|
|
|
@ -17,9 +17,9 @@ export default class Utils {
|
||||||
bool = bool && (
|
bool = bool && (
|
||||||
explored !== undefined && explored !== null
|
explored !== undefined && explored !== null
|
||||||
)
|
)
|
||||||
if (i - 1 === levels && isStr) {
|
}
|
||||||
bool = bool && typeof explored === 'string' && explored.length > 0
|
if (isStr) {
|
||||||
}
|
bool = bool && typeof explored === 'string' && explored.length > 0
|
||||||
}
|
}
|
||||||
return bool
|
return bool
|
||||||
}
|
}
|
||||||
|
|
14
src/app.ts
14
src/app.ts
|
@ -16,6 +16,12 @@ import twig from 'twig'
|
||||||
import EmailService from './EmailService'
|
import EmailService from './EmailService'
|
||||||
import ErrorController from './controllers/ErrorController'
|
import ErrorController from './controllers/ErrorController'
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (err) => {
|
||||||
|
console.error(err)
|
||||||
|
console.log('unhandledRejection!')
|
||||||
|
process.exit()
|
||||||
|
})
|
||||||
|
|
||||||
dotenv.config({
|
dotenv.config({
|
||||||
path: __dirname + '/../.env'
|
path: __dirname + '/../.env'
|
||||||
})
|
})
|
||||||
|
@ -110,11 +116,7 @@ let main = async () => {
|
||||||
|
|
||||||
app.get('/500', ErrorController.internalError)
|
app.get('/500', ErrorController.internalError)
|
||||||
|
|
||||||
app.use((err: any, req: express.Request, res: express.Response, next: express.RequestHandler) => {
|
app.use(ErrorController.handleServerError)
|
||||||
console.error(err.stack)
|
|
||||||
//res.status(res.statusCode).json({ success: false, error: err.stack })
|
|
||||||
ErrorController.handle(500, 'Erreur interne', 'Ouups je sais pas quoi dire', '💥', err.stack)(req, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(ErrorController.notFoundHandler())
|
app.use(ErrorController.notFoundHandler())
|
||||||
|
|
||||||
|
@ -123,4 +125,4 @@ let main = async () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -30,11 +30,22 @@ export default class AdminOrganizationController {
|
||||||
let body: any = {
|
let body: any = {
|
||||||
token: AdminOrganizationController.generateToken(),
|
token: AdminOrganizationController.generateToken(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
slug: slugify(req.body.adminName).toLowerCase(),
|
// start the slugs array
|
||||||
|
slugs: [slugify(req.body.adminName)],
|
||||||
...req.body,
|
...req.body,
|
||||||
...{
|
...{
|
||||||
proposedVersion: {
|
proposedVersion: {
|
||||||
name: req.body.adminName
|
name: req.body.adminName,
|
||||||
|
contacts: {
|
||||||
|
facebook: '',
|
||||||
|
twitter: '',
|
||||||
|
instagram: '',
|
||||||
|
website: '',
|
||||||
|
address: '',
|
||||||
|
person: '',
|
||||||
|
email: req.body.email,
|
||||||
|
phone: ''
|
||||||
|
}
|
||||||
// descriptionShort: '',
|
// descriptionShort: '',
|
||||||
// descriptionLong: '',
|
// descriptionLong: '',
|
||||||
// contacts: [],
|
// contacts: [],
|
||||||
|
@ -49,22 +60,35 @@ export default class AdminOrganizationController {
|
||||||
}
|
}
|
||||||
Organization.create(body).then(data => {
|
Organization.create(body).then(data => {
|
||||||
AdminOrganizationController.sendEmailTokenUniversal(data)
|
AdminOrganizationController.sendEmailTokenUniversal(data)
|
||||||
res.json({ success: true, data })
|
res.json({ success: true, data, body })
|
||||||
}).catch(err => res.status(400).json({ success: false, errors: err.errors }))
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors }))
|
||||||
}
|
}
|
||||||
|
|
||||||
static update(req: express.Request, res: express.Response) {
|
static update(req: express.Request, res: express.Response) {
|
||||||
let extra: any = {}
|
Organization.findById(req.params.id)
|
||||||
if (req.body.name !== undefined) {
|
.then(data => {
|
||||||
extra.slug = slugify(req.body.name)
|
// generate extra slugs
|
||||||
}
|
let extra: any = {}
|
||||||
Organization.updateOne({ _id: req.params.id }, {
|
if (req.body.name !== undefined) {
|
||||||
...extra,
|
let slug = slugify(req.body.name)
|
||||||
...req.body,
|
// only add this slug if the proposed slug is not found in the list of current slug
|
||||||
updatedAt: new Date()
|
let currentSlugs: string[] = []
|
||||||
})
|
if (data !== null && Array.isArray(data.get('slugs'))) {
|
||||||
.then(data => res.json({ success: true, data }))
|
currentSlugs = data.get('slugs')
|
||||||
.catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
}
|
||||||
|
if (currentSlugs.filter(s => s === slug).length === 0) {
|
||||||
|
extra.slugs = currentSlugs.concat([slug])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Organization.updateOne({ _id: req.params.id }, {
|
||||||
|
...extra,
|
||||||
|
...req.body,
|
||||||
|
updatedAt: new Date()
|
||||||
|
})
|
||||||
|
.then(data => res.json({ success: true, data }))
|
||||||
|
.catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
|
})
|
||||||
|
.catch(err => res.status(400).json({ success: false, errors: err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
static destroy(req: express.Request, res: express.Response) {
|
static destroy(req: express.Request, res: express.Response) {
|
||||||
|
|
|
@ -34,15 +34,19 @@ export default class DelegateController {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const next = (tag: any) => {
|
const next = (tags: any) => {
|
||||||
// only update proposedVersion
|
// only update proposedVersion
|
||||||
let proposedVersion: any = req.body
|
let proposedVersion: any = req.body
|
||||||
proposedVersion.tag = tag
|
proposedVersion.tags = tags
|
||||||
proposedVersion.descriptionLong = proposedVersion.descriptionLong.replace(/\n/g, '')
|
|
||||||
proposedVersion.descriptionLong = sanitizeHtml(
|
// sanitize long description
|
||||||
proposedVersion.descriptionLong,
|
if (Utils.isStrUsable(proposedVersion.descriptionLong)) {
|
||||||
{ allowedTags: ['h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'li', 'b', 'i', 'abbr', 'strong', 'em', 'hr', 'br', 'div', 'pre'] }
|
proposedVersion.descriptionLong = proposedVersion.descriptionLong.replace(/\n/g, '')
|
||||||
)
|
proposedVersion.descriptionLong = sanitizeHtml(
|
||||||
|
proposedVersion.descriptionLong,
|
||||||
|
{ allowedTags: ['h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'li', 'b', 'i', 'abbr', 'strong', 'em', 'hr', 'br', 'div', 'pre'] }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// validate contact.address
|
// validate contact.address
|
||||||
// validate all fields to not overflow
|
// validate all fields to not overflow
|
||||||
|
@ -73,6 +77,19 @@ export default class DelegateController {
|
||||||
MediaService.delete(organization.proposedVersion.cover.key, 'coverDeleted')
|
MediaService.delete(organization.proposedVersion.cover.key, 'coverDeleted')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete unused thumbnail
|
||||||
|
// if the thumbnail is reset we must delete the old one only if it is unused in the published version
|
||||||
|
if (
|
||||||
|
Utils.isStrUsable(organization, 'proposedVersion.thumbnail.key') &&
|
||||||
|
!Utils.isStrUsable(proposedVersion, 'thumbnail.key') &&
|
||||||
|
!(
|
||||||
|
Utils.isStrUsable(organization, 'publishedVersion.thumbnail.key') &&
|
||||||
|
organization.publishedVersion.thumbnail.key === organization.proposedVersion.thumbnail.key
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
MediaService.delete(organization.proposedVersion.thumbnail.key, 'thumbnailDeleted')
|
||||||
|
}
|
||||||
|
|
||||||
// format schedule, pricing
|
// format schedule, pricing
|
||||||
if (!Array.isArray(proposedVersion.schedule)) {
|
if (!Array.isArray(proposedVersion.schedule)) {
|
||||||
proposedVersion.schedule = []
|
proposedVersion.schedule = []
|
||||||
|
@ -93,22 +110,27 @@ export default class DelegateController {
|
||||||
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
}).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.tag !== undefined && req.body.tag !== null && req.body.tag.length > 2) {
|
if (Utils.isUsable(req.body, 'tags') && Array.isArray(req.body.tags)) {
|
||||||
// skip the tag part if the tag didn't changed
|
// skip the tag part if the tag didn't changed
|
||||||
if (
|
if (
|
||||||
organization.proposedVersion.tag === undefined ||
|
organization.proposedVersion.tags === undefined ||
|
||||||
organization.proposedVersion.tag === null ||
|
organization.proposedVersion.tags === null ||
|
||||||
req.body.tag !== organization.proposedVersion.tag._id
|
req.body.tags !== organization.proposedVersion.tags
|
||||||
) {
|
) {
|
||||||
// if the tag is defined, search the tag id
|
// if the tag is defined, search the tag id
|
||||||
Tag.findById(req.body.tag).then(tag => {
|
let promises: Promise<string>[] = []
|
||||||
next(tag)
|
req.body.tags.forEach((id: string) => {
|
||||||
}).catch(err => {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
console.log(err)
|
Tag.findById(id).then((tag: any) => resolve(tag.get('_id'))).catch((err: any) => reject(err))
|
||||||
res.status(400).json({ success: false, errors: err, _note: 'The tag id provided is invalid' })
|
}))
|
||||||
|
})
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
next(req.body.tags)
|
||||||
|
}).catch((err) => {
|
||||||
|
return res.status(400).json({ success: false, errors: err, _note: 'One of the tag id provided is invalid' })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
next(organization.tag)
|
next(organization.tags)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
next(null)
|
next(null)
|
||||||
|
|
|
@ -13,6 +13,12 @@ export default class ErrorController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static handleServerError(err: any, req: express.Request, res: express.Response, next: any) {
|
||||||
|
console.error(err.stack)
|
||||||
|
//res.status(res.statusCode).json({ success: false, error: err.stack })
|
||||||
|
return ErrorController.handle(500, 'Erreur interne', 'Ouups je sais pas quoi dire', '💥', err.stack)(req, res)
|
||||||
|
}
|
||||||
|
|
||||||
static notFoundHandler() {
|
static notFoundHandler() {
|
||||||
return ErrorController.handle(404, 'Page introuvable', 'Mais où peut donc se trouver cette page ?', '🔍 🕵️')
|
return ErrorController.handle(404, 'Page introuvable', 'Mais où peut donc se trouver cette page ?', '🔍 🕵️')
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { IconService, IconInterface } from '../IconService'
|
||||||
import IORedis from 'ioredis'
|
import IORedis from 'ioredis'
|
||||||
import Tag from '../models/Tag'
|
import Tag from '../models/Tag'
|
||||||
import ErrorController from './ErrorController'
|
import ErrorController from './ErrorController'
|
||||||
|
import Utils from '../Utils'
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
export default class PublicController {
|
export default class PublicController {
|
||||||
|
|
||||||
|
@ -19,45 +21,67 @@ export default class PublicController {
|
||||||
// data: await client.get('hello')
|
// data: await client.get('hello')
|
||||||
// })
|
// })
|
||||||
Tag.find().then(tags => {
|
Tag.find().then(tags => {
|
||||||
Organization.find().then(organizations => {
|
let isProposed = Utils.isStrUsable(req.query, 'only')
|
||||||
|
if (isProposed && !mongoose.Types.ObjectId.isValid(req.query.only)) {
|
||||||
|
return ErrorController.handleServerError({ stack: 'Invalid object id in only query param'}, req, res, [])
|
||||||
|
}
|
||||||
|
Organization.find(isProposed ? { _id: req.query.only } : {}).then(organizations => {
|
||||||
|
if (!isProposed) {
|
||||||
|
organizations = organizations.filter(o => o.get('publishedAt') !== undefined && o.get('publishedAt') !== null)
|
||||||
|
}
|
||||||
res.render('home.twig', {
|
res.render('home.twig', {
|
||||||
|
isProposed,
|
||||||
tags,
|
tags,
|
||||||
tagsJSON: JSON.stringify(tags),
|
tagsJSON: JSON.stringify(tags),
|
||||||
organizationsJSON: JSON.stringify(organizations
|
organizationsJSON: JSON.stringify(organizations
|
||||||
.filter(o => o.get('publishedAt') !== undefined && o.get('publishedAt') !== null)
|
|
||||||
.map(o => {
|
.map(o => {
|
||||||
const version = o.get('publishedVersion')
|
const version = isProposed ? o.get('proposedVersion'): o.get('publishedVersion')
|
||||||
return {
|
return {
|
||||||
|
_id: o._id,
|
||||||
name: version.name,
|
name: version.name,
|
||||||
description: version.descriptionShort,
|
description: version.descriptionShort,
|
||||||
thumbnail: version.thumbnail.location,
|
thumbnail: version.thumbnail.location,
|
||||||
tag: version.tag === null ? 'tag_not_found' : version.tag._id,
|
tags: version.tags === null ? 'tags_not_found' : version.tags,
|
||||||
slug: o.get('slug')
|
slugs: o.get('slugs'),
|
||||||
|
isProposed
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
}).catch(err => () => {
|
||||||
|
console.log(err)
|
||||||
|
return ErrorController.handleServerError(err, req, res, [])
|
||||||
})
|
})
|
||||||
|
}).catch(err => () => {
|
||||||
|
console.log(err)
|
||||||
|
return ErrorController.handleServerError(err, req, res, [])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async organization(req: express.Request, res: express.Response) {
|
static async organization(req: express.Request, res: express.Response) {
|
||||||
Organization.find({ slug: req.params.slug }).then(data => {
|
Organization.find({ slugs: { '$in': req.params.slug } }).then(data => {
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return ErrorController.notFoundHandler()(req, res)
|
return ErrorController.notFoundHandler()(req, res)
|
||||||
} else {
|
} else {
|
||||||
const org = data[0]
|
const org = data[0]
|
||||||
let version = org.get('publishedVersion')
|
let version = org.get('publishedVersion')
|
||||||
|
let lastPublished = org.get('publishedAt')
|
||||||
|
let isProposed = false
|
||||||
if (req.query.version === 'proposed') {
|
if (req.query.version === 'proposed') {
|
||||||
|
isProposed = true
|
||||||
|
lastPublished = org.get('updatedAt')
|
||||||
version = org.get('proposedVersion')
|
version = org.get('proposedVersion')
|
||||||
} else if (org.get('publishedAt') === undefined || org.get('publishedAt') === null) {
|
} else if (lastPublished === undefined || lastPublished === null) {
|
||||||
return ErrorController.notFoundHandler()(req, res)
|
return ErrorController.notFoundHandler()(req, res)
|
||||||
}
|
}
|
||||||
if (version.contacts !== null && version.contacts !== undefined) {
|
// if (lastPublished !== null) {
|
||||||
if (typeof version.contacts.address === 'string') {
|
// lastPublished = lastPublished
|
||||||
|
// }
|
||||||
|
if (Utils.isUsable(version.contacts)) {
|
||||||
|
if (Utils.isStrUsable(version.contacts, 'address')) {
|
||||||
version.contacts.address = version.contacts.address.split('\n')
|
version.contacts.address = version.contacts.address.split('\n')
|
||||||
}
|
}
|
||||||
if (typeof version.contacts.phone === 'string') {
|
if (Utils.isStrUsable(version.contacts, 'phone')) {
|
||||||
let phone = version.contacts.phone
|
let phone = version.contacts.phone
|
||||||
if (phone.indexOf('+33') === 0) {
|
if (phone.indexOf('+33') === 0) {
|
||||||
phone = '0' + phone.substr(3)
|
phone = '0' + phone.substr(3)
|
||||||
|
@ -83,10 +107,16 @@ export default class PublicController {
|
||||||
}
|
}
|
||||||
res.render('organization.twig', {
|
res.render('organization.twig', {
|
||||||
layout: 'standalone',
|
layout: 'standalone',
|
||||||
data: version
|
data: version,
|
||||||
|
lastPublished: lastPublished.toLocaleDateString('fr-FR') + ' ' + lastPublished.toLocaleTimeString('fr-FR', { timezone: '+2' }).substr(0, 5),
|
||||||
|
isProposed,
|
||||||
|
id: org.get('_id')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).catch(_ => res.status(404).render('not-found.twig'))
|
}).catch(err => () => {
|
||||||
|
console.log(err)
|
||||||
|
return ErrorController.handleServerError(err, req, res, [])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async about(req: express.Request, res: express.Response) {
|
static async about(req: express.Request, res: express.Response) {
|
||||||
|
|
|
@ -74,14 +74,14 @@ const OrganizationVersion = {
|
||||||
priceLabel: { type: String, required: true },
|
priceLabel: { type: String, required: true },
|
||||||
description: { type: String }
|
description: { type: String }
|
||||||
}],
|
}],
|
||||||
tag: [Tag]
|
tags: [{ type: String }]
|
||||||
}
|
}
|
||||||
|
|
||||||
const Organization = new Schema({
|
const Organization = new Schema({
|
||||||
adminName: { type: String, required: true },
|
adminName: { type: String, required: true },
|
||||||
email: { type: String, required: true },
|
email: { type: String, required: true },
|
||||||
token: { type: String, required: true },
|
token: { type: String, required: true },
|
||||||
slug: [{ type: String }], // aliases system
|
slugs: [{ type: String }], // aliases system
|
||||||
validationState: {
|
validationState: {
|
||||||
type: AllowedString,
|
type: AllowedString,
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
@ -181,6 +181,8 @@
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
box-shadow: 0 0 8px 0px rgba(0,0,0,0.1);
|
box-shadow: 0 0 8px 0px rgba(0,0,0,0.1);
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
height: 12em;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
|
@ -202,7 +204,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-content {
|
.card-content {
|
||||||
/* width: 100%; */
|
width: 100%;
|
||||||
padding: 1.5em;
|
padding: 1.5em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -238,12 +240,14 @@
|
||||||
color: #34495E;
|
color: #34495E;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-link {
|
.card-link {
|
||||||
color: #0029FF;
|
position: absolute;
|
||||||
opacity: 0.75;
|
right: .5em;
|
||||||
text-align: right;
|
bottom: 0;
|
||||||
|
margin-bottom: -.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1350px) {
|
@media (max-width: 1350px) {
|
||||||
|
|
|
@ -10,11 +10,6 @@ let navContent = document.getElementById('nav-content')
|
||||||
let mosaic = document.getElementById('mosaic')
|
let mosaic = document.getElementById('mosaic')
|
||||||
let mosaicHeader = document.getElementById('mosaic-header')
|
let mosaicHeader = document.getElementById('mosaic-header')
|
||||||
|
|
||||||
organizations = organizations.map(org => {
|
|
||||||
org.tag = tags.filter(t => t._id === org.tag)[0]
|
|
||||||
return org
|
|
||||||
})
|
|
||||||
|
|
||||||
navEnabler.onclick = async () => {
|
navEnabler.onclick = async () => {
|
||||||
if (!navOpened) {
|
if (!navOpened) {
|
||||||
// open the menu
|
// open the menu
|
||||||
|
@ -105,28 +100,36 @@ function renderCard(organization) {
|
||||||
titleContainer.appendChild(title)
|
titleContainer.appendChild(title)
|
||||||
|
|
||||||
let icon = createEl('card-icon')
|
let icon = createEl('card-icon')
|
||||||
icon.innerHTML = `<svg
|
if (Array.isArray(organization.tags) && organization.tags.length > 0) {
|
||||||
aria-hidden="true"
|
let tag = tags.filter(tag => organization.tags[0] === tag._id)[0]
|
||||||
focusable="false"
|
icon.innerHTML = `<svg
|
||||||
role="img"
|
aria-hidden="true"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
focusable="false"
|
||||||
viewBox="0 0 ${organization.tag.icon.width} ${organization.tag.icon.height}">
|
role="img"
|
||||||
<path fill="currentColor" d="${organization.tag.icon.path}"></path>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</svg>`
|
viewBox="0 0 ${tag.icon.width} ${tag.icon.height}">
|
||||||
|
<path fill="currentColor" d="${tag.icon.path}"></path>
|
||||||
|
</svg>`
|
||||||
|
}
|
||||||
titleContainer.appendChild(icon)
|
titleContainer.appendChild(icon)
|
||||||
upperContent.appendChild(titleContainer)
|
upperContent.appendChild(titleContainer)
|
||||||
|
|
||||||
let description = createEl('card-description')
|
let description = createEl('card-description')
|
||||||
description.textContent = organization.description
|
description.textContent = organization.description
|
||||||
upperContent.appendChild(description)
|
|
||||||
|
|
||||||
let link = createEl('card-link')
|
//let link = createEl('card-link')
|
||||||
let aTag = createEl(0, 'a')
|
let aTag = createEl('card-link', 'a')
|
||||||
aTag.href = "/association/" + organization.slug
|
aTag.href = "/association/" + organization.slugs[organization.slugs.length - 1]
|
||||||
|
if (organization.isProposed) {
|
||||||
|
aTag.href += "?version=proposed"
|
||||||
|
}
|
||||||
aTag.textContent = "En savoir plus"
|
aTag.textContent = "En savoir plus"
|
||||||
link.appendChild(aTag)
|
description.appendChild(aTag)
|
||||||
|
|
||||||
|
upperContent.appendChild(description)
|
||||||
|
//link.appendChild(aTag)
|
||||||
content.appendChild(upperContent)
|
content.appendChild(upperContent)
|
||||||
content.appendChild(link)
|
//content.appendChild(link)
|
||||||
card.appendChild(content)
|
card.appendChild(content)
|
||||||
|
|
||||||
card.onclick = () => {
|
card.onclick = () => {
|
||||||
|
@ -152,7 +155,7 @@ function enableTag(node) {
|
||||||
if (!all) {
|
if (!all) {
|
||||||
tagId = node.attributes['data-tag-id'].value
|
tagId = node.attributes['data-tag-id'].value
|
||||||
}
|
}
|
||||||
let data = organizations.filter(orga => orga.tag._id === tagId || all)
|
let data = organizations.filter(orga => orga.tags.filter(id => id === tagId).length > 0 || all)
|
||||||
let cards = renderMosaic(data)
|
let cards = renderMosaic(data)
|
||||||
if (currentCardContainer !== null) {
|
if (currentCardContainer !== null) {
|
||||||
mosaic.removeChild(currentCardContainer)
|
mosaic.removeChild(currentCardContainer)
|
||||||
|
|
|
@ -23,6 +23,35 @@ a:hover {
|
||||||
color: #2980b9;
|
color: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sticky footer */
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.up-footer {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
.sticky-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Porposed alert */
|
||||||
|
.proposed-alert {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9999999;
|
||||||
|
left: 1em;
|
||||||
|
bottom: 2em;
|
||||||
|
padding: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: 1px solid #c0392b;
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1600px) {
|
@media (max-width: 1600px) {
|
||||||
.container {
|
.container {
|
||||||
|
|
|
@ -538,13 +538,26 @@ section {
|
||||||
|
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
|
.mentions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
color: #d35400;
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
.mentions div {
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 50% 25% 25%;
|
grid-template-columns: 50% 25% 25%;
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
margin-top: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer div:nth-child(1) {
|
.footer div:nth-child(1) {
|
||||||
|
@ -559,6 +572,9 @@ section {
|
||||||
background-color: rgb(253, 110, 11, .83);
|
background-color: rgb(253, 110, 11, .83);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
RESPONSIVE
|
||||||
|
**/
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.schedule-category-days-container {
|
.schedule-category-days-container {
|
||||||
|
|
|
@ -21,6 +21,7 @@ Github: https://github.com/lefuturiste
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="up-footer">
|
||||||
{% if layout is not defined %}
|
{% if layout is not defined %}
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="container header-container">
|
<div class="container header-container">
|
||||||
|
@ -54,6 +55,17 @@ Github: https://github.com/lefuturiste
|
||||||
{% else %}
|
{% else %}
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if isProposed is defined and isProposed == true %}
|
||||||
|
<div class="proposed-alert">
|
||||||
|
! Version proposé !
|
||||||
|
{# <div class="proposed-alert-content">
|
||||||
|
</div> #}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="sticky-footer">
|
||||||
|
{% block footer %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="container header-container">
|
<div class="container header-container">
|
||||||
<a href="/" class="return">
|
<a href="{% if isProposed is defined and isProposed == true %}/?only={{ id }}{% else %}/{% endif %}" class="return">
|
||||||
<div class="return-icon">
|
<div class="return-icon">
|
||||||
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
<path fill="currentColor" d="M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zM142.1 273l135.5 135.5c9.4 9.4 24.6 9.4 33.9 0l17-17c9.4-9.4 9.4-24.6 0-33.9L226.9 256l101.6-101.6c9.4-9.4 9.4-24.6 0-33.9l-17-17c-9.4-9.4-24.6-9.4-33.9 0L142.1 239c-9.4 9.4-9.4 24.6 0 34z"></path>
|
<path fill="currentColor" d="M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zM142.1 273l135.5 135.5c9.4 9.4 24.6 9.4 33.9 0l17-17c9.4-9.4 9.4-24.6 0-33.9L226.9 256l101.6-101.6c9.4-9.4 9.4-24.6 0-33.9l-17-17c-9.4-9.4-24.6-9.4-33.9 0L142.1 239c-9.4 9.4-9.4 24.6 0 34z"></path>
|
||||||
|
@ -66,17 +66,21 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if data.descriptionLong|length > 0 %}
|
{# {% if data.descriptionLong|length > 0 %} #}
|
||||||
<section>
|
<section>
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>Présentation</h2>
|
<h2>Présentation</h2>
|
||||||
<div class="section-divider"></div>
|
<div class="section-divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{ data.descriptionLong|raw }}
|
{% if data.descriptionLong is not null and data.descriptionLong|length > 7 %}
|
||||||
|
{{ data.descriptionLong|raw }}
|
||||||
|
{% else %}
|
||||||
|
{{ data.descriptionShort }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{# {% endif %} #}
|
||||||
|
|
||||||
{% if data.schedule|length > 0 %}
|
{% if data.schedule|length > 0 %}
|
||||||
<section>
|
<section>
|
||||||
|
@ -274,6 +278,17 @@
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
<div class="mentions">
|
||||||
|
<div class="advice">
|
||||||
|
Contenu rédigée par l'une des associations participant à associations.espacecondorcet.org
|
||||||
|
</div>
|
||||||
|
<div class="last-update">
|
||||||
|
Dernière mise à jour le {{ lastPublished }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div></div>
|
<div></div>
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -667,6 +667,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5"
|
||||||
integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==
|
integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==
|
||||||
|
|
||||||
|
"@types/moment@^2.13.0":
|
||||||
|
version "2.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/moment/-/moment-2.13.0.tgz#604ebd189bc3bc34a1548689404e61a2a4aac896"
|
||||||
|
integrity sha1-YE69GJvDvDShVIaJQE5hoqSqyJY=
|
||||||
|
dependencies:
|
||||||
|
moment "*"
|
||||||
|
|
||||||
"@types/mongodb@*":
|
"@types/mongodb@*":
|
||||||
version "3.5.25"
|
version "3.5.25"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.5.25.tgz#ab187db04d79f8e3f15af236327dc9139d9d4736"
|
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.5.25.tgz#ab187db04d79f8e3f15af236327dc9139d9d4736"
|
||||||
|
@ -3471,6 +3478,11 @@ mkdirp@^0.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
|
moment@*, moment@^2.27.0:
|
||||||
|
version "2.27.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||||
|
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||||
|
|
||||||
mongodb@3.5.9:
|
mongodb@3.5.9:
|
||||||
version "3.5.9"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.9.tgz#799b72be8110b7e71a882bb7ce0d84d05429f772"
|
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.9.tgz#799b72be8110b7e71a882bb7ce0d84d05429f772"
|
||||||
|
|
Loading…
Reference in a new issue