diff --git a/README.md b/README.md index 03e3247..bfc29e6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ Vous pouvez, si vous le souhaitez héberger l'application sur votre propre serve ### Prérequis -Il existe 2 méthodes d'installation, soit via docker soit en mode standalone. Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle. +Il existe 2 méthodes d'installation, soit via docker soit en mode standalone. + +Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle ou configurer le SMTP tel que défini dans la section [variables d'environnements](#env-file). Pour la méthode docker il ne vous faudra rien de plus que `docker` et `docker-compose`. @@ -90,7 +92,7 @@ C'est terminé ! Le site est accessible sur [http://localhost:3001](http://localhost:3001). -:information_source: Information : Vous pouvez, et vous dreviez, également regarder du côté de `systemd`, `pm2` ou encore `supervisor` pour que le service démarre en même temps que votre serveur. +:information_source: Information : Vous pouvez, et vous devriez, également regarder du côté de `systemd`, `pm2` ou encore `supervisor` pour que le service démarre en même temps que votre serveur. ### Aller plus loin @@ -227,6 +229,13 @@ S3_BUCKET # Nom du bucket (musictopus par défaut, à changer impérativement si JOBS_HEADER_KEY # Nom du header utilisé pour l'identification des tâches cron (musictopus par défaut) JOBS_HEADER_VALUE # Valeur de la clé (ooYee9xok7eigo2shiePohyoGh1eepew par défaut) REGISTRATION_OPEN # true/false en fonction de si vous souhaitez activer ou non l'inscription à votre instance (true par défaut) +MAIL_METHOD # permet de définir la façon dont les mails de la page contact sont envoyés (formspree ou smtp) +MAIL_HOST # Adresse du server mail (dams le cas ou MAIL_METHOD est défini sur smtp) +MAIL_PORT # Port d'écoute du serveur smtp (dams le cas ou MAIL_METHOD est défini sur smtp) +MAIL_USER # Adresse mail du compte permettant d'envoyer les mails (dams le cas ou MAIL_METHOD est défini sur smtp) +MAIL_PASSWORD # Mot de passe du compte email (dams le cas ou MAIL_METHOD est défini sur smtp) +MAIL_TO # Adresse mail du contact qui recevra les messages de la page "nous contacter" (dams le cas ou MAIL_METHOD est défini sur smtp) + ``` ## Contributeurs diff --git a/docker-compose.yml.dev b/docker-compose.yml.dev index ca86d9e..b85397e 100644 --- a/docker-compose.yml.dev +++ b/docker-compose.yml.dev @@ -37,6 +37,12 @@ services: JOBS_HEADER_KEY: ${JOBS_HEADER_KEY} JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE} REGISTRATION_OPEN: ${REGISTRATION_OPEN} + MAIL_METHOD: ${MAIL_METHOD} + MAIL_HOST: ${MAIL_HOST} + MAIL_PORT: ${MAIL_PORT} + MAIL_USER: ${MAIL_USER} + MAIL_PASSWORD: ${MAIL_PASSWORD} + MAIL_TO: ${MAIL_TO} networks: - musictopus musictopus-db: diff --git a/fontello.json b/fontello.json new file mode 100644 index 0000000..11e03d9 --- /dev/null +++ b/fontello.json @@ -0,0 +1,118 @@ +{ + "name": "icon", + "css_prefix_text": "icon-", + "css_use_suffix": false, + "hinting": true, + "units_per_em": 1000, + "ascent": 850, + "glyphs": [ + { + "uid": "44e04715aecbca7f266a17d5a7863c68", + "css": "plus", + "code": 59392, + "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59393, + "src": "fontawesome" + }, + { + "uid": "9dd9e835aebe1060ba7190ad2b2ed951", + "css": "search", + "code": 59394, + "src": "fontawesome" + }, + { + "uid": "bf882b30900da12fca090d9796bc3030", + "css": "mail", + "code": 59395, + "src": "fontawesome" + }, + { + "uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1", + "css": "link", + "code": 59396, + "src": "fontawesome" + }, + { + "uid": "e15f0d620a7897e2035c18c80142f6d9", + "css": "link-ext", + "code": 61582, + "src": "fontawesome" + }, + { + "uid": "9bc2902722abb366a213a052ade360bc", + "css": "spin", + "code": 59449, + "src": "fontelico" + }, + { + "uid": "bbfb51903f40597f0b70fd75bc7b5cac", + "css": "trash", + "code": 61944, + "src": "fontawesome" + }, + { + "uid": "d73eceadda1f594cec0536087539afbf", + "css": "heart", + "code": 59397, + "src": "fontawesome" + }, + { + "uid": "cce5e05853d0798a4d10077ef613387c", + "css": "blind", + "code": 62109, + "src": "fontawesome" + }, + { + "uid": "567e3e257f2cc8fba2c12bf691c9f2d8", + "css": "moon", + "code": 61830, + "src": "fontawesome" + }, + { + "uid": "aa035df0908c4665c269b7b09a5596f3", + "css": "sun", + "code": 61829, + "src": "fontawesome" + }, + { + "uid": "c5fd349cbd3d23e4ade333789c29c729", + "css": "eye", + "code": 59398, + "src": "fontawesome" + }, + { + "uid": "d870630ff8f81e6de3958ecaeac532f2", + "css": "left-open", + "code": 59399, + "src": "fontawesome" + }, + { + "uid": "399ef63b1e23ab1b761dfbb5591fa4da", + "css": "right-open", + "code": 59400, + "src": "fontawesome" + }, + { + "uid": "895405dfac8a3b7b2f23b183c6608ee6", + "css": "export", + "code": 59401, + "src": "fontawesome" + }, + { + "uid": "4aad6bb50b02c18508aae9cbe14e784e", + "css": "share", + "code": 61920, + "src": "fontawesome" + }, + { + "uid": "a73c5deb486c8d66249811642e5d719a", + "css": "refresh", + "code": 59402, + "src": "fontawesome" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 49c965d..2ee4492 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "knacss": "^8.0.4", "mongoose": "^6.2.1", "mongoose-unique-validator": "^3.0.0", + "nodemailer": "^6.7.8", "npm-run-all": "^4.1.5", "passport": "^0.5.2", "passport-custom": "^1.1.1", @@ -66,6 +67,7 @@ "passport-local": "^1.0.0", "rimraf": "^3.0.2", "sass": "^1.49.7", + "svg-captcha": "^1.4.0", "uuid": "^8.3.2", "vue": "^3.2.31" }, diff --git a/public/font/icon.eot b/public/font/icon.eot index a17c0f6..424368f 100644 Binary files a/public/font/icon.eot and b/public/font/icon.eot differ diff --git a/public/font/icon.svg b/public/font/icon.svg index d5ac2f6..52ad914 100644 --- a/public/font/icon.svg +++ b/public/font/icon.svg @@ -26,6 +26,8 @@ + + diff --git a/public/font/icon.ttf b/public/font/icon.ttf index 4ccfa6d..6688a79 100644 Binary files a/public/font/icon.ttf and b/public/font/icon.ttf differ diff --git a/public/font/icon.woff b/public/font/icon.woff index 3262198..9543cf5 100644 Binary files a/public/font/icon.woff and b/public/font/icon.woff differ diff --git a/public/font/icon.woff2 b/public/font/icon.woff2 index f73baf4..c2acb70 100644 Binary files a/public/font/icon.woff2 and b/public/font/icon.woff2 differ diff --git a/public/img/loading-dark.gif b/public/img/loading-dark.gif new file mode 100644 index 0000000..54c888b Binary files /dev/null and b/public/img/loading-dark.gif differ diff --git a/public/img/loading-light.gif b/public/img/loading-light.gif new file mode 100644 index 0000000..66472af Binary files /dev/null and b/public/img/loading-light.gif differ diff --git a/sass/colors.scss b/sass/colors.scss index 441062c..83b5325 100644 --- a/sass/colors.scss +++ b/sass/colors.scss @@ -56,6 +56,8 @@ $pagination-hover-color: rgb(115, 151, 186); --button-link-text-color: #2C364A; + --loader-img: url('/img/loading-light.gif'); + --nord0: #{$nord0}; --nord1: #{$nord1}; --nord2: #{$nord2}; @@ -94,4 +96,6 @@ $pagination-hover-color: rgb(115, 151, 186); --border-color: #{$nord1}; --button-link-text-color: #{$white}; + + --loader-img: url('/img/loading-dark.gif'); } \ No newline at end of file diff --git a/sass/icons.scss b/sass/icons.scss index 4250307..7b2150b 100644 --- a/sass/icons.scss +++ b/sass/icons.scss @@ -1,11 +1,11 @@ @font-face { font-family: 'icon'; - src: url('/font/icon.eot?80770511'); - src: url('/font/icon.eot?80770511#iefix') format('embedded-opentype'), - url('/font/icon.woff2?80770511') format('woff2'), - url('/font/icon.woff?80770511') format('woff'), - url('/font/icon.ttf?80770511') format('truetype'), - url('/font/icon.svg?80770511#icon') format('svg'); + src: url('/font/icon.eot?41426785'); + src: url('/font/icon.eot?41426785#iefix') format('embedded-opentype'), + url('/font/icon.woff2?41426785') format('woff2'), + url('/font/icon.woff?41426785') format('woff'), + url('/font/icon.ttf?41426785') format('truetype'), + url('/font/icon.svg?41426785#icon') format('svg'); font-weight: normal; font-style: normal; } @@ -42,6 +42,7 @@ .icon-left-open:before { content: '\e807'; } /* '' */ .icon-right-open:before { content: '\e808'; } /* '' */ .icon-export:before { content: '\e809'; } /* '' */ +.icon-refresh:before { content: '\e80a'; } /* '' */ .icon-spin:before { content: '\e839'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ .icon-sun:before { content: '\f185'; } /* '' */ diff --git a/sass/index.scss b/sass/index.scss index 373b302..1979bb1 100644 --- a/sass/index.scss +++ b/sass/index.scss @@ -39,6 +39,7 @@ @import './icons'; @import './list'; @import './box'; +@import './loader'; @import './error'; @import './500'; diff --git a/sass/loader.scss b/sass/loader.scss new file mode 100644 index 0000000..a2ecace --- /dev/null +++ b/sass/loader.scss @@ -0,0 +1,13 @@ +.loader { + display: flex; + flex-direction: column; + align-items: center; + + .animation { + background-image: var(--loader-img); + background-repeat: no-repeat; + background-position: center center; + width: 64px; + height: 64px; + } +} \ No newline at end of file diff --git a/sass/ma-collection-details.scss b/sass/ma-collection-details.scss index 952a026..00a8946 100644 --- a/sass/ma-collection-details.scss +++ b/sass/ma-collection-details.scss @@ -1,4 +1,26 @@ .ma-collection-details { + h1 { + i { + cursor: pointer; + + &.icon-trash { + color: $danger-color; + @include transition() {} + + &:hover { + color: $danger-color-hl; + } + } + &.icon-refresh { + color: $primary-color; + @include transition() {} + + &:hover { + color: $primary-color-hl; + } + } + } + } .galerie { display: flex; flex-wrap: wrap; diff --git a/sass/navbar.scss b/sass/navbar.scss index f9583b9..fdca308 100644 --- a/sass/navbar.scss +++ b/sass/navbar.scss @@ -54,7 +54,7 @@ position: relative; width: 3.25rem; margin-left: auto; - color: rgba(0,0,0,.7); + color: var(--font-color); @include respond-to("medium-up") { display: none; diff --git a/src/app.js b/src/app.js index 85650e8..7c40a27 100644 --- a/src/app.js +++ b/src/app.js @@ -23,6 +23,7 @@ import importJobsRouter from "./routes/jobs"; import importAlbumRouterApiV1 from "./routes/api/v1/albums"; import importSearchRouterApiV1 from "./routes/api/v1/search"; import importMeRouterApiV1 from "./routes/api/v1/me"; +import importContactRouterApiV1 from "./routes/api/v1/contact"; passportConfig(passport); @@ -91,6 +92,7 @@ app.use("/jobs", importJobsRouter); app.use("/api/v1/albums", importAlbumRouterApiV1); app.use("/api/v1/search", importSearchRouterApiV1); app.use("/api/v1/me", importMeRouterApiV1); +app.use("/api/v1/contact", importContactRouterApiV1); // Handle 404 app.use((req, res) => { diff --git a/src/config/index.js b/src/config/index.js index 7906240..fa02df5 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -19,4 +19,14 @@ module.exports = { process.env.JOBS_HEADER_VALUE || "ooYee9xok7eigo2shiePohyoGh1eepew", registrationOpen: (process.env.REGISTRATION_OPEN || "true").toLowerCase() === "true", + mailMethod: process.env.MAIL_METHOD || "formspree", + smtpConfig: { + host: process.env.MAIL_HOST, + port: process.env.MAIL_PORT, + auth: { + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASSWORD, + }, + }, + mailTo: process.env.MAIL_TO, }; diff --git a/src/helpers/index.js b/src/helpers/index.js index 128f594..3a81cc7 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -5,13 +5,25 @@ import { discogsToken } from "../config"; export const getBaseUrl = (req) => `${req.protocol}://${req.get("host")}`; -export const searchSong = async (q) => { +export const searchSong = async (q, format, year, country) => { const dis = new Discogs({ userToken: discogsToken }).database(); - const res = await dis.search({ + const params = { q, type: "release", - }); + }; + + if (format) { + params.format = format; + } + if (year) { + params.year = year; + } + if (country) { + params.country = country; + } + + const res = await dis.search(params); return res; }; diff --git a/src/middleware/Albums.js b/src/middleware/Albums.js index 312675c..3ca4558 100644 --- a/src/middleware/Albums.js +++ b/src/middleware/Albums.js @@ -7,7 +7,8 @@ import AlbumsModel from "../models/albums"; import JobsModel from "../models/jobs"; import UsersModel from "../models/users"; import ErrorEvent from "../libs/error"; -// import { uploadFromUrl } from "../libs/aws"; + +import { getAlbumDetails } from "../helpers"; /** * Classe permettant la gestion des albums d'un utilisateur @@ -182,6 +183,34 @@ class Albums extends Pages { } } + /** + * Méthode permettant de mettre à jour un album + * + * @return {Object} + */ + async patchOne() { + const { itemId: _id } = this.req.params; + const { _id: User } = this.req.user; + const album = await AlbumsModel.findOne({ + _id, + User, + }); + + if (!album) { + throw new ErrorEvent( + 404, + "Mise à jour", + "Impossible de trouver cet album" + ); + } + + const values = await getAlbumDetails(album.discogsId); + + await album.updateOne(values); + + return album; + } + /** * Méthode permettant de supprimer un élément d'une collection * @return {Boolean} @@ -196,7 +225,11 @@ class Albums extends Pages { return true; } - throw new ErrorEvent(404, "Impossible de trouver cet album"); + throw new ErrorEvent( + 404, + "Suppression", + "Impossible de trouver cet album" + ); } /** diff --git a/src/middleware/Jobs.js b/src/middleware/Jobs.js index ccb2cf9..91964f2 100644 --- a/src/middleware/Jobs.js +++ b/src/middleware/Jobs.js @@ -18,7 +18,7 @@ class Jobs { throw new ErrorEvent( 404, "Item non trouvé", - `L'album avant l'id ${itemId} n'existe plus dans la collection` + `L'album avec l'id ${itemId} n'existe plus dans la collection` ); } diff --git a/src/routes/api/v1/albums.js b/src/routes/api/v1/albums.js index e1caa1f..596b502 100644 --- a/src/routes/api/v1/albums.js +++ b/src/routes/api/v1/albums.js @@ -47,6 +47,16 @@ router router .route("/:itemId") + .patch(ensureLoggedIn("/connexion"), async (req, res, next) => { + try { + const albums = new Albums(req); + const data = await albums.patchOne(); + + sendResponse(req, res, data); + } catch (err) { + next(err); + } + }) .delete(ensureLoggedIn("/connexion"), async (req, res, next) => { try { const albums = new Albums(req); diff --git a/src/routes/api/v1/contact.js b/src/routes/api/v1/contact.js new file mode 100644 index 0000000..4ecce22 --- /dev/null +++ b/src/routes/api/v1/contact.js @@ -0,0 +1,77 @@ +import express from "express"; +import nodemailer from "nodemailer"; +import svgCaptcha from "svg-captcha"; + +import { sendResponse } from "../../../libs/format"; + +import { mailMethod, smtpConfig, mailTo, siteName } from "../../../config"; +import ErrorEvent from "../../../libs/error"; + +// eslint-disable-next-line new-cap +const router = express.Router(); + +router + .route("/") + .get(async (req, res, next) => { + try { + const captcha = svgCaptcha.create({ + size: 4, + noise: 2, + color: true, + }); + req.session.captcha = captcha.text; + + res.type("svg"); + return res.status(200).send(captcha.data); + } catch (err) { + return next(err); + } + }) + .post(async (req, res, next) => { + try { + if (mailMethod === "smtp") { + const { email, name, message, captcha } = req.body; + + if (!captcha || captcha !== req.session.captcha) { + throw new ErrorEvent( + 406, + "Captcha", + "Le captcha n'est pas valide" + ); + } + + if (!email || !message) { + throw new ErrorEvent( + 406, + "Erreur de saisie", + "Le formulaire n'est pas correctement saisi" + ); + } + + const transporter = nodemailer.createTransport(smtpConfig); + + const text = `Bonjour, + Vous venez de recevoir un nouveau message de ${name} (${email}) : + + ${message} + `; + + const data = await transporter.sendMail({ + from: smtpConfig.auth.user, + to: mailTo, + subject: `${siteName} : Nouveau message`, + text, + }); + + const { messageId, response } = data; + + return sendResponse(req, res, { messageId, response }); + } + + throw new ErrorEvent(500, "Routeur", "Méthode non configurée"); + } catch (err) { + return next(err); + } + }); + +export default router; diff --git a/src/routes/api/v1/search.js b/src/routes/api/v1/search.js index 76e1c7e..267ec0f 100644 --- a/src/routes/api/v1/search.js +++ b/src/routes/api/v1/search.js @@ -9,7 +9,12 @@ const router = express.Router(); router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => { try { - const data = await searchSong(req.query.q); + const data = await searchSong( + req.query.q, + req.query.format || null, + req.query.year || null, + req.query.country || null + ); sendResponse(req, res, data); } catch (err) { diff --git a/views/pages/ajouter-un-album.ejs b/views/pages/ajouter-un-album.ejs index 2cd4ab4..549140e 100644 --- a/views/pages/ajouter-un-album.ejs +++ b/views/pages/ajouter-un-album.ejs @@ -1,19 +1,37 @@

Ajouter un album

-
-
-
+ +
+
- +
- +
+
+ + +
+
+ + +
+
+ + +
+
+
-
+ +
@@ -158,10 +176,77 @@ data() { return { q: '', + year: '', + country: '', + format: '', loading: false, items: [], details: {}, modalIsVisible: false, + formats: [ + 'Vinyl', + 'Acetate', + 'Flexi-disc', + 'Lathe Cut', + 'Mighty Tiny', + 'Shellac', + 'Sopic', + 'Pathé Disc', + 'Edison Disc', + 'Cylinder', + 'CD', + 'CDr', + 'CDV', + 'DVD', + 'DVDr', + 'HD DVD', + 'HD DVD-R', + 'Blu-ray', + 'Blu-ray-R', + 'Ultra HD Blu-ray', + 'SACD', + '4-Track Cartridge', + '8-Track Cartridge', + 'Cassette', + 'DC-International', + 'Elcaset', + 'PlayTape', + 'RCA Tape Cartridge', + 'DAT', + 'DCC', + 'Microcassette', + 'NT Cassette', + 'Pocket Rocker', + 'Revere Magnetic Stereo Tape Ca', + 'Tefifon', + 'Reel-To-Reel', + 'Sabamobil', + 'Betacam', + 'Betacam SP', + 'Betamax', + 'Cartrivision', + 'MiniDV', + 'Super VHS', + 'U-matic', + 'VHS', + 'Video 2000', + 'Video8', + 'Film Reel', + 'HitClips', + 'Laserdisc', + 'SelectaVision', + 'VHD', + 'Wire Recording', + 'Minidisc', + 'MVD', + 'UMD', + 'Floppy Disk', + 'File', + 'Memory Stick', + 'Hybrid', + 'All Media', + 'Box Set', + ] } }, methods: { @@ -173,8 +258,19 @@ } this.loading = true; + let url = `/api/v1/search?q=${this.q}`; - axios.get(`/api/v1/search?q=${this.q}`) + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.country ) { + url += `&country=${this.country}`; + } + if ( this.format ) { + url += `&format=${this.format}`; + } + + axios.get(url) .then( response => { const { results, @@ -242,6 +338,9 @@ showToastr(err.response?.data?.message || "Impossible d'ajouter cet album pour le moment…"); }); }, + orderedItems(items) { + return items.sort(); + } } }).mount('#app'); diff --git a/views/pages/collection.ejs b/views/pages/collection.ejs index 8ba62cd..a558cd7 100644 --- a/views/pages/collection.ejs +++ b/views/pages/collection.ejs @@ -85,6 +85,12 @@
+
+
+
+ Chargement des données en cours… +
+
{{ item.artists_sort}} - {{ item.title }} @@ -172,8 +178,55 @@ methods: { fetch() { this.loading = true; + this.total = 0; + + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const entries = urlParams.entries(); + + for(const entry of entries) { + switch(entry[0]) { + case 'artists_sort': + this.artist = entry[1]; + break; + default: + this[entry[0]] = entry[1]; + } + } let url = `/api/v1/albums?userId=${this.userId}&page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if ( this.artist ) { + url += `&artists_sort=${this.artist}`; + } + if ( this.format ) { + url += `&format=${this.format}`; + } + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.genre ) { + url += `&genre=${this.genre}`; + } + if ( this.style ) { + url += `&style=${this.style}`; + } + + axios.get(url) + .then( response => { + this.items = response.data.rows; + this.total = response.data.count || 0; + this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0); + + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de charger cette collection"); + }) + .finally(() => { + this.loading = false; + }); + }, + changeUrl() { + let url = `?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; if ( this.artist ) { url += `&artists_sort=${this.artist.replace('&', '%26')}`; } @@ -190,38 +243,26 @@ url += `&style=${this.style.replace('&', '%26')}`; } - axios.get(url) - .then( response => { - this.items = response.data.rows; - this.total = response.data.count; - this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0); - - }) - .catch((err) => { - showToastr(err.response?.data?.message || "Impossible de charger cette collection"); - }) - .finally(() => { - this.loading = false; - }); + location.href = url; }, next(event) { event.preventDefault(); this.page += 1; - this.fetch(); + this.changeUrl(); }, previous(event) { event.preventDefault(); this.page -= 1; - this.fetch(); + this.changeUrl(); }, goTo(page) { this.page = page; - this.fetch(); + this.changeUrl(); }, changeSort() { const [sort,order] = this.sortOrder.split('-'); @@ -229,12 +270,12 @@ this.order = order; this.page = 1; - this.fetch(); + this.changeUrl(); }, changeFilter() { this.page = 1; - this.fetch(); + this.changeUrl(); }, showMoreFilters() { this.moreFilters = !this.moreFilters; diff --git a/views/pages/composants.ejs b/views/pages/composants.ejs index 1cba4e5..cdd1776 100644 --- a/views/pages/composants.ejs +++ b/views/pages/composants.ejs @@ -355,6 +355,7 @@ .icon-left-open .icon-right-open .icon-export + .icon-refresh .icon-share .icon-spin .icon-sun diff --git a/views/pages/mon-compte/ma-collection/details.ejs b/views/pages/mon-compte/ma-collection/details.ejs index bd97f90..41c0b24 100644 --- a/views/pages/mon-compte/ma-collection/details.ejs +++ b/views/pages/mon-compte/ma-collection/details.ejs @@ -1,6 +1,10 @@
-

{{item.artists_sort}} - {{item.title}}

+

+ {{item.artists_sort}} - {{item.title}} + + +

@@ -70,6 +74,9 @@
  • {{format.name}} +