Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
bed5139a27 | |||
5b2758afca | |||
68414e3e71 | |||
d692090022 | |||
061e72c459 | |||
bf2e9be3b7 | |||
d4e6d23459 | |||
0ea6a21b90 | |||
6b2f7b61cb | |||
f1220fc05a | |||
8d22435b90 |
|
@ -6,6 +6,12 @@
|
|||
"units_per_em": 1000,
|
||||
"ascent": 850,
|
||||
"glyphs": [
|
||||
{
|
||||
"uid": "ca90da02d2c6a3183f2458e4dc416285",
|
||||
"css": "adjust",
|
||||
"code": 59408,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
||||
"css": "plus",
|
||||
|
|
|
@ -10,6 +10,7 @@ const babel = require("gulp-babel");
|
|||
const sourceJs = "javascripts/**/*.js";
|
||||
const sourceRemoteJS = [
|
||||
"./node_modules/vue/dist/vue.global.prod.js",
|
||||
"./node_modules/chart.js/dist/chart.umd.js",
|
||||
"./node_modules/axios/dist/axios.min.js",
|
||||
];
|
||||
|
||||
|
|
|
@ -78,6 +78,12 @@ Vue.createApp({
|
|||
],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
window.addEventListener("keydown", this.keyDown);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener("keydown", this.keyDown);
|
||||
},
|
||||
methods: {
|
||||
search(event) {
|
||||
event.preventDefault();
|
||||
|
@ -189,5 +195,13 @@ Vue.createApp({
|
|||
orderedItems(items) {
|
||||
return items.sort();
|
||||
},
|
||||
keyDown(event) {
|
||||
const keycode = event.code;
|
||||
|
||||
if (this.modalIsVisible && keycode === "Escape") {
|
||||
event.preventDefault();
|
||||
this.modalIsVisible = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount("#ajouter-album");
|
||||
|
|
|
@ -7,8 +7,8 @@ Vue.createApp({
|
|||
total: 0,
|
||||
// eslint-disable-next-line no-undef
|
||||
page: query.page || 1,
|
||||
totalPages: 1,
|
||||
limit: 16,
|
||||
totalPages: 1,
|
||||
artist: "",
|
||||
format: "",
|
||||
year: "",
|
||||
|
@ -34,6 +34,11 @@ Vue.createApp({
|
|||
},
|
||||
created() {
|
||||
this.fetch();
|
||||
|
||||
window.addEventListener("keydown", this.keyDown);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener("keydown", this.keyDown);
|
||||
},
|
||||
methods: {
|
||||
formatParams(param) {
|
||||
|
@ -69,7 +74,7 @@ Vue.createApp({
|
|||
|
||||
this.sortOrder = `${sortOrder.sort}-${sortOrder.order}`;
|
||||
|
||||
let url = `/api/v1/albums?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`;
|
||||
let url = `/api/v1/albums?page=${this.page}&sort=${this.sort}&order=${this.order}`;
|
||||
if (this.artist) {
|
||||
url += `&artist=${this.formatParams(this.artist)}`;
|
||||
}
|
||||
|
@ -94,6 +99,7 @@ Vue.createApp({
|
|||
.get(url)
|
||||
.then((response) => {
|
||||
this.items = response.data.rows;
|
||||
this.limit = response.data.limit;
|
||||
this.total = response.data.count || 0;
|
||||
this.totalPages =
|
||||
parseInt(response.data.count / this.limit, 10) +
|
||||
|
@ -240,5 +246,16 @@ Vue.createApp({
|
|||
|
||||
return render;
|
||||
},
|
||||
keyDown(event) {
|
||||
const keycode = event.code;
|
||||
if (this.showModalDelete && keycode === "Escape") {
|
||||
event.preventDefault();
|
||||
this.showModalDelete = false;
|
||||
}
|
||||
if (this.showModalShare && keycode === "Escape") {
|
||||
event.preventDefault();
|
||||
this.showModalShare = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount("#collection");
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
if (typeof email !== "undefined" && typeof username !== "undefined") {
|
||||
Vue.createApp({
|
||||
data() {
|
||||
|
@ -12,6 +11,8 @@ if (typeof email !== "undefined" && typeof username !== "undefined") {
|
|||
password: "",
|
||||
passwordConfirm: "",
|
||||
// eslint-disable-next-line no-undef
|
||||
pagination,
|
||||
// eslint-disable-next-line no-undef
|
||||
mastodon: mastodon || {
|
||||
publish: false,
|
||||
url: "",
|
||||
|
@ -57,7 +58,7 @@ if (typeof email !== "undefined" && typeof username !== "undefined") {
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
async updateProfil() {
|
||||
this.errors = [];
|
||||
const { oldPassword, password, passwordConfirm, mastodon } =
|
||||
const { oldPassword, password, passwordConfirm, mastodon, pagination } =
|
||||
this.formData;
|
||||
|
||||
if (password && !oldPassword) {
|
||||
|
@ -83,6 +84,8 @@ if (typeof email !== "undefined" && typeof username !== "undefined") {
|
|||
data.oldPassword = oldPassword;
|
||||
}
|
||||
|
||||
data.pagination = pagination;
|
||||
|
||||
try {
|
||||
await axios.patch(`/api/v1/me`, data);
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ if (typeof item !== "undefined") {
|
|||
this.setTrackList();
|
||||
this.setIdentifiers();
|
||||
|
||||
window.addEventListener("keydown", this.changeImage);
|
||||
window.addEventListener("keydown", this.keyDown);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener("keydown", this.changeImage);
|
||||
window.removeEventListener("keydown", this.keyDown);
|
||||
},
|
||||
watch: {
|
||||
shareMessage(message) {
|
||||
|
@ -40,6 +40,8 @@ if (typeof item !== "undefined") {
|
|||
this.shareMessageTransformed = message
|
||||
.replaceAll("{artist}", this.item.artists[0].name)
|
||||
.replaceAll("{format}", this.item.formats[0].name)
|
||||
.replaceAll("{genres}", this.item.genres.join())
|
||||
.replaceAll("{styles}", this.item.styles.join())
|
||||
.replaceAll("{year}", this.item.year)
|
||||
.replaceAll("{video}", video)
|
||||
.replaceAll("{album}", this.item.title);
|
||||
|
@ -139,10 +141,10 @@ if (typeof item !== "undefined") {
|
|||
this.setImage();
|
||||
},
|
||||
changeImage(event) {
|
||||
event.preventDefault();
|
||||
const direction = event.code;
|
||||
|
||||
if (
|
||||
this.modalIsVisible &&
|
||||
["ArrowRight", "ArrowLeft", "Escape"].indexOf(direction) !==
|
||||
-1
|
||||
) {
|
||||
|
@ -159,6 +161,20 @@ if (typeof item !== "undefined") {
|
|||
|
||||
return true;
|
||||
},
|
||||
keyDown(event) {
|
||||
const keycode = event.code;
|
||||
if (this.modalIsVisible) {
|
||||
this.changeImage(event);
|
||||
}
|
||||
if (this.showModalDelete && keycode === "Escape") {
|
||||
event.preventDefault();
|
||||
this.showModalDelete = false;
|
||||
}
|
||||
if (this.showModalShare && keycode === "Escape") {
|
||||
event.preventDefault();
|
||||
this.showModalShare = false;
|
||||
}
|
||||
},
|
||||
showAllIdentifiers() {
|
||||
this.identifiersMode = "all";
|
||||
this.setIdentifiers();
|
||||
|
|
|
@ -1,19 +1,3 @@
|
|||
/**
|
||||
* Fonction permettant de sauvegarder dans le stockage local le choix du thème
|
||||
* @param {String} scheme
|
||||
*/
|
||||
function saveColorScheme(scheme) {
|
||||
localStorage.setItem("theme", scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction permettant de changer le thème du site
|
||||
* @param {String} scheme
|
||||
*/
|
||||
function setColorScheme(scheme) {
|
||||
document.documentElement.setAttribute("data-theme", scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction permettant de récupérer le thème du système
|
||||
* @return {String}
|
||||
|
@ -28,11 +12,57 @@ function getPreferredColorScheme() {
|
|||
return "light";
|
||||
}
|
||||
|
||||
// INFO: On place un event sur le bouton
|
||||
const toggleSwitch = document.querySelector(
|
||||
'.theme-switch input[type="checkbox"]'
|
||||
/**
|
||||
* @param {String} scheme
|
||||
*/
|
||||
function setPictoOnMenu(scheme) {
|
||||
document.querySelectorAll(".icon-theme").forEach((item) => {
|
||||
item.classList.add("hidden");
|
||||
});
|
||||
document
|
||||
.querySelector(`.icon-theme.theme-${scheme}`)
|
||||
.classList.remove("hidden");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction permettant de sauvegarder dans le stockage local le choix du thème
|
||||
* @param {String} scheme
|
||||
*/
|
||||
function saveColorScheme(scheme) {
|
||||
localStorage.setItem("theme", scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction permettant de changer le thème du site
|
||||
* @param {String} scheme
|
||||
*/
|
||||
function setColorScheme(scheme) {
|
||||
document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
scheme === "system" ? getPreferredColorScheme() : scheme
|
||||
);
|
||||
|
||||
setPictoOnMenu(scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction déclenchée lorsqu'un utilisateur clique sur un bouton dans le menu déroulant
|
||||
* @param {Object} e
|
||||
*/
|
||||
function changeTheme(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const scheme = this.dataset.value;
|
||||
|
||||
saveColorScheme(scheme);
|
||||
setColorScheme(scheme);
|
||||
}
|
||||
|
||||
// INFO: On place un event sur le bouton
|
||||
const buttonsTheme = document.getElementsByClassName("theme");
|
||||
// INFO: On récupère du local storage (ou des préférences navigateur) le thème actuel
|
||||
const currentTheme = localStorage.getItem("theme") || getPreferredColorScheme();
|
||||
|
||||
/**
|
||||
* Event permettant de détecter les changements de thème du système
|
||||
*/
|
||||
|
@ -44,28 +74,14 @@ if (window.matchMedia) {
|
|||
if (selectedColorScheme === "system") {
|
||||
const preferedColorScheme = getPreferredColorScheme();
|
||||
setColorScheme(preferedColorScheme);
|
||||
|
||||
toggleSwitch.checked = preferedColorScheme === "dark";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const currentTheme = localStorage.getItem("theme") || getPreferredColorScheme();
|
||||
|
||||
// INFO: Au chargement de la page on détecte le thème à charger
|
||||
setColorScheme(currentTheme);
|
||||
|
||||
toggleSwitch.checked = currentTheme === "dark";
|
||||
|
||||
toggleSwitch.addEventListener(
|
||||
"change",
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const scheme = e.target.checked ? "dark" : "light";
|
||||
|
||||
saveColorScheme(scheme);
|
||||
setColorScheme(scheme);
|
||||
},
|
||||
false
|
||||
);
|
||||
// INFO: On place un event au click sur chacun des boutons du menu
|
||||
for (let i = 0; i < buttonsTheme.length; i += 1) {
|
||||
buttonsTheme[i].addEventListener("click", changeTheme, false);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"@babel/core": "^7.17.2",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"axios": "^0.26.0",
|
||||
"chart.js": "^4.4.1",
|
||||
"connect-ensure-login": "^0.1.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-mongo": "^4.6.0",
|
||||
|
@ -5257,6 +5258,11 @@
|
|||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz",
|
||||
|
@ -7913,6 +7919,17 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
|
||||
"integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
"@babel/core": "^7.17.2",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"axios": "^0.26.0",
|
||||
"chart.js": "^4.4.1",
|
||||
"connect-ensure-login": "^0.1.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-mongo": "^4.6.0",
|
||||
|
|
Binary file not shown.
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2022 by original authors @ fontello.com</metadata>
|
||||
<metadata>Copyright (C) 2024 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="icon" horiz-adv-x="1000" >
|
||||
<font-face font-family="icon" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
|
@ -28,6 +28,8 @@
|
|||
|
||||
<glyph glyph-name="refresh" unicode="" d="M843 261q0-3 0-4-36-150-150-243t-267-93q-81 0-157 31t-136 88l-72-72q-11-11-25-11t-25 11-11 25v250q0 14 11 25t25 11h250q14 0 25-11t10-25-10-25l-77-77q40-36 90-57t105-20q74 0 139 37t104 99q6 10 30 66 4 13 16 13h107q8 0 13-6t5-12z m14 446v-250q0-14-10-25t-26-11h-250q-14 0-25 11t-10 25 10 25l77 77q-82 77-194 77-75 0-140-37t-104-99q-6-10-29-66-5-13-17-13h-111q-7 0-13 6t-5 12v4q36 150 151 243t268 93q81 0 158-31t137-88l72 72q11 11 25 11t26-11 10-25z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="adjust" unicode="" d="M429 46v608q-83 0-153-41t-110-111-41-152 41-152 110-111 153-41z m428 304q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="spin" unicode="" d="M855 9c-189-190-520-172-705 13-190 190-200 494-28 695 11 13 21 26 35 34 36 23 85 18 117-13 30-31 35-76 16-112-5-9-9-15-16-22-140-151-145-379-8-516 153-153 407-121 542 34 106 122 142 297 77 451-83 198-305 291-510 222l0 1c236 82 492-24 588-252 71-167 37-355-72-493-11-15-23-29-36-42z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||
|
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -5,16 +5,20 @@
|
|||
color: var(--font-color);
|
||||
display: block;
|
||||
padding: 1.25rem;
|
||||
width: calc(100% - 2rem);
|
||||
margin: auto;
|
||||
|
||||
@include transition() {}
|
||||
|
||||
&.mini {
|
||||
margin: auto;
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
@include respond-to("small-up") {
|
||||
width: 65%;
|
||||
}
|
||||
@include respond-to("medium-up") {
|
||||
width: 35%;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
|
|
|
@ -45,6 +45,7 @@ $close-background-dark: rgba(240,240,240,.6);
|
|||
--bg-color: #{darken($white, 5%)};
|
||||
--bg-alternate-color: #{darken($white, 8%)};
|
||||
--font-color: #{$nord3};
|
||||
--hover-font-color: #{lighten($nord3, 16%)};
|
||||
--footer-color: #{$darken-white};
|
||||
--link-color: #{$nord1};
|
||||
|
||||
|
@ -88,6 +89,7 @@ $close-background-dark: rgba(240,240,240,.6);
|
|||
--bg-color: #{lighten($nord0, 2%)};
|
||||
--bg-alternate-color: #{lighten($nord3, 8%)};
|
||||
--font-color: #{$nord6};
|
||||
--hover-font-color: #{darken($nord6, 16%)};
|
||||
--footer-color: #{$nord1};
|
||||
--link-color: #{$nord4};
|
||||
|
||||
|
|
|
@ -71,77 +71,3 @@
|
|||
padding-right: 2.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
em {
|
||||
margin-left: 10px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-switch {
|
||||
display: inline-block;
|
||||
height: 34px;
|
||||
position: relative;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.theme-switch input {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.slider {
|
||||
background-color: #ccc;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: .4s;
|
||||
@include transition() {}
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
background-color: #fff;
|
||||
bottom: 4px;
|
||||
content: '\f185';
|
||||
height: 26px;
|
||||
left: 4px;
|
||||
position: absolute;
|
||||
transition: .4s;
|
||||
width: 26px;
|
||||
padding: 0;
|
||||
|
||||
font-family: "icon";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
text-align: center;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: $primary-color;
|
||||
@include transition() {}
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
content: '\f186';
|
||||
background-color: var(--input-active-color);
|
||||
@include transition() {}
|
||||
}
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
|
|
@ -1,35 +1,57 @@
|
|||
@font-face {
|
||||
font-family: 'icon';
|
||||
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');
|
||||
src: url('/font/icon.eot?15219908');
|
||||
src: url('/font/icon.eot?15219908#iefix') format('embedded-opentype'),
|
||||
url('/font/icon.woff2?15219908') format('woff2'),
|
||||
url('/font/icon.woff?15219908') format('woff'),
|
||||
url('/font/icon.ttf?15219908') format('truetype'),
|
||||
url('/font/icon.svg?15219908#icon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
/*
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'icon';
|
||||
src: url('../font/icon.svg?15219908#icon') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "icon";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: never;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em;
|
||||
|
||||
/* Animation center compensation - margins should be symmetric */
|
||||
/* remove if not needed */
|
||||
margin-left: .2em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
|
||||
/* Font smoothing. That was taken from TWBS */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Uncomment for 3D effect */
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-plus:before { content: '\e800'; } /* '' */
|
||||
|
@ -43,6 +65,7 @@
|
|||
.icon-right-open:before { content: '\e808'; } /* '' */
|
||||
.icon-export:before { content: '\e809'; } /* '' */
|
||||
.icon-refresh:before { content: '\e80a'; } /* '' */
|
||||
.icon-adjust:before { content: '\e810'; } /* '' */
|
||||
.icon-spin:before { content: '\e839'; } /* '' */
|
||||
.icon-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.icon-sun:before { content: '\f185'; } /* '' */
|
||||
|
@ -51,16 +74,3 @@
|
|||
.icon-trash:before { content: '\f1f8'; } /* '' */
|
||||
.icon-blind:before { content: '\f29d'; } /* '' */
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 2s infinite linear;
|
||||
display: inline-block;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
@import './list';
|
||||
@import './box';
|
||||
@import './loader';
|
||||
@import './table';
|
||||
|
||||
@import './error';
|
||||
@import './messages.scss';
|
||||
|
|
|
@ -117,7 +117,6 @@
|
|||
@include respond-to("medium-up") {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
color: rgba(0,0,0,.7);
|
||||
|
||||
.navbar-dropdown {
|
||||
background-color: var(--default-color);
|
||||
|
@ -127,7 +126,6 @@
|
|||
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
||||
display: none;
|
||||
font-size: .875rem;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
|
@ -138,7 +136,7 @@
|
|||
|
||||
.navbar-link {
|
||||
background-color: var(--default-hl-color);
|
||||
color: rgba(0,0,0,.7);
|
||||
color: var(--hover-font-color);
|
||||
}
|
||||
|
||||
.navbar-dropdown {
|
||||
|
@ -167,15 +165,6 @@
|
|||
@include respond-to("medium-up") {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border: 3px solid transparent;
|
||||
|
@ -196,11 +185,22 @@
|
|||
right: 1.125em;
|
||||
|
||||
@include respond-to("medium-up") {
|
||||
border-color: rgba(0,0,0,.7);
|
||||
border-color: var(--font-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.navbar-menu {
|
||||
display: none;
|
||||
background-color: var(--default-color);
|
||||
|
@ -260,7 +260,7 @@
|
|||
background-color: var(--font-color);
|
||||
border: none;
|
||||
height: 2px;
|
||||
margin: .5rem 0;
|
||||
margin: .5rem 0 0 1.5rem;
|
||||
}
|
||||
|
||||
.navbar-item {
|
||||
|
@ -277,11 +277,15 @@
|
|||
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
||||
display: none;
|
||||
font-size: .875rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-width: 100%;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
|
||||
hr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.navbar-item {
|
||||
white-space: nowrap;
|
||||
padding: .375rem 1rem;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
table {
|
||||
th,
|
||||
td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
tr {
|
||||
border-bottom: 2px solid var(--font-color);
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
tr {
|
||||
background-color: var(--default-color);
|
||||
|
||||
&:nth-child(2n) {
|
||||
background-color: var(--bg-alternate-color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import MongoStore from "connect-mongo";
|
|||
|
||||
import passportConfig from "./libs/passport";
|
||||
|
||||
import config, { env, mongoDbUri, secret } from "./config";
|
||||
import config, { env, mongoDbUri, port, secret } from "./config";
|
||||
|
||||
import { isXhr } from "./helpers";
|
||||
|
||||
|
@ -150,4 +150,6 @@ app.use((error, req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
console.log(`Server listening on port ${port}!`);
|
||||
|
||||
export default app;
|
||||
|
|
|
@ -83,6 +83,8 @@ class Albums extends Pages {
|
|||
)
|
||||
.replaceAll("{artist}", data.artists[0].name)
|
||||
.replaceAll("{format}", data.formats[0].name)
|
||||
.replaceAll("{genres}", data.genres.join())
|
||||
.replaceAll("{styles}", data.styles.join())
|
||||
.replaceAll("{year}", data.year)
|
||||
.replaceAll("{video}", video)
|
||||
.replaceAll("{album}", data.title)}
|
||||
|
@ -162,6 +164,22 @@ Publié automatiquement via #musictopus`;
|
|||
return distincts;
|
||||
}
|
||||
|
||||
constructor(req, viewname) {
|
||||
super(req, viewname);
|
||||
|
||||
this.colors = [
|
||||
"#2e3440",
|
||||
"#d8dee9",
|
||||
"#8fbcbb",
|
||||
"#5e81ac",
|
||||
"#d08770",
|
||||
"#bf616a",
|
||||
"#ebcb8b",
|
||||
"#a3be8c",
|
||||
"#b48ead",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant de récupérer la liste des albums d'une collection
|
||||
* @return {Object}
|
||||
|
@ -169,7 +187,6 @@ Publié automatiquement via #musictopus`;
|
|||
async getAll() {
|
||||
const {
|
||||
page,
|
||||
limit,
|
||||
exportFormat = "json",
|
||||
sort = "artists_sort",
|
||||
order = "asc",
|
||||
|
@ -183,6 +200,8 @@ Publié automatiquement via #musictopus`;
|
|||
discogsId,
|
||||
} = this.req.query;
|
||||
|
||||
const limit = this.req.user?.pagination || 16;
|
||||
|
||||
let userId = this.req.user?._id;
|
||||
|
||||
const where = {};
|
||||
|
@ -280,6 +299,7 @@ Publié automatiquement via #musictopus`;
|
|||
default:
|
||||
return {
|
||||
rows,
|
||||
limit,
|
||||
count,
|
||||
};
|
||||
}
|
||||
|
@ -500,6 +520,139 @@ Publié automatiquement via #musictopus`;
|
|||
await this.loadItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant d'afficher des statistiques au sujet de ma collection
|
||||
*/
|
||||
async statistics() {
|
||||
const { _id: User } = this.req.user;
|
||||
const top = {};
|
||||
const byGenres = {};
|
||||
const byStyles = {};
|
||||
const byFormats = {};
|
||||
const top10 = [];
|
||||
let byStyles10 = [];
|
||||
const max = this.colors.length - 1;
|
||||
|
||||
const colorsCount = this.colors.length;
|
||||
|
||||
const albums = await AlbumsModel.find({
|
||||
User,
|
||||
artists: { $exists: true, $not: { $size: 0 } },
|
||||
});
|
||||
|
||||
for (let i = 0; i < albums.length; i += 1) {
|
||||
const currentFormats = [];
|
||||
const { artists, genres, styles, formats } = albums[i];
|
||||
|
||||
// INFO: On regroupe les artistes par nom pour en faire le top10
|
||||
for (let j = 0; j < artists.length; j += 1) {
|
||||
const { name } = artists[j];
|
||||
if (!top[name]) {
|
||||
top[name] = {
|
||||
name,
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
top[name].count += 1;
|
||||
}
|
||||
|
||||
// INFO: On regroupe les genres
|
||||
for (let j = 0; j < genres.length; j += 1) {
|
||||
const name = genres[j];
|
||||
if (!byGenres[name]) {
|
||||
byGenres[name] = {
|
||||
name,
|
||||
count: 0,
|
||||
color: this.colors[
|
||||
Object.keys(byGenres).length % colorsCount
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
byGenres[name].count += 1;
|
||||
}
|
||||
|
||||
// INFO: On regroupe les styles
|
||||
for (let j = 0; j < styles.length; j += 1) {
|
||||
const name = styles[j];
|
||||
if (!byStyles[name]) {
|
||||
byStyles[name] = {
|
||||
name,
|
||||
count: 0,
|
||||
color: this.colors[
|
||||
Object.keys(byStyles).length % colorsCount
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
byStyles[name].count += 1;
|
||||
}
|
||||
|
||||
// INFO: On regroupe les formats
|
||||
for (let j = 0; j < formats.length; j += 1) {
|
||||
const { name } = formats[j];
|
||||
// INFO: On évite qu'un album avec 2 vinyles soit compté 2x
|
||||
if (!currentFormats.includes(name)) {
|
||||
if (!byFormats[name]) {
|
||||
byFormats[name] = {
|
||||
name,
|
||||
count: 0,
|
||||
color: this.colors[
|
||||
Object.keys(byFormats).length % colorsCount
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
byFormats[name].count += 1;
|
||||
currentFormats.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INFO: On convertit le top en tableau
|
||||
Object.keys(top).forEach((index) => {
|
||||
top10.push(top[index]);
|
||||
});
|
||||
|
||||
// INFO: On convertit les styles en tableau
|
||||
Object.keys(byStyles).forEach((index) => {
|
||||
byStyles10.push(byStyles[index]);
|
||||
});
|
||||
|
||||
// INFO: On ordonne les artistes par quantité d'albums
|
||||
top10.sort((a, b) => (a.count > b.count ? -1 : 1));
|
||||
|
||||
// INFO: On ordonne les styles par quantité
|
||||
byStyles10.sort((a, b) => (a.count > b.count ? -1 : 1));
|
||||
const tmp = [];
|
||||
|
||||
// INFO: On recupère le top N des styles et on mets le reste dans le label "autre"
|
||||
for (let i = 0; i < byStyles10.length; i += 1) {
|
||||
if (i < max) {
|
||||
tmp.push({
|
||||
...byStyles10[i],
|
||||
color: this.colors[max - i],
|
||||
});
|
||||
} else if (i === max) {
|
||||
tmp.push({
|
||||
name: "Autre",
|
||||
count: 0,
|
||||
color: this.colors[0],
|
||||
});
|
||||
tmp[max].count += byStyles10[i].count;
|
||||
} else {
|
||||
tmp[max].count += byStyles10[i].count;
|
||||
}
|
||||
}
|
||||
byStyles10 = tmp;
|
||||
|
||||
this.setPageTitle("Mes statistiques");
|
||||
this.setPageContent("top10", top10.splice(0, 10));
|
||||
this.setPageContent("byGenres", byGenres);
|
||||
this.setPageContent("byStyles", byStyles10);
|
||||
this.setPageContent("byFormats", byFormats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant de créer la page "collection/:userId"
|
||||
*/
|
||||
|
@ -522,8 +675,8 @@ Publié automatiquement via #musictopus`;
|
|||
const genres = await Albums.getAllDistincts("genres", userId);
|
||||
const styles = await Albums.getAllDistincts("styles", userId);
|
||||
|
||||
this.setPageContent("username", user.username);
|
||||
this.setPageTitle(`Collection publique de ${user.username}`);
|
||||
this.setPageContent("username", user.username);
|
||||
this.setPageContent("artists", artists);
|
||||
this.setPageContent("formats", formats);
|
||||
this.setPageContent("years", years);
|
||||
|
|
|
@ -16,6 +16,7 @@ class Me extends Pages {
|
|||
const { _id } = this.req.user;
|
||||
|
||||
const schema = Joi.object({
|
||||
pagination: Joi.number(),
|
||||
isPublicCollection: Joi.boolean(),
|
||||
oldPassword: Joi.string(),
|
||||
password: Joi.string(),
|
||||
|
@ -45,6 +46,10 @@ class Me extends Pages {
|
|||
user.salt = value.password;
|
||||
}
|
||||
|
||||
if (value.pagination) {
|
||||
user.pagination = value.pagination;
|
||||
}
|
||||
|
||||
if (value.isPublicCollection !== undefined) {
|
||||
user.isPublicCollection = value.isPublicCollection;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ const UserSchema = new mongoose.Schema(
|
|||
},
|
||||
hash: String,
|
||||
salt: String,
|
||||
pagination: {
|
||||
type: Number,
|
||||
default: 16,
|
||||
},
|
||||
isPublicCollection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
|
@ -38,6 +38,23 @@ router
|
|||
}
|
||||
});
|
||||
|
||||
router
|
||||
.route("/statistiques")
|
||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||
try {
|
||||
const page = new Albums(
|
||||
req,
|
||||
"mon-compte/ma-collection/statistiques"
|
||||
);
|
||||
|
||||
await page.statistics();
|
||||
|
||||
render(res, page);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router
|
||||
.route("/exporter")
|
||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</ul>
|
||||
<ul v-if="track.extraartists && track.extraartists.length > 0" class="sm-hidden">
|
||||
<li v-for="extra in track.extraartists" class=" ml-4">
|
||||
<small>{{extra.role}} : {{extra.name}}</small>
|
||||
<small>{{extra.role}} : <a :href="`/ma-collection?page=1&limit=16&sort=year&order=asc&artist=${extra.name}`">{{extra.name}}</a></small>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
<link href="/css/main.css" rel="stylesheet" />
|
||||
|
||||
<script src="/js/libs.js"></script>
|
||||
<script defer src="/js/main.js"></script>
|
||||
|
||||
<% if ( config.matomoUrl ) { %>
|
||||
<!-- Matomo -->
|
||||
<script>
|
||||
|
@ -70,6 +73,37 @@
|
|||
<a class="navbar-item" href="/nous-contacter">
|
||||
Nous contacter
|
||||
</a>
|
||||
<div class="navbar-item has-dropdown">
|
||||
<a class="navbar-link">
|
||||
<i class="icon-adjust theme-system icon-theme hidden"></i>
|
||||
<i class="icon-sun theme-light icon-theme hidden"></i>
|
||||
<i class="icon-moon theme-dark icon-theme hidden"></i>
|
||||
<span>
|
||||
Thème
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<button class="navbar-item theme" data-value="system">
|
||||
<i class="icon-adjust"></i>
|
||||
<span>
|
||||
Système
|
||||
</span>
|
||||
</button>
|
||||
<button class="navbar-item theme" data-value="light">
|
||||
<i class="icon-sun"></i>
|
||||
<span>
|
||||
Clair
|
||||
</span>
|
||||
</button>
|
||||
<button class="navbar-item theme" data-value="dark">
|
||||
<i class="icon-moon"></i>
|
||||
<span>
|
||||
Sombre
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<% if ( user ) { %>
|
||||
<div class="navbar-item has-dropdown">
|
||||
<a class="navbar-link">
|
||||
|
@ -90,6 +124,9 @@
|
|||
<a class="navbar-item" href="/ma-collection/on-air">
|
||||
On air
|
||||
</a>
|
||||
<a class="navbar-item" href="/ma-collection/statistiques">
|
||||
Statistiques
|
||||
</a>
|
||||
<a class="navbar-item" href="/ma-collection/exporter">
|
||||
Exporter ma collection
|
||||
</a>
|
||||
|
@ -103,14 +140,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="navbar-item apparence">
|
||||
<div class="theme-switch-wrapper">
|
||||
<label class="theme-switch" for="checkbox" aria-label="Passer du thème clair au thème sombre et inversement">
|
||||
<input type="checkbox" id="checkbox" />
|
||||
<div class="slider round"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<% if ( !user ) { %>
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
|
@ -187,8 +216,5 @@
|
|||
Fait avec ❤️ à Bordeaux.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script defer src="/js/libs.js"></script>
|
||||
<script defer src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -238,7 +238,7 @@
|
|||
</div>
|
||||
|
||||
<h2 id="boites">Les boites</h2>
|
||||
<div class="box">
|
||||
<div class="box mini">
|
||||
<form method="POST">
|
||||
<h1>
|
||||
Connexion
|
||||
|
@ -487,14 +487,6 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-item apparence">
|
||||
<div class="theme-switch-wrapper">
|
||||
<label class="theme-switch" for="checkbox" aria-label="Passer du thème clair au thème sombre et inversement">
|
||||
<input type="checkbox" id="checkbox" />
|
||||
<div class="slider round"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-danger" href="/se-deconnecter">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="box">
|
||||
<div class="box mini">
|
||||
<form method="POST">
|
||||
<h1>
|
||||
Connexion
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
Pourquoi utiliser MusicTopus ?
|
||||
</h2>
|
||||
<p class="text-justify">
|
||||
<strong>MusicTopus</strong> est indispensable lorsqu'une collection, de CD-audios et vyniles, est devenue trop importante pour qu'on puisse se souvenir de tous les albums qu'elle contient. Consulter MusicTopus peut par exemple éviter un achat en double, et de savoir qu'on a des albums à céder ou échanger.
|
||||
<strong>MusicTopus</strong> est indispensable lorsqu'une collection, de CD-audios et vinyles, est devenue trop importante pour qu'on puisse se souvenir de tous les albums qu'elle contient. Consulter MusicTopus peut par exemple éviter un achat en double, et de savoir qu'on a des albums à céder ou échanger.
|
||||
<br />
|
||||
Il existe déjà plusieurs applications de gestion de librairies musicales mais, (au moment de l'édition de cette présentation) aucune facilement accessible via internet, par exemple lorsqu'on est chez un disquaire.
|
||||
</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="box">
|
||||
<div class="box mini">
|
||||
<form method="POST">
|
||||
<h1>
|
||||
Inscription
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<form method="POST" @submit.prevent="updateProfil">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
||||
<div>
|
||||
<div class="box">
|
||||
<h2>Mes données personnelles</h2>
|
||||
<div>
|
||||
<div class="field">
|
||||
|
@ -68,7 +68,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="box">
|
||||
<h2>Mon activité</h2>
|
||||
<div>
|
||||
<div class="field">
|
||||
|
@ -118,6 +118,8 @@
|
|||
<li>{format}, exemple : Cassette</li>
|
||||
<li>{year}, exemple: 1984</li>
|
||||
<li>{video}, exemple : https://www.youtube.com/watch?v=Qx0s8OqgBIw</li>
|
||||
<li>{genres}, exemple : Rock</li>
|
||||
<li>{styles}, exemple : Hard Rock, Heavy Metal</li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
|
@ -126,6 +128,22 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2>Mes préférences</h2>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label for="pagination">Pagination</label>
|
||||
<select id="pagination" v-model="formData.pagination">
|
||||
<option value="16">16 albums/page</option>
|
||||
<option value="24">24 albums/page</option>
|
||||
<option value="32">32 albums/page</option>
|
||||
<option value="48">48 albums/page</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div></div>
|
||||
|
||||
<button type="submit" class="button is-primary mt-10" :disabled="loading">
|
||||
<span v-if="!loading">Mettre à jour</span>
|
||||
|
@ -139,5 +157,6 @@
|
|||
<script>
|
||||
const email = '<%= user.email %>';
|
||||
const username = '<%= user.username %>';
|
||||
const pagination = "<%= user.pagination || 16 %>";
|
||||
const mastodon = <%- JSON.stringify(user.mastodon || {publish: false, url: '', token: '', message: ''}) %>;
|
||||
</script>
|
||||
|
|
|
@ -79,6 +79,8 @@
|
|||
<li>{format}, exemple : Cassette</li>
|
||||
<li>{year}, exemple: 1984</li>
|
||||
<li>{video}, exemple : https://www.youtube.com/watch?v=Qx0s8OqgBIw</li>
|
||||
<li>{genres}, exemple : Rock</li>
|
||||
<li>{styles}, exemple : Hard Rock, Heavy Metal</li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<main class="layout-maxed ma-collection-details" id="ma-collection-statistiques">
|
||||
<h1>
|
||||
Mes statistiques
|
||||
</h1>
|
||||
|
||||
<div class="grid gap-10 grid-cols-1 md:grid-cols-2 mb-10">
|
||||
<div class="md:col-span-2 box">
|
||||
<h2>Mon top 10</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 60px;"></th>
|
||||
<th>Artiste</th>
|
||||
<th style="width: 100px;">Albums</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for ( let i = 0 ; i < page.top10.length ; i += 1 ) { %>
|
||||
<tr>
|
||||
<td><%= i+1 %></td>
|
||||
<td><%= page.top10[i].name %></td>
|
||||
<td><%= page.top10[i].count %></td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-10 grid-cols-1 md:grid-cols-2 mb-10">
|
||||
<div class="box">
|
||||
<h2>Genres</h2>
|
||||
<canvas id="byGenres"></canvas>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2>Styles</h2>
|
||||
<canvas id="byStyles"></canvas>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h2>Formats</h2>
|
||||
<canvas id="byFormats"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const byGenres = <%- JSON.stringify(page.byGenres) %>;
|
||||
const byStyles = <%- JSON.stringify(page.byStyles) %>;
|
||||
const byFormats = <%- JSON.stringify(page.byFormats) %>;
|
||||
|
||||
const ctxGenres= document.getElementById('byGenres');
|
||||
const ctxStyles = document.getElementById('byStyles');
|
||||
const ctxFormats = document.getElementById('byFormats');
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctxGenres, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(byGenres).map((index) => {return byGenres[index].name}),
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: Object.keys(byGenres).map((index) => {return byGenres[index].color}),
|
||||
data: Object.keys(byGenres).map((index) => {return byGenres[index].count}),
|
||||
},
|
||||
],
|
||||
},
|
||||
options,
|
||||
});
|
||||
|
||||
const styleLabels = [];
|
||||
const styleBg = [];
|
||||
const styleData = [];
|
||||
for ( let i = 0 ; i < byStyles.length ; i += 1 ) {
|
||||
const {
|
||||
name,
|
||||
color,
|
||||
count,
|
||||
} = byStyles[i];
|
||||
|
||||
styleLabels.push(name);
|
||||
styleBg.push(color);
|
||||
styleData.push(count);
|
||||
}
|
||||
|
||||
new Chart(ctxStyles, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: styleLabels,
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: styleBg,
|
||||
data: styleData,
|
||||
},
|
||||
],
|
||||
},
|
||||
options,
|
||||
});
|
||||
|
||||
new Chart(ctxFormats, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(byFormats).map((index) => {return byFormats[index].name}),
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: Object.keys(byFormats).map((index) => {return byFormats[index].color}),
|
||||
data: Object.keys(byFormats).map((index) => {return byFormats[index].count}),
|
||||
},
|
||||
],
|
||||
},
|
||||
options,
|
||||
});
|
||||
</script>
|
|
@ -1,4 +1,4 @@
|
|||
<section class="box" id="contact">
|
||||
<section class="box mini" id="contact">
|
||||
<h1>Nous contacter</h1>
|
||||
<form @submit="send" <% if (config.mailMethod === 'formspree' ) { %> id="contact" method="POST" action="https://formspree.io/f/<%= config.formspreeId %>" <% } %>>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-16">
|
||||
|
|
Loading…
Reference in New Issue
Block a user