From 1d59ee3b7136321af40218b5529abedc4730da88 Mon Sep 17 00:00:00 2001 From: Damien Broqua Date: Sun, 30 Oct 2022 21:48:49 +0100 Subject: [PATCH] Version 1.4 (#67) Co-authored-by: dbroqua Reviewed-on: https://git.darkou.fr/dbroqua/MusicTopus/pulls/67 --- .eslintrc.js | 73 ++++--- .gitignore | 2 +- gulpfile.js | 46 ++++ javascripts/ajouter-un-album.js | 179 ++++++++++++++++ javascripts/collection.js | 141 ++++++++++++ javascripts/conctact.js | 43 ++++ javascripts/main.js | 151 +++++++++++++ javascripts/mon-compte/index.js | 29 +++ .../mon-compte/ma-collection/details.js | 186 ++++++++++++++++ .../mon-compte/ma-collection/exporter.js | 16 ++ javascripts/mon-compte/ma-collection/index.js | 201 ++++++++++++++++++ package.json | 11 +- public/js/main.js | 138 ------------ src/app.js | 8 - views/index.ejs | 5 +- views/pages/ajouter-un-album.ejs | 178 +--------------- views/pages/collection.ejs | 139 +----------- views/pages/mon-compte/index.ejs | 28 +-- .../mon-compte/ma-collection/details.ejs | 167 +-------------- .../mon-compte/ma-collection/exporter.ejs | 25 +-- .../pages/mon-compte/ma-collection/index.ejs | 186 +--------------- views/pages/nous-contacter.ejs | 49 +---- 22 files changed, 1067 insertions(+), 934 deletions(-) create mode 100644 gulpfile.js create mode 100644 javascripts/ajouter-un-album.js create mode 100644 javascripts/collection.js create mode 100644 javascripts/conctact.js create mode 100644 javascripts/main.js create mode 100644 javascripts/mon-compte/index.js create mode 100644 javascripts/mon-compte/ma-collection/details.js create mode 100644 javascripts/mon-compte/ma-collection/exporter.js create mode 100644 javascripts/mon-compte/ma-collection/index.js delete mode 100644 public/js/main.js diff --git a/.eslintrc.js b/.eslintrc.js index 143bf10..1f95ab5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,36 +1,43 @@ module.exports = { - env: { - browser: true, - es2020: true, - node: true, - jquery: true, - }, - extends: ['airbnb-base', 'prettier'], - plugins: ['prettier'], - parserOptions: { - ecmaVersion: 11, - sourceType: 'module', - }, - rules: { - 'prettier/prettier': ['error'], - 'no-underscore-dangle': [ - 'error', - { - allow: ['_id', 'artists_sort', 'type_'], - }, - ], - 'camelcase': [ - 'error', - { - allow: ['artists_sort',] - }, - ], - }, - ignorePatterns: ['public/libs/**/*.js', 'public/js/main.js', 'dist/**'], - overrides: [ - { - files: ['**/*.js'], - excludedFiles: '*.ejs', + env: { + browser: true, + es2020: true, + node: true, + jquery: true, + }, + extends: ["airbnb-base", "prettier"], + plugins: ["prettier"], + parserOptions: { + ecmaVersion: 11, + sourceType: "module", + }, + rules: { + "prettier/prettier": ["error"], + "no-underscore-dangle": [ + "error", + { + allow: ["_id", "artists_sort", "type_"], + }, + ], + camelcase: [ + "error", + { + allow: ["artists_sort"], + }, + ], + }, + ignorePatterns: ["public/libs/**/*.js", "public/js/main.js", "dist/**"], + overrides: [ + { + files: ["**/*.js"], + excludedFiles: "*.ejs", + }, + ], + globals: { + Vue: true, + axios: true, + showToastr: true, + protocol: true, + host: true, }, - ], }; diff --git a/.gitignore b/.gitignore index a8dcba6..3054b59 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,6 @@ dist dist yarn.lock public/css -public/css +public/js docker-compose.yml dump diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..8e76f38 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,46 @@ +const { parallel, src, dest } = require("gulp"); + +const sourcemaps = require("gulp-sourcemaps"); +const concat = require("gulp-concat"); + +const gulp = require("gulp"); +const uglify = require("gulp-uglify"); +const babel = require("gulp-babel"); + +const sourceJs = "javascripts/**/*.js"; +const sourceRemoteJS = [ + "./node_modules/vue/dist/vue.global.prod.js", + "./node_modules/axios/dist/axios.min.js", +]; + +const destination = "public/js"; + +// TASKS ---------------------------------------------------------------------- + +const compileJs = function () { + return gulp + .src(sourceJs) + .pipe(sourcemaps.init()) + .pipe(concat("main.js")) + .pipe( + babel({ + presets: ["@babel/env"], + }) + ) + .pipe(uglify()) + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest(destination)); +}; +const compileRemoteJs = function () { + return gulp + .src(sourceRemoteJS) + .pipe(sourcemaps.init()) + .pipe(concat("libs.js")) + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest(destination)); +}; +// ---------------------------------------------------------------------------- + +// COMMANDS ------------------------------------------------------------------- +exports.default = parallel(compileJs, compileRemoteJs); +// ---------------------------------------------------------------------------- diff --git a/javascripts/ajouter-un-album.js b/javascripts/ajouter-un-album.js new file mode 100644 index 0000000..eb55697 --- /dev/null +++ b/javascripts/ajouter-un-album.js @@ -0,0 +1,179 @@ +Vue.createApp({ + 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: { + search(event) { + event.preventDefault(); + + if (this.loading) { + return false; + } + + this.loading = true; + let url = `/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}`; + } + + return axios + .get(url) + .then((response) => { + const { results } = response.data; + const items = []; + + for (let i = 0; i < results.length; i += 1) { + const { + id, + title, + thumb, + year, + country, + format, + genre, + style, + } = results[i]; + items.push({ + id, + title, + thumb, + year, + country, + format, + genre, + style, + }); + } + + this.items = items; + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Aucun résultat trouvé :/" + ); + }) + .finally(() => { + this.loading = false; + }); + }, + toggleModal() { + this.modalIsVisible = !this.modalIsVisible; + }, + loadDetails(discogsId) { + axios + .get(`/api/v1/search/${discogsId}`) + .then((response) => { + const { data } = response; + + this.details = data; + this.toggleModal(); + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de charger les détails de cet album" + ); + }) + .finally(() => { + this.loading = false; + }); + }, + add() { + axios + .post("/api/v1/albums", this.details) + .then(() => { + window.location.href = "/ma-collection"; + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible d'ajouter cet album pour le moment…" + ); + }); + }, + orderedItems(items) { + return items.sort(); + }, + }, +}).mount("#ajouter-album"); diff --git a/javascripts/collection.js b/javascripts/collection.js new file mode 100644 index 0000000..beed2c6 --- /dev/null +++ b/javascripts/collection.js @@ -0,0 +1,141 @@ +if (typeof userId !== "undefined") { + Vue.createApp({ + data() { + return { + loading: false, + moreFilters: false, + items: [], + total: 0, + page: 1, + totalPages: 1, + limit: 16, + artist: "", + format: "", + year: "", + genre: "", + style: "", + sortOrder: "artists_sort-asc", + sort: "artists_sort", + order: "asc", + // eslint-disable-next-line no-undef + userId, + }; + }, + created() { + this.fetch(); + }, + methods: { + fetch() { + this.loading = true; + this.total = 0; + + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const entries = urlParams.entries(); + + // eslint-disable-next-line no-restricted-syntax + for (const entry of entries) { + const [key, value] = entry; + switch (key) { + case "artists_sort": + this.artist = value; + break; + default: + this[key] = value; + } + } + + 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.replace("&", "%26")}`; + } + if (this.format) { + url += `&format=${this.format.replace("&", "%26")}`; + } + if (this.year) { + url += `&year=${this.year}`; + } + if (this.genre) { + url += `&genre=${this.genre.replace("&", "%26")}`; + } + if (this.style) { + url += `&style=${this.style.replace("&", "%26")}`; + } + + axios + .get(url) + .then((response) => { + this.items = response.data.rows; + this.total = response.data.count || 0; + this.totalPages = + parseInt(response.data.count / this.limit, 10) + + (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")}`; + } + if (this.format) { + url += `&format=${this.format.replace("&", "%26")}`; + } + if (this.year) { + url += `&year=${this.year}`; + } + if (this.genre) { + url += `&genre=${this.genre.replace("&", "%26")}`; + } + if (this.style) { + url += `&style=${this.style.replace("&", "%26")}`; + } + + window.location.href = url; + }, + next(event) { + event.preventDefault(); + + this.page += 1; + + this.changeUrl(); + }, + previous(event) { + event.preventDefault(); + + this.page -= 1; + + this.changeUrl(); + }, + goTo(page) { + this.page = page; + + this.changeUrl(); + }, + changeSort() { + const [sort, order] = this.sortOrder.split("-"); + this.sort = sort; + this.order = order; + this.page = 1; + + this.changeUrl(); + }, + changeFilter() { + this.page = 1; + + this.changeUrl(); + }, + showMoreFilters() { + this.moreFilters = !this.moreFilters; + }, + }, + }).mount("#collection-publique"); +} diff --git a/javascripts/conctact.js b/javascripts/conctact.js new file mode 100644 index 0000000..1fbde34 --- /dev/null +++ b/javascripts/conctact.js @@ -0,0 +1,43 @@ +// eslint-disable-next-line no-undef +if (typeof contactMethod !== "undefined" && contactMethod === "smtp") { + Vue.createApp({ + data() { + return { + email: "", + name: "", + message: "", + captcha: "", + loading: false, + }; + }, + methods: { + send(event) { + event.preventDefault(); + + if (this.loading) { + return false; + } + + this.loading = true; + + const { email, message, name, captcha } = this; + + return axios + .post("/api/v1/contact", { email, name, message, captcha }) + .then(() => { + showToastr("Message correctement envoyé", true); + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible d'envoyer votre message", + false + ); + }) + .finally(() => { + this.loading = false; + }); + }, + }, + }).mount("#contact"); +} diff --git a/javascripts/main.js b/javascripts/main.js new file mode 100644 index 0000000..388ab22 --- /dev/null +++ b/javascripts/main.js @@ -0,0 +1,151 @@ +/* eslint-disable no-unused-vars */ +const { protocol, host } = window.location; + +/** + * Fonction permettant d'afficher un message dans un toastr + * @param {String} message + */ +function showToastr(message, success = false) { + const x = document.getElementById("toastr"); + if (message) { + x.getElementsByTagName("SPAN")[0].innerHTML = message; + } + + x.className = `${x.className} show`.replace("sucess", ""); + if (success) { + x.className = `${x.className} success`; + } + setTimeout(() => { + x.className = x.className.replace("show", ""); + }, 3000); +} + +/** + * Fonction permettant de masquer le toastr + */ +function hideToastr() { + const x = document.getElementById("toastr"); + + x.className = x.className.replace("show", ""); + x.getElementsByTagName("SPAN")[0].innerHTML = ""; +} + +/** + * Fonction permettant de récupérer la valeur d'un cookie + * @param {String} cname + * @param {String} defaultValue + * + * @return {String} + */ +function getCookie(cname, defaultValue = "false") { + const name = `${cname}=`; + const decodedCookie = decodeURIComponent(document.cookie); + const ca = decodedCookie.split(";"); + for (let i = 0; i < ca.length; i += 1) { + let c = ca[i]; + while (c.charAt(0) === " ") { + c = c.substring(1); + } + if (c.indexOf(name) === 0) { + return c.substring(name.length, c.length); + } + } + return defaultValue; +} + +/** + * Fonction permettant de créer un cookie + * @param {String} cname + * @param {String} cvalue + * @param {Number} exdays + */ +function setCookie(cname, cvalue, exdays = 30) { + const d = new Date(); + d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000); + const expires = `expires=${d.toUTCString()}`; + document.cookie = `${cname}=${cvalue};${expires};path=/`; +} + +/** + * Fonction de (dé)charger le thème accessible + * @param {String} value + */ +function setAriaTheme(value) { + const { body } = document; + if (value === "true") { + const classesString = body.className || ""; + if (classesString.indexOf("is-accessible") === -1) { + body.classList.add("is-accessible"); + } + } else { + body.classList.remove("is-accessible"); + } +} + +/** + * Fonction de (dé)charger le thème accessible + */ +function switchAriaTheme() { + const { body } = document; + + body.classList.toggle("is-accessible"); + + setCookie("ariatheme", body.classList.contains("is-accessible")); +} + +/** + * Fonction permettant de switcher de thème clair/sombre + * @param {Object} e + */ +function switchTheme(e) { + const theme = e.target.checked ? "dark" : "light"; + + document.documentElement.setAttribute("data-theme", theme); + setCookie("theme", theme); +} + +/** + * Ensemble d'actions effectuées au chargement de la page + */ +document.addEventListener("DOMContentLoaded", () => { + const $navbarBurgers = Array.prototype.slice.call( + document.querySelectorAll(".navbar-burger"), + 0 + ); + if ($navbarBurgers.length > 0) { + $navbarBurgers.forEach((el) => { + el.addEventListener("click", () => { + const { target } = el.dataset; + const $target = document.getElementById(target); + + el.classList.toggle("is-active"); + $target.classList.toggle("is-active"); + }); + }); + } + + const switchAriaThemeBtn = document.querySelector("#switchAriaTheme"); + if (switchAriaThemeBtn) { + switchAriaThemeBtn.addEventListener("click", switchAriaTheme); + } + setAriaTheme(getCookie("ariatheme")); + + const toggleSwitch = document.querySelector( + '.theme-switch input[type="checkbox"]' + ); + if (toggleSwitch) { + toggleSwitch.addEventListener("change", switchTheme, false); + } + + let currentThemeIsDark = getCookie("theme"); + if (currentThemeIsDark === "false" && window.matchMedia) { + currentThemeIsDark = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + } + switchTheme({ target: { checked: currentThemeIsDark === "dark" } }); + if (toggleSwitch) { + toggleSwitch.checked = currentThemeIsDark === "dark"; + } +}); diff --git a/javascripts/mon-compte/index.js b/javascripts/mon-compte/index.js new file mode 100644 index 0000000..ed40f03 --- /dev/null +++ b/javascripts/mon-compte/index.js @@ -0,0 +1,29 @@ +if (typeof email !== "undefined" && typeof username !== "undefined") { + Vue.createApp({ + data() { + return { + // eslint-disable-next-line no-undef + email, + // eslint-disable-next-line no-undef + username, + oldPassword: "", + password: "", + passwordConfirm: "", + loading: false, + }; + }, + methods: { + // eslint-disable-next-line no-unused-vars + async updateProfil(event) { + // try { + // if ( this.password !== this.passwordConfirm ) { + // throw "La confirnation du mot de passe ne correspond pas"; + // } + // } catch(err) { + // event.preventDefault(); + // showToastr(err); + // } + }, + }, + }).mount("#mon-compte"); +} diff --git a/javascripts/mon-compte/ma-collection/details.js b/javascripts/mon-compte/ma-collection/details.js new file mode 100644 index 0000000..fe8d079 --- /dev/null +++ b/javascripts/mon-compte/ma-collection/details.js @@ -0,0 +1,186 @@ +if (typeof item !== "undefined") { + Vue.createApp({ + data() { + return { + // eslint-disable-next-line no-undef + item, + tracklist: [], + identifiers: [], + modalIsVisible: false, + identifiersMode: "preview", + identifiersPreviewLength: 16, + preview: null, + index: null, + showModalDelete: false, + }; + }, + created() { + this.setTrackList(); + this.setIdentifiers(); + + window.addEventListener("keydown", this.changeImage); + }, + destroyed() { + window.removeEventListener("keydown", this.changeImage); + }, + methods: { + setIdentifiers() { + this.identifiers = []; + + const max = + this.identifiersMode === "preview" && + this.item.identifiers.length > this.identifiersPreviewLength + ? this.identifiersPreviewLength + : this.item.identifiers.length; + + for (let i = 0; i < max; i += 1) { + this.identifiers.push(this.item.identifiers[i]); + } + }, + setTrackList() { + let subTrack = { + type: null, + title: null, + tracks: [], + }; + for (let i = 0; i < this.item.tracklist.length; i += 1) { + const { type_, title, position, duration, extraartists } = + this.item.tracklist[i]; + + if (type_ === "heading") { + if (subTrack.type) { + this.tracklist.push(subTrack); + subTrack = { + type: null, + title: null, + tracks: [], + }; + } + + subTrack.type = type_; + subTrack.title = title; + } else { + subTrack.tracks.push({ + title, + position, + duration, + extraartists, + }); + } + } + this.tracklist.push(subTrack); + }, + setImage() { + this.preview = this.item.images[this.index].uri; + }, + showGallery(event) { + const item = + event.target.tagName === "IMG" + ? event.target.parentElement + : event.target; + + const { index } = item.dataset; + + this.index = Number(index); + this.modalIsVisible = true; + + this.setImage(); + }, + toggleModal() { + this.modalIsVisible = !this.modalIsVisible; + }, + previous() { + this.index = + this.index > 0 + ? this.index - 1 + : this.item.images.length - 1; + this.setImage(); + }, + next() { + this.index = + this.index + 1 === this.item.images.length + ? 0 + : this.index + 1; + this.setImage(); + }, + changeImage(event) { + const direction = event.code; + + if ( + this.modalIsVisible && + ["ArrowRight", "ArrowLeft", "Escape"].indexOf(direction) !== + -1 + ) { + switch (direction) { + case "ArrowRight": + return this.next(); + case "ArrowLeft": + return this.previous(); + default: + this.modalIsVisible = false; + return true; + } + } + + return true; + }, + showAllIdentifiers() { + this.identifiersMode = "all"; + this.setIdentifiers(); + }, + showLessIdentifiers() { + this.identifiersMode = "preview"; + this.setIdentifiers(); + + document + .querySelector("#identifiers") + .scrollIntoView({ behavior: "smooth" }); + }, + showConfirmDelete() { + this.toggleModalDelete(); + }, + toggleModalDelete() { + this.showModalDelete = !this.showModalDelete; + }, + updateItem() { + showToastr("Mise à jour en cours…", true); + axios + .patch(`/api/v1/albums/${this.item._id}`) + .then((res) => { + showToastr("Mise à jour réalisée avec succès", true); + this.item = res.data; + + this.setTrackList(); + this.setIdentifiers(); + this.showLessIdentifiers(); + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de mettre à jour cet album", + false + ); + }); + }, + deleteItem() { + axios + .delete(`/api/v1/albums/${this.item._id}`) + .then(() => { + window.location.href = "/ma-collection"; + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de supprimer cet album" + ); + }) + .finally(() => { + this.toggleModalDelete(); + }); + }, + goToArtist() { + return ""; + }, + }, + }).mount("#ma-collection-details"); +} diff --git a/javascripts/mon-compte/ma-collection/exporter.js b/javascripts/mon-compte/ma-collection/exporter.js new file mode 100644 index 0000000..b391436 --- /dev/null +++ b/javascripts/mon-compte/ma-collection/exporter.js @@ -0,0 +1,16 @@ +Vue.createApp({ + data() { + return { + format: "xml", + }; + }, + created() {}, + destroyed() {}, + methods: { + exportCollection(event) { + event.preventDefault(); + + window.open(`/api/v1/albums?exportFormat=${this.format}`, "_blank"); + }, + }, +}).mount("#exporter"); diff --git a/javascripts/mon-compte/ma-collection/index.js b/javascripts/mon-compte/ma-collection/index.js new file mode 100644 index 0000000..967e3f2 --- /dev/null +++ b/javascripts/mon-compte/ma-collection/index.js @@ -0,0 +1,201 @@ +if (typeof isPublicCollection !== "undefined") { + Vue.createApp({ + data() { + return { + loading: false, + moreFilters: false, + items: [], + total: 0, + page: 1, + totalPages: 1, + limit: 16, + artist: "", + format: "", + year: "", + genre: "", + style: "", + sortOrder: "artists_sort-asc", + sort: "artists_sort", + order: "asc", + itemId: null, + showModalDelete: false, + showModalShare: false, + shareLink: `${protocol}//${host}/collection/<%= user._id %>`, + // eslint-disable-next-line no-undef + isPublicCollection, + }; + }, + created() { + this.fetch(); + }, + methods: { + fetch() { + this.loading = true; + this.total = 0; + + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const entries = urlParams.entries(); + + // eslint-disable-next-line no-restricted-syntax + for (const entry of entries) { + const [key, value] = entry; + switch (key) { + case "artists_sort": + this.artist = value; + break; + default: + this[key] = value; + } + } + + let url = `/api/v1/albums?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if (this.artist) { + url += `&artists_sort=${this.artist.replace("&", "%26")}`; + } + if (this.format) { + url += `&format=${this.format.replace("&", "%26")}`; + } + if (this.year) { + url += `&year=${this.year}`; + } + if (this.genre) { + url += `&genre=${this.genre.replace("&", "%26")}`; + } + if (this.style) { + url += `&style=${this.style.replace("&", "%26")}`; + } + + axios + .get(url) + .then((response) => { + this.items = response.data.rows; + this.total = response.data.count || 0; + this.totalPages = + parseInt(response.data.count / this.limit, 10) + + (response.data.count % this.limit > 0 ? 1 : 0); + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de charger votre 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")}`; + } + if (this.format) { + url += `&format=${this.format.replace("&", "%26")}`; + } + if (this.year) { + url += `&year=${this.year}`; + } + if (this.genre) { + url += `&genre=${this.genre.replace("&", "%26")}`; + } + if (this.style) { + url += `&style=${this.style.replace("&", "%26")}`; + } + + window.location.href = url; + }, + next(event) { + event.preventDefault(); + + this.page += 1; + + this.changeUrl(); + }, + previous(event) { + event.preventDefault(); + + this.page -= 1; + + this.changeUrl(); + }, + goTo(page) { + this.page = page; + + this.changeUrl(); + }, + changeSort() { + const [sort, order] = this.sortOrder.split("-"); + this.sort = sort; + this.order = order; + this.page = 1; + + this.changeUrl(); + }, + changeFilter() { + this.page = 1; + + this.changeUrl(); + }, + showMoreFilters() { + this.moreFilters = !this.moreFilters; + }, + toggleModal() { + this.showModalDelete = !this.showModalDelete; + }, + toggleModalShare() { + this.showModalShare = !this.showModalShare; + }, + showConfirmDelete(itemId) { + this.itemId = itemId; + this.toggleModal(); + }, + deleteItem() { + axios + .delete(`/api/v1/albums/${this.itemId}`) + .then(() => { + this.fetch(); + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de supprimer cet album" + ); + }) + .finally(() => { + this.toggleModal(); + }); + }, + shareCollection() { + axios + .patch(`/api/v1/me`, { + isPublicCollection: !this.isPublicCollection, + }) + .then((res) => { + this.isPublicCollection = res.data.isPublicCollection; + + if (this.isPublicCollection) { + showToastr( + "Votre collection est désormais publique", + true + ); + } else { + showToastr( + "Votre collection n'est plus partagée", + true + ); + } + }) + .catch((err) => { + showToastr( + err.response?.data?.message || + "Impossible de supprimer cet album" + ); + }) + .finally(() => { + this.toggleModalShare(); + }); + }, + }, + }).mount("#ma-collection"); +} diff --git a/package.json b/package.json index 2ee4492..b3dffed 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "description": "Simple application to manage your CD/Vinyl collection", "scripts": { "start": "node ./dist/bin/www", - "run:all": "npm-run-all build sass start", + "run:all": "npm-run-all build sass uglify start", "watch": "nodemon -e js,scss", "sass": "npx sass sass/index.scss public/css/main.css -s compressed --color", + "uglify": "npx gulp", "prebuild": "rimraf dist", "build": "babel ./src --out-dir dist --copy-files", "test": "jest", @@ -55,6 +56,11 @@ "excel4node": "^1.7.2", "express": "^4.17.2", "express-session": "^1.17.2", + "gulp": "^4.0.2", + "gulp-babel": "^8.0.0", + "gulp-concat": "^2.6.1", + "gulp-sourcemaps": "^3.0.0", + "gulp-uglify": "^3.0.2", "joi": "^17.6.0", "knacss": "^8.0.4", "mongoose": "^6.2.1", @@ -75,7 +81,8 @@ "exec": "yarn run:all", "watch": [ "src/*", - "sass/*" + "sass/*", + "javascripts/*" ], "ignore": [ "**/__tests__/**", diff --git a/public/js/main.js b/public/js/main.js deleted file mode 100644 index f203a63..0000000 --- a/public/js/main.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Fonction permettant d'afficher un message dans un toastr - * @param {String} message - */ - function showToastr(message, success = false) { - let x = document.getElementById("toastr"); - if ( message ) { - x.getElementsByTagName("SPAN")[0].innerHTML = message; - } - - x.className = `${x.className} show`.replace("sucess", ""); - if ( success ) { - x.className = `${x.className} success`; - } - setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000); -}; - -/** - * Fonction permettant de masquer le toastr - */ -function hideToastr() { - let x = document.getElementById("toastr"); - - x.className = x.className.replace("show", ""); - x.getElementsByTagName("SPAN")[0].innerHTML = ""; -} - -/** - * Fonction permettant de récupérer la valeur d'un cookie - * @param {String} cname - * @param {String} defaultValue - * - * @return {String} - */ -function getCookie(cname, defaultValue = 'false') { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for(let i = 0; i < ca.length; i+=1) { - let c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return defaultValue; -} - -/** - * Fonction permettant de créer un cookie - * @param {String} cname - * @param {String} cvalue - * @param {Number} exdays - */ -function setCookie(cname, cvalue, exdays = 30) { - const d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); - let expires = "expires="+ d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; -} - -/** - * Fonction de (dé)charger le thème accessible - * @param {String} value - */ -function setAriaTheme(value) { - let body = document.body; - if ( value === 'true' ) { - let classesString = body.className || ""; - if (classesString.indexOf("is-accessible") === -1) { - body.classList.add("is-accessible"); - } - } else { - body.classList.remove("is-accessible"); - } -} - -/** - * Fonction de (dé)charger le thème accessible - */ -function switchAriaTheme() { - let body = document.body; - - body.classList.toggle("is-accessible"); - - setCookie('ariatheme', body.classList.contains("is-accessible")); -} - -/** - * Fonction permettant de switcher de thème clair/sombre - * @param {Object} e - */ -function switchTheme(e) { - const theme = e.target.checked ? 'dark' : 'light'; - - document.documentElement.setAttribute('data-theme', theme); - setCookie('theme', theme); -} - -/** - * Ensemble d'actions effectuées au chargement de la page - */ -document.addEventListener('DOMContentLoaded', () => { - const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); - if ($navbarBurgers.length > 0) { - $navbarBurgers.forEach( el => { - el.addEventListener('click', () => { - const target = el.dataset.target; - const $target = document.getElementById(target); - - el.classList.toggle('is-active'); - $target.classList.toggle('is-active'); - }); - }); - } - - const switchAriaThemeBtn = document.querySelector("#switchAriaTheme"); - if ( switchAriaThemeBtn ) { - switchAriaThemeBtn.addEventListener("click", switchAriaTheme); - } - setAriaTheme(getCookie('ariatheme')); - - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); - if ( toggleSwitch ) { - toggleSwitch.addEventListener('change', switchTheme, false); - } - - let currentThemeIsDark = getCookie('theme'); - if ( currentThemeIsDark === 'false' && window.matchMedia ) { - currentThemeIsDark = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - } - switchTheme({target: {checked: currentThemeIsDark === 'dark'}}); - if ( toggleSwitch) { - toggleSwitch.checked = currentThemeIsDark === 'dark'; - } -}); \ No newline at end of file diff --git a/src/app.js b/src/app.js index 7c40a27..6fa4688 100644 --- a/src/app.js +++ b/src/app.js @@ -75,14 +75,6 @@ app.set("views", path.join(__dirname, "../views")); app.set("view engine", "ejs"); app.use(express.static(path.join(__dirname, "../public"))); -app.use( - "/libs/vue", - express.static(path.join(__dirname, "../node_modules/vue/dist")) -); -app.use( - "/libs/axios", - express.static(path.join(__dirname, "../node_modules/axios/dist")) -); app.use("/", indexRouter); app.use("/mon-compte", monCompteRouter); diff --git a/views/index.ejs b/views/index.ejs index 2386a83..a310984 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -16,9 +16,8 @@ - - - + + <% if ( config.matomoUrl ) { %> diff --git a/views/pages/ajouter-un-album.ejs b/views/pages/ajouter-un-album.ejs index a7306ff..f32ea67 100644 --- a/views/pages/ajouter-un-album.ejs +++ b/views/pages/ajouter-un-album.ejs @@ -1,4 +1,4 @@ -
+

Ajouter un album

@@ -169,178 +169,4 @@
-
- - +
\ No newline at end of file diff --git a/views/pages/collection.ejs b/views/pages/collection.ejs index 9960983..3272d8b 100644 --- a/views/pages/collection.ejs +++ b/views/pages/collection.ejs @@ -1,4 +1,4 @@ -
+

Collection de <%= page.username %>

@@ -146,140 +146,5 @@
diff --git a/views/pages/mon-compte/index.ejs b/views/pages/mon-compte/index.ejs index d8f7f6a..af7b634 100644 --- a/views/pages/mon-compte/index.ejs +++ b/views/pages/mon-compte/index.ejs @@ -1,4 +1,4 @@ -
+

Mon compte

@@ -72,28 +72,6 @@
diff --git a/views/pages/mon-compte/ma-collection/details.ejs b/views/pages/mon-compte/ma-collection/details.ejs index 41c0b24..475ee79 100644 --- a/views/pages/mon-compte/ma-collection/details.ejs +++ b/views/pages/mon-compte/ma-collection/details.ejs @@ -1,7 +1,7 @@ -
+

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

@@ -120,14 +120,13 @@ - +
@@ -170,164 +169,12 @@
- +
\ No newline at end of file diff --git a/views/pages/mon-compte/ma-collection/exporter.ejs b/views/pages/mon-compte/ma-collection/exporter.ejs index 13e3f98..28cc559 100644 --- a/views/pages/mon-compte/ma-collection/exporter.ejs +++ b/views/pages/mon-compte/ma-collection/exporter.ejs @@ -1,4 +1,4 @@ -
+

Exporter ma collection

Les formats CSV et Excel sont facilement lisiblent par un humain. Dans ces 2 formats vous trouverez seulement les informations principales de vos albums, à savoir : @@ -44,25 +44,4 @@ Exporter -

- - \ No newline at end of file +
\ No newline at end of file diff --git a/views/pages/mon-compte/ma-collection/index.ejs b/views/pages/mon-compte/ma-collection/index.ejs index fb67cd3..38993c8 100644 --- a/views/pages/mon-compte/ma-collection/index.ejs +++ b/views/pages/mon-compte/ma-collection/index.ejs @@ -1,4 +1,4 @@ -
+

Ma collection @@ -196,185 +196,5 @@

+ const isPublicCollection = <%= user.isPublicCollection ? 'true' : 'false' %>; + \ No newline at end of file diff --git a/views/pages/nous-contacter.ejs b/views/pages/nous-contacter.ejs index 276b6f4..f179135 100644 --- a/views/pages/nous-contacter.ejs +++ b/views/pages/nous-contacter.ejs @@ -1,4 +1,4 @@ -
+

Nous contacter

id="contact" method="POST" action="https://formspree.io/f/<%= config.formspreeId %>" <% } %>>
@@ -34,47 +34,6 @@
-<% if (config.mailMethod === 'smtp' ) { %> - -<% } %> \ No newline at end of file + \ No newline at end of file