Version 1.0 (#26)

Issues :

* [x] Pouvoir partager sa collection #1
* [x] Exporter sa collection #2
* [x] Pouvoir visualiser le détail d'un album #3
* [x] Pouvoir visualiser les médias d'un album #5
* [x] Avoir un titre distinct par page #29
* [x] Avoir des statistiques avec Matomo #30
* [x] Avoir un logo pour les pages d'erreurs #32

Co-authored-by: dbroqua <contact@darkou.fr>
Reviewed-on: #26
This commit is contained in:
Damien Broqua 2022-04-08 15:03:01 +02:00
parent b27a81a0b6
commit 23c58459af
44 changed files with 1161 additions and 133 deletions

View File

@ -10,7 +10,7 @@ Le code source est publié sous licence libre [GNU GPL-3.0-or-later](LICENSE) et
Vous pouvez librement utiliser le service en vous inscrivant sur [https://www.musictopus.fr/](https://www.musictopus.fr/).
Une fois inscrit vous pourrez saisir vos CDs et Vinyles sur votre espace personnel le tout gratuitement, sans tracker et sans utilisation de vos données personnelles !
Une fois inscrit vous pourrez saisir vos CDs et Vinyles sur votre espace personnel le tout gratuitement, sans tracker (en dehors de statistiques web via [Matomo](https://fr.matomo.org/)) et sans utilisation de vos données personnelles !
## Auto hébergement
@ -195,9 +195,13 @@ MONGODB_URI # Url du serveur mongo (par défaut mongodb://musictopus-db/musictop
SECRET # Hash utilisé pour pour sauvegardé les dessions (par défaut waemaeMe5ahc6ce1chaeKohKa6Io8Eik)
DISCOGS_TOKEN # Token Discogs (vous devez créer un compte sur discogs afin d'en obtenir un gratuitement)
FORMSPREE_ID # Id du formulaire formspree pour la page "nous-contacter"
MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/)
MATOMO_ID # Id du site sur votre instance matomo (exemple: 1)
SITE_NAME # Nom du site (utilisé dans le titre des pages)
```
## Contributeurs
- Damien Broqua (développeur principal du projet)
- Brunus (Logo et fournisseur d'idées :wink: )
- Brunus (Logo et fournisseur d'idées :wink: )

View File

@ -25,6 +25,9 @@ services:
SECRET: ${SECRET}
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
FORMSPREE_ID: ${FORMSPREE_ID}
MATOMO_URL: ${MATOMO_URL}
MATOMO_ID: ${MATOMO_ID}
SITE_NAME: ${SITE_NAME}
networks:
- musictopus
musictopus-db:

View File

@ -25,6 +25,9 @@ services:
SECRET: ${SECRET}
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
FORMSPREE_ID: ${FORMSPREE_ID}
MATOMO_URL: ${MATOMO_URL}
MATOMO_ID: ${MATOMO_ID}
SITE_NAME: ${SITE_NAME}
networks:
- musictopus
musictopus-db:

View File

@ -27,9 +27,6 @@
},
"license": "GPL-3.0-or-later",
"devDependencies": {
"@babel/cli": "^7.17.0",
"@babel/core": "^7.17.2",
"@babel/preset-env": "^7.16.11",
"eslint": "^8.9.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.3.0",
@ -38,11 +35,12 @@
"husky": "^7.0.4",
"lint-staged": "^12.3.3",
"nodemon": "^2.0.15",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
"rimraf": "^3.0.2"
"prettier": "^2.5.1"
},
"dependencies": {
"@babel/cli": "^7.17.0",
"@babel/core": "^7.17.2",
"@babel/preset-env": "^7.16.11",
"axios": "^0.26.0",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
@ -54,14 +52,17 @@
"excel4node": "^1.7.2",
"express": "^4.17.2",
"express-session": "^1.17.2",
"joi": "^17.6.0",
"knacss": "^8.0.4",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"mongoose": "^6.2.1",
"mongoose-unique-validator": "^3.0.0",
"npm-run-all": "^4.1.5",
"passport": "^0.5.2",
"passport-http": "^0.3.0",
"passport-local": "^1.0.0",
"rimraf": "^3.0.2",
"sass": "^1.49.7",
"vue": "^3.2.31"
},

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Site en maintenance</title>
<title>MusicTopus - Erreur applicative</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
@ -17,37 +17,10 @@
<link href="/css/main.css" rel="stylesheet" />
<script src="/js/main.js"></script>
</head>
<body>
<nav class="navbar" aria-label="Navigation principale">
<div class="navbar-brand">
<a class="navbar-item" href="/">
<img src="/img/logo.png" alt="Logo MusicTopus">
<span>MusicTopus</span>
</a>
<a role="button" class="navbar-burger" aria-label="Afficher le menu" aria-expanded="false" data-target="navbar">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
</nav>
<main class="layout-maxed home">
<div class="header layout-hero"></div>
<h1>Site inaccessible</h1>
<p>
Pas de panique on revient très vite !
</p>
</main>
<footer class="footer layout-hero">
<p>
<strong title="Merci Brunus ! 😜">MusicTopus</strong> par <a href="https://www.darkou.fr" target="_blank" rel="noopener noreferrer">Damien Broqua <i class="icon-link"></i></a>.
Fait avec ❤️ à Bordeaux.
Le code source est sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html" target="_blank" rel="noopener noreferrer">GNU GPL-3.0-or-later <i class="icon-link"></i></a>.
</p>
</footer>
<body class="body-500">
<img src="/img/404.svg" alt="Image représentant la mascotte tenant un vinyle cassé" />
Nous sommes désolé mais quelque chose a mal tourné de notre côté.
<br />
<small>On devrait revenir très vite ! 😅</small>
</body>
</html>

Binary file not shown.

View File

@ -34,6 +34,8 @@
<glyph glyph-name="moon" unicode="&#xf186;" d="M704 123q-30-5-61-5-102 0-188 50t-137 137-50 188q0 107 58 199-112-33-183-128t-72-214q0-72 29-139t76-113 114-77 139-28q80 0 152 34t123 96z m114 47q-53-113-159-181t-230-68q-87 0-167 34t-136 92-92 137-34 166q0 85 32 163t87 135 132 92 161 38q25 1 34-22 11-23-8-40-48-43-73-101t-26-122q0-83 41-152t111-111 152-41q66 0 127 29 23 10 40-7 8-8 10-19t-2-22z" horiz-adv-x="857.1" />
<glyph glyph-name="share" unicode="&#xf1e0;" d="M679 279q74 0 126-53t52-126-52-126-126-53-127 53-52 126q0 7 1 19l-201 100q-51-48-121-48-75 0-127 53t-52 126 52 126 127 53q70 0 121-48l201 100q-1 12-1 19 0 74 52 126t127 53 126-53 52-126-52-126-126-53q-71 0-122 48l-201-100q1-12 1-19t-1-19l201-100q51 48 122 48z" horiz-adv-x="857.1" />
<glyph glyph-name="trash" unicode="&#xf1f8;" d="M286 82v393q0 8-5 13t-13 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q8 0 13 5t5 13z m143 0v393q0 8-5 13t-13 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q8 0 13 5t5 13z m142 0v393q0 8-5 13t-12 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q7 0 12 5t5 13z m-303 554h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q23 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
<glyph glyph-name="blind" unicode="&#xf29d;" d="M204 677q-35 0-61 25t-26 62q0 35 26 61t61 25 61-25 26-61q0-37-26-62t-61-25z m308-359q0-28-17-37t-35-4-27 19l-205 244q-4 7-8 9t-6 1l-1-2q-4-4 2-12l68-77 1-198-90-255q-38-107-52-130-8-15-15-18-28-15-58-1-16 7-23 24t-5 32q1 9 110 345l3 232-48-91 20-124q2-14-1-24t-8-15-10-9-10-4l-4-1q-10-2-19 1t-13 9-8 13-4 10-2 6l-25 167 118 212q12 19 63 19 41 0 59-22l237-291q4-3 8-9l1-2 0-1q4-7 4-16z m-225-83q24-64 49-126t39-94l13-31q21-51 24-69 6-39-20-54-20-13-37-9t-28 12-17 19h0q-4 9-5 14l-69 196z m460-331q17-27 17-32 0-3-2-4-5-2-8 1t-8 14-9 17q-64 96-236 369 1 0 4 1t3 2l2 1q6 5 6 10z" horiz-adv-x="785.7" />

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1,343 @@
<?xml version="1.0" ?><svg data-name="Layer 1" id="Layer_1" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#515570;}.cls-2,.cls-3,.cls-4,.cls-5{fill:none;stroke:#00adfe;stroke-miterlimit:10;}.cls-2,.cls-3,.cls-4,.cls-5,.cls-6{stroke-linecap:round;}.cls-2,.cls-5{stroke-width:4px;}.cls-3,.cls-4{stroke-width:3px;}.cls-4{stroke-dasharray:0.09 8.71;}.cls-5{stroke-dasharray:0.09 8.92;}.cls-6{fill:#fff;stroke:#393c54;stroke-linejoin:round;stroke-width:6px;}.cls-7{fill:#f8dc25;}</style></defs><title/><path class="cls-1" d="M41.48,93.8l-1.25,0-.6,14.28a2.07,2.07,0,0,1-1.89,2l-12.65,1.12A2.07,2.07,0,0,1,22.83,109L23.6,94.2,6.21,94.6a2.07,2.07,0,0,1-2.09-2.42L8.93,63.66a2.07,2.07,0,0,1,2-1.73h9.85a2.07,2.07,0,0,1,2,2.44L19.21,84.74l4.92-.15,1.32-26.28a2.07,2.07,0,0,1,2.07-2h12a2.07,2.07,0,0,1,2.07,2.16L40.59,84l.73,0A2.07,2.07,0,0,1,43.5,86v5.71A2.07,2.07,0,0,1,41.48,93.8Z"/><path class="cls-1" d="M121.48,50.8l-1.25,0-.6,14.28a2.07,2.07,0,0,1-1.89,2l-12.65,1.12A2.07,2.07,0,0,1,102.83,66l.77-14.83-17.39.39a2.07,2.07,0,0,1-2.09-2.42l4.81-28.51a2.07,2.07,0,0,1,2-1.73h9.85a2.07,2.07,0,0,1,2,2.44L99.21,41.74l4.92-.15,1.32-26.28a2.07,2.07,0,0,1,2.07-2h12a2.07,2.07,0,0,1,2.07,2.16l-1,25.49.73,0A2.07,2.07,0,0,1,123.5,43v5.71A2.07,2.07,0,0,1,121.48,50.8Z"/><path class="cls-2" d="M75.83,54.64l.69.48a14.84,14.84,0,0,1,4.26,19,11.87,11.87,0,0,1-16.07,4.85,9.5,9.5,0,0,1-3.88-12.86A7.6,7.6,0,0,1,71.12,63a6.08,6.08,0,0,1,2.48,8.23,4.86,4.86,0,0,1-6.58,2,3.89,3.89,0,0,1-1.59-5.27,3.11,3.11,0,0,1,4.21-1.27,2.49,2.49,0,0,1,1,3.37,2,2,0,0,1-2.7.81,1.59,1.59,0,0,1-.65-2.16A1.27,1.27,0,0,1,69,68.14a1,1,0,0,1,.42,1.38"/><path class="cls-3" d="M56,91a23.19,23.19,0,0,1-6.42-29.44,18.55,18.55,0,0,1,21.64-9"/><path class="cls-3" d="M87,92.64l0,0"/><path class="cls-4" d="M78.88,95.74a28.9,28.9,0,0,1-13.12-.21"/><path class="cls-3" d="M61.62,94.16l0,0"/><path class="cls-3" d="M98.32,81.11a28.9,28.9,0,0,1-6.83,8.44"/><path class="cls-2" d="M62.52,85.08A14.84,14.84,0,0,1,56.46,65a11.87,11.87,0,0,1,16.07-4.85A9.5,9.5,0,0,1,76.41,73a7.6,7.6,0,0,1-10.29,3.1,6.08,6.08,0,0,1-2.48-8.23,4.86,4.86,0,0,1,6.58-2,3.89,3.89,0,0,1,1.59,5.27,3.11,3.11,0,0,1-4.21,1.27,2.49,2.49,0,0,1-1-3.37,2,2,0,0,1,2.7-.81,1.59,1.59,0,0,1,.65,2.16,1.27,1.27,0,0,1-1.73.52,1,1,0,0,1-.42-1.38"/><path class="cls-3" d="M61,42.87a28.85,28.85,0,0,1,17.13,3.24A23.18,23.18,0,0,1,87.63,77.5,18.55,18.55,0,0,1,68.4,87.06"/><path class="cls-2" d="M41.49,54l0,0"/><path class="cls-5" d="M48,47.81a29,29,0,0,1,3.93-2.28"/><path class="cls-2" d="M56,43.92h0"/><ellipse class="cls-6" cx="68.42" cy="69.5" rx="7.58" ry="7.43"/><circle class="cls-7" cx="55.22" cy="55.5" r="4"/><path class="cls-7" d="M39.64,81.33l.47-1.2a.4.4,0,0,1,.75,0l.47,1.2a3.63,3.63,0,0,0,2,2l1.2.47a.4.4,0,0,1,0,.75l-1.2.47a3.63,3.63,0,0,0-2,2l-.47,1.2a.4.4,0,0,1-.75,0l-.47-1.2a3.63,3.63,0,0,0-2-2l-1.2-.47a.4.4,0,0,1,0-.75l1.2-.47A3.63,3.63,0,0,0,39.64,81.33Z"/><path class="cls-7" d="M94,48.27l.47-1.2a.4.4,0,0,1,.75,0l.47,1.2a3.63,3.63,0,0,0,2,2l1.2.47a.4.4,0,0,1,0,.75l-1.2.47a3.63,3.63,0,0,0-2,2l-.47,1.2a.4.4,0,0,1-.75,0L94,54.07a3.63,3.63,0,0,0-2-2l-1.2-.47a.4.4,0,0,1,0-.75l1.2-.47A3.63,3.63,0,0,0,94,48.27Z"/><circle class="cls-7" cx="95.61" cy="95.63" r="1.6"/><circle class="cls-7" cx="42.02" cy="63.64" r="1.6"/><circle class="cls-7" cx="85.48" cy="42.04" r="1.6"/></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 168.85766 133.4734"
version="1.1"
id="MusicTopus"
height="100%"
width="100%">
<defs
id="defs2">
<linearGradient
id="linearGradient3016">
<stop
offset="0"
stop-color="#949494"
id="stop3018-4" />
<stop
offset="1"
stop-opacity="0"
stop-color="#949494"
id="stop3020-0" />
</linearGradient>
<linearGradient
x1="57.074001"
y1="27.309999"
gradientTransform="translate(-19.041285,-22.505715)"
x2="103.29"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3016"
y2="104.59"
id="linearGradient1417" />
<radialGradient
r="7.395"
gradientTransform="matrix(2.2777,1.8145,-1.5547,2.3139,262.42,-105.22857)"
cx="16.073999"
cy="98.385002"
gradientUnits="userSpaceOnUse"
id="radialGradient10835">
<stop
offset="0"
stop-color="#989898"
id="stop10767-64" />
<stop
offset="1"
stop-opacity="0"
stop-color="#989898"
id="stop10769-6" />
</radialGradient>
</defs>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="translate(-4.0461145,-24.740973)">
<g
transform="matrix(-1,0,0,1,16.909353,13.841748)"
id="g4845">
<path
style="fill:#301818;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -124.03737,64.054339 c -0.67869,5.653701 -1.79458,14.69263 1.57781,21.418531 0.16987,0.339747 -10.7216,2.427294 -11.91019,-4.293016 -1.11922,-6.328076 -2.52943,-10.954215 -1.33635,-13.964832 3.07095,-7.749247 11.86215,-3.651666 11.66873,-3.160683 z"
id="path4839" />
<path
style="fill:#7b2121;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -125.27588,63.26029 c 0.41675,0.233807 1.34252,0.316004 1.74941,0.697875 -0.40534,4.680582 -0.98591,7.419536 -0.0655,11.187673 1.56849,6.421216 1.99919,7.121733 2.51886,9.876016 -0.60349,0.201385 -1.88006,0.796452 -2.50566,0.776752 -0.78768,-7.838385 -2.58856,-15.620579 -1.69713,-22.538316 z"
id="path4841" />
<path
style="fill:#f3e8d4;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -135.88244,67.615699 c 0,0 3.35454,1.181174 3.82701,2.787573 0.47247,1.606399 0.70871,6.000372 0.37798,8.031993 -0.33073,2.031624 -2.74033,2.882069 -2.74033,2.882069 -0.81232,-5.382945 -2.2983,-10.046051 -1.46466,-13.701635 z"
id="path4843" />
</g>
<g
id="g4837"
transform="translate(195.33147,13.841748)">
<path
id="path4828"
d="m -124.03737,64.054339 c -0.67869,5.653701 -1.79458,14.69263 1.57781,21.418531 0.16987,0.339747 -10.7216,2.427294 -11.91019,-4.293016 -1.11922,-6.328076 -2.52943,-10.954215 -1.33635,-13.964832 3.07095,-7.749247 11.86215,-3.651666 11.66873,-3.160683 z"
style="fill:#301818;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4830"
d="m -125.27588,63.26029 c 0.41675,0.233807 0.84139,0.349413 1.24828,0.731284 -0.40534,4.680582 -0.23721,7.396829 0.16838,11.25449 0.56623,5.385547 0.8967,7.522637 1.41637,10.27692 -0.60349,0.201385 -1.17847,0.395548 -1.80407,0.375848 -0.90324,-7.658049 -3.18063,-15.595778 -1.02896,-22.638542 z"
style="fill:#7b2121;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4832"
d="m -135.88244,67.615699 c 0,0 3.35454,1.181174 3.82701,2.787573 0.47247,1.606399 0.70871,6.000372 0.37798,8.031993 -0.33073,2.031624 -2.74033,2.882069 -2.74033,2.882069 -0.81232,-5.382945 -2.2983,-10.046051 -1.46466,-13.701635 z"
style="fill:#f3e8d4;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<path
style="fill:#ec8479;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 104.6994,52.638393 c -15.180178,-0.07451 -33.140948,8.951637 -33.639877,27.78125 -0.50786,19.166666 9.223449,34.250207 -6.425596,34.301337 -10.643849,0.0348 -6.80357,-16.158483 -6.80357,-16.158483 0.443803,-1.702342 -1.552627,-2.254759 -2.693082,-0.897692 -11.388255,13.593915 3.527003,26.318385 11.722934,37.071045 11.135404,14.60911 24.729611,20.33094 37.083241,-6.31323 16.89051,50.78843 72.75864,-14.34852 42.71131,-27.44716 -0.79429,-0.21911 -2.98222,-0.45948 -1.5119,1.74478 0,0 7.46421,11.18075 -3.02381,13.22916 -7.74851,0.37797 -6.46564,-8.79054 -1.88989,-25.702378 5.34881,-19.769046 -13.75388,-37.501741 -35.52976,-37.608629 z"
id="path4668" />
<path
style="fill:#fbb9b8;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 149.1438,102.30834 c 0,0 -0.30808,0.3199 -0.0742,0.7208 0.23386,0.4009 2.13815,1.20271 1.90429,2.03793 -0.23386,0.83522 -0.23386,1.46998 0.46773,2.03793 0.70158,0.56795 3.00678,1.43657 3.07359,2.23838 0.65095,0.94785 0.11036,4.73821 -1.10248,5.34539 -0.30068,0.83522 -0.26727,1.5368 -0.26727,1.5368 l 2.00452,4.47677 c 2.67736,-5.97229 4.5819,-11.72353 -6.00616,-18.394 z"
id="path4723" />
<path
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54.790252,98.184879 c 0,0 -8.485807,6.113791 -6.147199,15.134141 2.338609,9.02034 7.015826,10.95805 7.015826,10.95805 -0.248403,-2.16366 -0.08184,-4.23511 0.801809,-6.1472 0,0 -0.200453,-2.07134 -1.336349,-3.00678 -1.135896,-0.93545 -3.073599,-1.67044 -2.672694,-3.07361 0.400905,-1.40316 1.403165,-3.07359 0.935442,-4.14267 -0.467722,-1.06908 -1.069078,-1.73725 -0.801809,-2.87315 0.267272,-1.1359 2.204974,-6.848781 2.204974,-6.848781 z"
id="path4689" />
<ellipse
ry="20.250488"
rx="22.683598"
cy="99.451027"
cx="39.641644"
id="circle4790"
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8.20355606;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
<path
id="path4"
transform="translate(4.0461145,24.740973)"
d="m 34.146484,51.925781 c -0.314077,0.0092 -0.628478,0.01217 -0.941406,0.02734 -11.059699,0.788242 -20.839327,8.72416 -22.753906,18.482422 -1.0222988,4.666022 -0.263288,9.571297 1.953125,13.871094 l 2.441406,-2.441407 4.525391,2.404297 2.121094,-3.251953 5.230468,1.13086 0.707032,-4.09961 1.414062,4.382813 0.566406,2.402343 -5.65625,-0.564453 -1.414062,4.351719 -4.013672,2.913906 c 0.239784,0.192385 0.473561,0.390466 0.722656,0.574219 7.713021,6.01634 19.591819,7.137217 28.550781,2.716797 C 57.055648,90.480286 62.646095,80.45375 60.816406,71.099609 59.52397,63.325904 53.292903,56.50211 45.197266,53.552734 c 1.309899,2.835554 1.897379,5.928925 2.693359,8.935547 l -10.398437,2 0.90039,3.699219 -3.90039,-4 6.199218,-3.498047 -8.498047,-4.099609 3.998047,-1.900391 z m -9.673828,6.271485 c 0.23821,0.241845 0.400025,0.530577 0.587891,0.802734 -0.185522,2.23358 -3.870804,4.435587 -3.755859,4.089844 -1.319654,1.186159 -2.291211,3.893629 -4.216797,1.74414 1.76144,-2.731738 4.374999,-5.003791 7.384765,-6.636718 z m 2.291016,3.501953 c 0.217609,0.03588 0.400611,0.22504 0.52539,0.664062 1.260066,1.740193 -2.311306,4.335474 -3.519531,4.785157 -0.880279,0.143799 -1.50846,0.89274 -2.666015,-0.138672 0.261264,-1.586273 5.126947,-5.38799 3.720703,-4.21875 0.526069,-0.238877 1.182742,-0.95689 1.710937,-1.080078 0.07923,-0.01848 0.15598,-0.02368 0.228516,-0.01172 z m 2.261719,3.511719 c 0.318835,0.04398 0.591476,0.29958 0.78125,0.880859 0.691211,2.140581 -1.931888,4.415457 -2.626953,4.083984 -1.770649,-0.510364 -3.306383,-1.512291 -1.222657,-2.802734 0.746208,-0.529496 2.111854,-2.294057 3.06836,-2.162109 z m 6.597656,6.009765 c 0.500623,0.0012 1.007104,0.09327 1.488281,0.292969 3.025279,0.986468 3.11205,5.198119 0.179688,6.324219 -2.297247,1.06592 -5.580236,-0.439849 -5.564454,-2.841797 -0.353584,-2.07507 1.72712,-3.780425 3.896485,-3.775391 z m 7.677734,8.976563 c 0.454001,-0.07081 0.897599,0.528293 1.34375,0.736328 1.285766,0.439342 -0.203248,1.889914 -0.646484,2.283203 -1.277789,1.157639 -3.468693,-2.140236 -1.15625,-2.685547 0.154247,-0.211427 0.307651,-0.310382 0.458984,-0.333984 z m 4.345703,2.757812 c 0.225859,0.03343 0.481027,0.198816 0.785157,0.564453 1.747014,0.72605 -1.303778,2.574316 -1.595703,2.697266 -1.103201,1.48933 -3.167305,-1.432728 -1.03125,-1.835938 0.74365,-0.437265 1.164221,-1.526065 1.841796,-1.425781 z m 3.269532,2.306641 c 0.03893,0.0024 0.06028,0.01926 0.0625,0.05078 0.307212,0.15856 0.582857,0.359282 0.86914,0.544922 1.634832,1.36857 -2.961621,4.630292 -3.164062,4.070312 -2.704111,-1.615931 1.64845,-4.702094 2.232422,-4.666015 z"
style="fill:#212121;fill-opacity:1;stroke-width:0.00521397" />
<ellipse
ry="5.6584005"
rx="6.7384396"
style="opacity:1;fill:#ffaf62;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.41468528;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="path4786"
cx="39.641644"
cy="99.451027" />
<ellipse
ry="0.96285915"
rx="1.1466434"
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.41468525;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="path4788"
cx="39.641644"
cy="99.451027" />
<path
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 65.146946,109.76994 c -0.178541,-0.31245 -7.366014,3.88706 -8.686258,8.35993 -2.169132,7.34882 1.269529,10.75759 2.271789,11.89349 1.002261,1.1359 8.753078,5.41221 8.753078,5.41221 0,0 -2.53906,-8.01809 -2.472242,-12.22758 0.06682,-4.2095 1.336347,-12.2944 1.336347,-12.2944 0.214328,-1.45042 -0.351567,-1.51081 -1.202714,-1.14365 z"
id="path4693" />
<path
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 62.177083,111.27195 c 0,0 1.370163,-0.28348 1.700892,1.13393 0.330729,1.41741 -1.748139,2.92931 -1.559153,4.86644 0.18899,1.93713 0.944941,3.68527 0.803201,5.00818 -0.141742,1.32292 -1.228423,2.4096 -0.897694,4.15774 0.330729,1.74814 1.370163,3.73252 1.748139,4.96094 0.377979,1.22842 0.472472,2.22061 0.472472,2.22061 l -5.575149,-3.35454 c 0,0 -4.771948,-4.77195 -2.787573,-10.91406 1.984375,-6.14211 6.094865,-8.07924 6.094865,-8.07924 z"
id="path4695" />
<path
style="fill:#feb6b9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70.30357,122.75298 c -1.514956,0.94925 -3.443343,1.9309 -5.197172,1.98437 l 0.07087,-2.10249 c 1.584545,0.45556 3.723038,0.23999 5.126302,0.11812 z"
id="path4697" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 78.146576,107.06696 c 1.636436,10.62428 -0.93564,16.56205 -12.449591,21.04855 l -0.590587,-3.37816 c 7.160654,-1.083 13.649769,-7.8735 13.040178,-17.67039 z"
id="path4699" />
<path
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 81.637986,123.01159 c -12.670965,0.95958 -14.152431,12.42398 -10.076572,21.37751 7.724779,4.9815 14.580842,3.99696 17.238885,3.0736 0,0 -9.888972,-5.07812 -9.354434,-11.55941 0.53454,-6.48129 3.741774,-10.89123 3.741774,-10.89123 0.428014,-1.07406 -0.282581,-1.80534 -1.549653,-2.00047 z"
id="path4701" />
<path
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 79.448737,123.30205 c 0.992925,-0.19905 1.78017,0.29602 1.107439,1.0337 0,0 -1.819011,0.56696 -2.031622,2.52772 -0.212614,1.96075 0.354351,2.36235 0,3.47265 -0.354354,1.11031 -2.362353,3.85063 -2.315104,5.14993 0.04725,1.29929 2.244233,3.56715 2.433219,4.70108 0.18899,1.13393 -0.377975,2.81119 0.566965,4.11049 0.944941,1.29929 2.14974,3.73251 2.14974,3.73251 -4.734539,-0.4794 -8.074618,-2.55807 -9.79796,-3.64103 -1.841663,-3.17287 -5.803388,-17.66538 7.887323,-21.08705 z"
id="path4703" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 96.150212,124.61115 c -4.01876,5.52151 -8.794257,9.92116 -16.711757,11.36671 -0.08036,0.88765 0.137909,1.87224 0.41237,2.79858 11.745993,-2.60459 14.877081,-9.93974 16.299387,-14.16529 z"
id="path4705" />
<path
style="fill:#eea6a7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 86.603794,146.14025 2.196505,1.32245 c 6.701159,-2.41232 11.511951,-7.21119 15.190401,-13.27595 1.66173,6.03294 8.2499,13.42904 14.22135,13.46541 l 1.87506,-1.76484 c 0,0 0.0148,0.77265 -2.15854,-0.59751 -2.17336,-1.37017 -0.75595,-2.74033 -2.17336,-2.83483 -1.41741,-0.0945 -1.9286,0.12534 -4.20499,-0.7087 -1.44801,-0.53053 -0.75609,-4.10885 -1.74814,-5.05543 -0.99131,-0.94586 -2.3151,-0.56697 -2.92931,-2.5041 -0.61422,-1.93712 0.33073,-2.64583 -0.56697,-3.96875 -0.89178,-1.83082 -2.3092,-2.24423 -2.3092,-2.24423 -0.1266,-0.0643 -1.51607,0.38315 -2.36825,1.96075 -0.37793,0.96857 -0.8032,4.48847 -1.748143,4.77195 -0.944941,0.28348 -1.700892,0.28348 -2.69308,2.03162 -0.992187,1.74814 -0.944941,3.77976 -2.173364,4.25224 -1.228421,0.47247 -2.83482,0.33073 -3.638021,1.46465 -0.803198,1.13393 -4.771948,3.68527 -4.771948,3.68527 z"
id="path4707" />
<path
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 123.59821,125.20982 c -1.3987,-1.65054 -0.85807,-3.15445 2.88207,-2.78757 12.13782,1.66374 11.02212,16.63634 10.25261,17.85937 -2.85371,8.22299 -13.58843,8.19381 -18.52084,7.37054 0.96034,-1.98472 15.90292,-9.54748 5.38616,-22.44234 z"
id="path4709" />
<path
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 127.42522,122.75298 c 0,0 -1.93712,-0.23624 -1.65364,1.13392 0.28348,1.37017 2.3151,1.46466 2.26785,2.69308 -0.0472,1.22843 -0.51971,4.06325 0.33073,4.63021 0.85045,0.56697 2.55134,2.97656 1.55915,5.71689 -0.99218,2.74033 -1.98437,1.08668 -2.40959,3.54353 -0.42523,2.45684 -0.75596,3.77976 -1.46466,4.53571 -0.70871,0.75595 -1.22842,2.88207 -1.22842,2.88207 0,0 6.75631,0.37798 8.97693,-3.30729 9.56355,-9.24479 -1.05337,-21.95234 -6.37835,-21.82812 z"
id="path4711" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 115.7277,126.3484 c 2.71026,4.74824 6.41201,7.84401 11.526,8.58604 0.0146,0.75716 -0.10568,1.5973 -0.4009,2.22168 -5.69579,-1.24493 -9.34848,-5.1092 -11.1251,-10.80772 z"
id="path4713" />
<path
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 143.05601,138.17508 c 8.45644,-12.2696 1.58622,-19.08645 -2.40542,-26.9274 -1.17246,-1.73452 0.56242,-1.73337 1.26953,-1.5368 4.83863,1.10659 18.77671,5.73353 11.35895,22.98518 -3.1826,2.72671 -6.38288,4.87014 -10.22306,5.47902 z"
id="path4715" />
<path
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 145.33184,110.65773 c -1.44409,-0.17245 -1.87623,0.88971 -1.74814,1.55916 0.12809,0.66945 1.37019,0.51971 1.41742,2.07887 0.0472,1.55916 1.74813,4.91369 2.03162,5.57515 0.28348,0.66145 1.88988,1.65364 2.07887,4.06324 0.18898,2.4096 -0.36512,6.46405 -0.99219,7.46503 -0.73995,1.18117 -0.10965,4.58296 0.33073,4.58296 1.94348,-0.70588 3.22695,-2.18177 4.82892,-3.28608 3.76978,-8.80185 3.25139,-17.5402 -7.94723,-22.03833 z"
id="path4717" />
<path
style="fill:#feb6b9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 145.66189,120.68563 c 0.50608,1.04701 0.81356,2.13795 1.05237,3.27405 0,0 -1.26953,0.21716 -3.54132,0.15034 -2.27179,-0.0668 -5.028,-1.1526 -5.028,-1.1526 0,0 2.12145,0.33409 4.07585,-0.3842 1.95441,-0.71829 3.4411,-1.88759 3.4411,-1.88759 z"
id="path4719" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 131.34627,111.26438 c -0.46387,6.60804 -0.23431,15.14869 15.71879,15.3513 l -0.3508,-2.656 c -5.36818,0.47928 -14.93993,-0.59196 -15.36799,-12.6953 z"
id="path4721" />
<g
id="g4740"
transform="rotate(19.617168,87.590538,52.720911)">
<path
id="path4725"
d="m 108.72438,96.054369 c -1.67459,2.099573 -6.51237,1.558255 -9.762082,-0.889359 -3.249707,-2.447614 -5.286913,-7.54075 -1.925906,-10.583607 2.406544,-2.178749 7.101418,-0.79705 9.431368,1.36184 2.53306,2.34709 4.6065,7.164892 2.25662,10.111126 z"
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.39299998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
<path
style="opacity:1;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35395768;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 108.84598,96.203312 c -1.50822,1.890992 -5.8654,1.403451 -8.79227,-0.801006 -2.926867,-2.204458 -4.761688,-6.79162 -1.734578,-9.532187 2.167468,-1.962302 6.395928,-0.717867 8.494418,1.226549 2.28141,2.11392 4.14887,6.453101 2.03243,9.106644 z"
id="path4730" />
<circle
r="3.4367433"
cy="91.80127"
cx="101.24133"
id="path4732"
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.46482563;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
<circle
style="opacity:1;fill:#ffffff;fill-opacity:0.94117647;fill-rule:nonzero;stroke:none;stroke-width:0.14161019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="circle4734"
cx="100.24915"
cy="91.470535"
r="1.0470117" />
<circle
r="0.6970861"
cy="93.070198"
cx="102.3987"
id="circle4657"
style="opacity:1;fill:#ffffff;fill-opacity:0.94117647;fill-rule:nonzero;stroke:none;stroke-width:0.14161019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
</g>
<g
transform="matrix(-0.84292907,0.5380247,0.5380247,0.84292907,153.98869,-37.720137)"
id="g4750">
<path
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.39299998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 108.72438,96.054369 c -1.67459,2.099573 -6.51237,1.558255 -9.762082,-0.889359 -3.249707,-2.447614 -5.286913,-7.54075 -1.925906,-10.583607 2.406544,-2.178749 7.101418,-0.79705 9.431368,1.36184 2.53306,2.34709 4.6065,7.164892 2.25662,10.111126 z"
id="path4742" />
<path
id="path4744"
d="m 108.84598,96.203312 c -1.50822,1.890992 -5.8654,1.403451 -8.79227,-0.801006 -2.926867,-2.204458 -4.761688,-6.79162 -1.734578,-9.532187 2.167468,-1.962302 6.395928,-0.717867 8.494418,1.226549 2.28141,2.11392 4.14887,6.453101 2.03243,9.106644 z"
style="opacity:1;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35395768;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
<circle
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.46482563;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="circle4746"
cx="105.85611"
cy="90.845963"
r="3.4367433" />
<circle
r="1.0470117"
cy="89.537895"
cx="106.96864"
id="circle4748"
style="opacity:1;fill:#ffffff;fill-opacity:0.94117647;fill-rule:nonzero;stroke:none;stroke-width:0.14161019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
<circle
style="opacity:1;fill:#ffffff;fill-opacity:0.94117647;fill-rule:nonzero;stroke:none;stroke-width:0.14161019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="circle4655"
cx="105.70718"
cy="92.300102"
r="0.59710735" />
</g>
<path
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 79.440842,83.663452 c 2.401026,-4.843277 7.683432,-2.978854 9.070822,-3.016481 -2.556226,0.763727 -4.708947,2.955988 -6.701589,4.398539 -0.740592,0.536143 -2.442986,-0.394305 -2.369233,-1.382058 z"
id="path4752" />
<path
id="path4754"
d="m 125.44005,85.24075 c -2.30199,-5.105822 -7.76722,-3.941999 -8.60332,-4.166742 2.43567,1.088609 4.28608,3.541392 6.07494,5.23005 0.66486,0.627613 2.47348,-0.07434 2.52838,-1.063308 z"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70.96503,82.262275 c 0,0 6.331101,3.071056 7.606771,-0.519718 1.275667,-3.590772 -1.27567,-7.9375 -1.27567,-7.9375 2.892685,-1.049875 3.091067,-3.4469 2.220608,-6.378347 2.152776,-0.348696 4.444174,-0.539426 1.322917,-6.898066 -4.769498,4.415313 -9.420477,9.057506 -9.874626,21.733631 z"
id="path4756" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 88.132125,56.156746 c 0,0 3.254408,16.386084 14.566195,13.79779 7.88444,-1.80407 6.28083,-7.617182 6.28083,-7.617182 3.56518,5.081814 12.40153,4.310975 11.29214,-2.539061 2.41655,0.835207 3.97125,0.06982 5.14493,-1.403165 -13.2372,-7.82638 -25.466402,-6.83272 -37.284095,-2.238382 z"
id="path4758" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 131.36297,62.871892 c -1.12784,1.399585 -4.16412,7.545045 2.00452,7.650591 -0.56702,5.505473 1.00425,9.251589 7.45014,8.986938 -1.43088,-7.033989 -4.7288,-12.453251 -9.45466,-16.637529 z"
id="path4760" />
<path
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 140.95127,86.391611 c -1.79338,-0.414357 -3.99759,-0.910879 -6.68174,-1.503391 -2.67445,-0.05943 -3.63783,1.364072 -2.3052,4.777443 2.82115,3.939599 4.73867,3.511676 7.08264,5.144937 0.70035,-2.780098 1.61025,-5.476371 1.9043,-8.418989 z"
id="path4762" />
<path
style="fill:#7f2625;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 93.342168,119.42814 c 0.529236,-0.73806 0.604365,-0.3573 1.136405,-1.09204 0.364677,-0.50361 0.199924,-1.06153 0.644573,-1.46533 0.309321,-0.28091 0.977351,-0.124 1.362201,-0.31657 0.345479,-0.17287 0.405453,-0.70096 0.732931,-0.84737 0.789296,-0.35288 1.800707,0.26944 2.651021,0.0936 1.078571,-0.22307 2.076941,-1.09961 3.190181,-1.10946 0.97926,-0.009 1.7824,0.64931 2.75826,0.86661 0.76717,0.17085 1.97838,-0.33367 2.73467,-0.0767 0.43365,0.14737 1.07369,-0.0937 1.4783,0.11451 0.30157,0.15517 0.33276,0.75525 0.61169,0.95375 1.72336,1.22644 1.31075,0.33606 2.60238,2.06475 0,0 0.28975,-0.0348 -2.07105,-2.47226 -0.32347,-0.33398 -0.51448,-1.08044 -0.86627,-1.38846 -0.45582,-0.39911 -1.04178,-0.39938 -1.53149,-0.74298 -0.6807,-0.4776 -1.9258,0.26924 -2.60859,-0.0841 -1.25954,-0.65171 -2.00993,-2.31042 -3.18147,-2.02677 -1.01838,0.24657 -2.19748,1.18524 -3.446552,1.56394 -0.794617,0.24091 -1.705404,-0.22114 -2.337848,0.30385 -0.769592,0.63883 -0.5723,0.83835 -1.171115,1.59816 -0.306698,0.38915 -0.985411,0.37691 -1.287999,0.78707 -0.398797,0.54057 -0.388125,1.15748 -0.697902,1.70652 -0.81359,1.44197 -0.702326,1.56918 -0.702326,1.56918 z"
id="path4764" />
<path
style="fill:none;stroke:#301818;stroke-width:4.46500015;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 66.416477,77.838987 c 0,0 2.248303,-29.702875 39.355443,-29.132379 37.10713,0.570497 40.69178,29.399648 40.69178,29.399648"
id="path4847" />
<path
id="path4651"
d="m 82.283169,89.859621 c 1.901054,-0.826347 5.017206,-1.87303 7.190809,-1.209662 2.299291,0.701728 3.661648,3.157157 5.855653,3.784919 0,0 -0.675634,-7.540694 -7.907825,-7.911193 -3.749793,-0.192098 -5.138637,5.335936 -5.138637,5.335936 z"
style="opacity:1;vector-effect:none;fill:#7f2625;fill-opacity:1;stroke:none;stroke-width:1.48535442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="opacity:1;vector-effect:none;fill:#7f2625;fill-opacity:1;stroke:none;stroke-width:1.48535442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 122.49651,94.411543 c -3.78678,-6.81696 -8.0759,-7.480996 -11.84594,-3.933836 0,0 3.20445,-7.27537 10.03419,-4.867774 4.12152,1.452904 1.81175,8.80161 1.81175,8.80161 z"
id="path4653" />
<g
style="stroke-width:42.36951447"
id="g6"
transform="matrix(0.02360188,0,0,0.02360188,113.18825,105.15104)">
<path
id="path2"
d="m 117.16,11.645 c 0,0 -31.496,38.028 -68.31,85.296 -47.195,60.599 -70.131,146.52 31.843,165.81 113.27,21.429 132.5,-118.17 65.274,-157.01 -49.223,-28.424 -28.81,-94.097 -28.81,-94.095 z"
style="fill:#29abe2;stroke-width:42.36951447" />
<path
id="path4-9"
d="m 51.844,134.31 c 0,0 -56.287,70.6 25.797,108.12 0,0.01 -44.304,-31.59 -25.797,-108.12 z"
style="fill:#ffffff;stroke-width:42.36951447" />
</g>
<g
transform="matrix(-0.0125514,0,0,0.0125514,96.697579,101.08859)"
id="g4688"
style="stroke-width:79.67237854">
<path
style="fill:#29abe2;stroke-width:79.67237854"
d="m 117.16,11.645 c 0,0 -31.496,38.028 -68.31,85.296 -47.195,60.599 -70.131,146.52 31.843,165.81 113.27,21.429 132.5,-118.17 65.274,-157.01 -49.223,-28.424 -28.81,-94.097 -28.81,-94.095 z"
id="path4684" />
<path
style="fill:#ffffff;stroke-width:79.67237854"
d="m 51.844,134.31 c 0,0 -56.287,70.6 25.797,108.12 0,0.01 -44.304,-31.59 -25.797,-108.12 z"
id="path4686" />
</g>
<path
id="path4692"
d="m 85.947033,99.042108 0.671609,-0.0707 c 0,0 0.49487,-0.636261 0.777652,-0.353479 0.282783,0.282783 0.424174,0.777652 1.343218,0.565565 0.919043,-0.212086 0.353478,0.212087 1.343217,0.49487 0.989739,0.282783 0.777653,0 1.484609,-0.282783 0.706957,-0.282782 0.212087,-0.777652 1.060435,-0.565565 0.848348,0.212087 0.282782,0.212087 1.272521,-0.212087 0.989739,-0.424174 1.334642,-0.665544 1.334642,-0.665544 0,0 -0.5002,2.353665 -3.049011,3.334305 -4.895674,0.33581 -6.238892,-2.244587 -6.238892,-2.244587 z"
style="opacity:1;vector-effect:none;fill:#29abe2;fill-opacity:1;stroke:none;stroke-width:1.48535442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="fill:#29ebe2;fill-opacity:1;stroke-width:1.00000024"
d="m 90.215634,100.78237 c 0,0 0.47109,0.17648 1.084764,0.10067 0.31585,-0.039 0.649984,-0.31716 1.017863,-0.42775 0.326516,-0.0982 0.666764,-0.12133 0.976323,-0.31443 0.19834,-0.12373 0.30993,-0.330545 0.48732,-0.500317 0.276456,-0.264584 0.578906,-0.54704 0.757961,-0.950004 2.09e-4,-7.1e-5 -0.344817,0.269525 -1.022134,0.718841 -0.188018,0.124727 -0.295615,0.385643 -0.557188,0.510044 -0.286058,0.136046 -0.727267,0.150626 -1.096851,0.284556 -0.199876,0.0724 -0.248589,0.33662 -0.45906,0.42367 -0.308425,0.12756 -0.791836,0.0775 -1.188998,0.15472 z"
id="path4694" />
<path
style="opacity:1;vector-effect:none;fill:#29abe2;fill-opacity:1;stroke:none;stroke-width:1.48535442;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 119.99047,99.106433 -0.65394,0.265104 c 0,0 -0.42418,-0.459522 -0.70696,-0.17674 -0.28278,0.282783 -0.26511,0.848353 -1.18415,0.636261 -0.91904,-0.212086 -0.35348,0.212092 -1.34322,0.494872 -0.98974,0.28278 -0.77765,0 -1.48461,-0.28278 -0.70695,-0.282787 -0.21208,-0.777657 -1.06043,-0.56557 -0.84835,0.212087 -0.28278,0.212087 -1.27252,-0.212087 -0.98974,-0.424174 -1.51138,-0.479968 -1.51138,-0.479968 0,0 0.9067,2.486225 3.22575,3.148735 2.34542,0.68368 4.48756,-0.65364 5.92076,-2.54505 z"
id="path4698" />
<path
id="path4700"
d="m 115.9693,101.42994 c 0,0 -0.47109,0.17648 -1.08477,0.10067 -0.31585,-0.039 -0.64998,-0.31716 -1.01786,-0.42775 -0.32651,-0.0982 -0.66676,-0.12133 -0.97632,-0.31443 -0.19834,-0.12373 -0.30993,-0.33055 -0.48732,-0.50032 -0.27646,-0.26458 -0.57891,-0.547043 -0.75796,-0.950007 -2.1e-4,-7.1e-5 0.34481,0.269525 1.02213,0.718847 0.18802,0.12472 0.29562,0.38564 0.55719,0.51004 0.28606,0.13605 0.72727,0.15063 1.09685,0.28456 0.19988,0.0724 0.24859,0.33662 0.45906,0.42367 0.30843,0.12756 0.79184,0.0775 1.189,0.15472 z"
style="fill:#29ebe2;fill-opacity:1;stroke-width:1.00000024" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" ?><svg height="24" version="1.1" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="translate(0 -1028.4)"><path d="m12 1037.4c-2.2091 0-4 1.8-4 4s1.7909 4 4 4c2.209 0 4-1.8 4-4s-1.791-4-4-4zm0 3c0.552 0 1 0.4 1 1 0 0.5-0.448 1-1 1s-1-0.5-1-1c0-0.6 0.448-1 1-1z" fill="#e67e22"/><path d="m5.0503 1034.2c-3.9053 3.9-3.9053 10.2 0 14.1 3.9052 3.9 10.237 3.9 14.142 0 3.906-3.9 3.906-10.2 0-14.1-3.905-3.9-10.236-3.9-14.142 0zm4.9497 4.9c1.172-1.2 3.071-1.2 4.243 0 1.171 1.2 1.171 3.1 0 4.3-1.172 1.1-3.071 1.1-4.243 0-1.1716-1.2-1.1716-3.1 0-4.3z" fill="#bdc3c7"/><path d="m4.9289 1033.3c-3.9052 3.9-3.9052 10.2 0 14.1 3.9053 3.9 10.237 3.9 14.142 0s3.905-10.2 0-14.1-10.237-3.9-14.142 0zm4.9498 4.9c1.1713-1.1 3.0713-1.1 4.2423 0 1.172 1.2 1.172 3.1 0 4.3-1.171 1.2-3.071 1.2-4.2423 0-1.1716-1.2-1.1716-3.1 0-4.3z" fill="#34495e"/><path d="m3.3392 1035.4c-0.4005 0.7-0.6934 1.4-0.9126 2.1l6.9748 1.4-4.6623-5.4c-0.5275 0.6-0.9993 1.2-1.3999 1.9z" fill="#ecf0f1"/><path d="m20.66 1045.4c0.4-0.7 0.693-1.4 0.912-2.2l-6.974-1.3 4.662 5.3c0.527-0.5 0.999-1.1 1.4-1.8z" fill="#ecf0f1"/><path d="m12 1036.4c-2.2091 0-4 1.8-4 4s1.7909 4 4 4c2.209 0 4-1.8 4-4s-1.791-4-4-4zm0 3c0.552 0 1 0.4 1 1 0 0.5-0.448 1-1 1s-1-0.5-1-1c0-0.6 0.448-1 1-1z" fill="#f1c40f"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -2,13 +2,16 @@
* Fonction permettant d'afficher un message dans un toastr
* @param {String} message
*/
function showToastr(message) {
function showToastr(message, success = false) {
let x = document.getElementById("toastr");
if ( message ) {
x.getElementsByTagName("SPAN")[0].innerHTML = message;
}
x.className = `${x.className} show`;
x.className = `${x.className} show`.replace("sucess", "");
if ( success ) {
x.className = `${x.className} success`;
}
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

22
sass/500.scss Normal file
View File

@ -0,0 +1,22 @@
.body-500 {
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: larger;
line-height: 200%;
text-align: center;
img {
max-width: 60%;
margin-bottom: 32px;
@include respond-to("small-up") {
max-width: 30%;
}
}
}

View File

@ -54,7 +54,7 @@
&.is-link {
background-color: transparent;
border-color: $nord9;
color: $nord9;
color: var(--button-link-text-color);
&:hover {
border-color: darken($nord9, $hoverAmount);

View File

@ -1,4 +1,9 @@
.ma-collection {
.collection {
h1 {
i {
cursor: pointer;
}
}
.filters {
display: flex;
justify-content: end;

View File

@ -29,7 +29,7 @@ $danger-color-hl: darken($danger-color, $hoverAmount);
$warning-color-hl: darken($warning-color, $hoverAmount);
$success-color-hl: darken($success-color, $hoverAmount);
$button-font-color: $nord1;
$button-font-color: #2C364A;
$button-alternate-color: #01103C;
$pagination-border-color: $nord3;
@ -38,6 +38,7 @@ $pagination-hover-color: rgb(115, 151, 186);
:root {
--default-color: #{$white};
--bg-color: #{darken($white, 5%)};
--bg-alternate-color: #{darken($white, 8%)};
--font-color: #{$nord3};
--footer-color: #{$darken-white};
--link-color: #{$nord1};
@ -45,18 +46,38 @@ $pagination-hover-color: rgb(115, 151, 186);
--input-font-color: #{$nord3};
--input-color: #{$white};
--input-active-color: #{$nord5};
--navbar-color: #{darken($white, 5%)};
--box-bg-color: #F8F9FB;
--box-shadow-color: #{rgba($nord4, 0.35)};
--border-color: #{$nord4};
--button-link-text-color: #2C364A;
--nord0: #{$nord0};
--nord1: #{$nord1};
--nord2: #{$nord2};
--nord3: #{$nord3};
--nord4: #{$nord4};
--nord5: #{$nord5};
--nord6: #{$nord6};
--nord7: #{$nord7};
--nord8: #{$nord8};
--nord9: #{$nord9};
--nord10: #{$nord10};
--nord11: #{$nord11};
--nord12: #{$nord12};
--nord13: #{$nord13};
--nord14: #{$nord14};
--nord15: #{$nord15};
}
[data-theme="dark"] {
--default-color: #{$nord3};
--bg-color: #{lighten($nord0, 2%)};
--bg-alternate-color: #{lighten($nord3, 8%)};
--font-color: #{$nord6};
--footer-color: #{$nord1};
--link-color: #{$nord4};
@ -71,4 +92,6 @@ $pagination-hover-color: rgb(115, 151, 186);
--box-shadow-color: #{rgba($nord4, 0.2)};
--border-color: #{$nord1};
--button-link-text-color: #{$white};
}

13
sass/composants.scss Normal file
View File

@ -0,0 +1,13 @@
.composants {
.couleur {
margin-bottom: 1rem;
text-align: center;
border: 1px solid var(--input-active-color);
box-shadow: var(--box-shadow-color) 0px 3px 6px 0px;
div {
height: 56px;
}
}
}

View File

@ -82,4 +82,8 @@ html {
@include respond-to("small-up") {
display: initial;
}
}
.is-danger {
color: $nord12;
}

View File

@ -46,6 +46,7 @@
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-sun:before { content: '\f185'; } /* '' */
.icon-moon:before { content: '\f186'; } /* '' */
.icon-share:before { content: '\f1e0'; } /* '' */
.icon-trash:before { content: '\f1f8'; } /* '' */
.icon-blind:before { content: '\f29d'; } /* '' */
@ -61,4 +62,4 @@
100% {
transform: rotate(359deg);
}
}
}

View File

@ -41,7 +41,9 @@
@import './box';
@import './error';
@import './500';
@import './home';
@import './ajouter-un-album';
@import './ma-collection';
@import './ma-collection-details';
@import './collection';
@import './ma-collection-details';
@import './composants';

View File

@ -3,30 +3,35 @@
.item{
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--border-color);
border-bottom: 2px solid var(--border-color);
background-color: var(--bg-alternate-color);
@include transition() {}
@include respond-to("medium") {
&:nth-child(2n) {
background-color: var(--default-color);
}
border: none;
}
@include respond-to("medium-up") {
border-left: 1px solid var(--border-color);
border-left: 2px solid var(--border-color);
&:nth-child(4n),
&:nth-child(4n-1)
{
background-color: var(--default-color);
}
&:first-child,
&:nth-child(2) {
border-top: 1px solid var(--border-color);
border-top: 2px solid var(--border-color);
}
&:nth-child(2n),
&:last-child {
border-right: 1px solid var(--border-color);
margin-right: -1px;
}
&:hover {
background-color: var(--default-color);
border-right: 2px solid var(--border-color);
margin-right: -2px;
}
}
@ -44,4 +49,10 @@
max-width: 90%;
}
}
&.hover {
.item:hover {
background-color: var(--border-color);
}
}
}

View File

@ -16,6 +16,7 @@
img {
max-width: 90%;
max-height: 90%;
}
}
}

View File

@ -13,6 +13,11 @@
color: $button-alternate-color;
border-radius: 6px;
&.success {
background-color: $success-color;
color: $button-font-color;
}
&.show {
visibility: visible;
animation: toastrFadein 0.5s, toastrFadeout 0.5s 2.5s;

View File

@ -13,9 +13,11 @@ import { isXhr } from "./helpers";
import indexRouter from "./routes";
import maCollectionRouter from "./routes/ma-collection";
import collectionRouter from "./routes/collection";
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
import importSearchRouterApiV1 from "./routes/api/v1/search";
import importMeRouterApiV1 from "./routes/api/v1/me";
// Mongoose schema init
require("./models/users");
@ -82,8 +84,10 @@ app.use(
app.use("/", indexRouter);
app.use("/ma-collection", maCollectionRouter);
app.use("/collection", collectionRouter);
app.use("/api/v1/albums", importAlbumRouterApiV1);
app.use("/api/v1/search", importSearchRouterApiV1);
app.use("/api/v1/me", importMeRouterApiV1);
// Handle 404
app.use((req, res) => {
@ -113,7 +117,10 @@ app.use((error, req, res, next) => {
} else {
res.status(error.errorCode || 500);
res.render("index", {
page: { title: "500: Oups… le serveur a crashé !", error },
page: {
title: error.title || "500: Oups… le serveur a crashé !",
error,
},
errorCode: error.errorCode || 500,
viewname: "error",
user: req.user || null,

View File

@ -5,4 +5,7 @@ module.exports = {
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
discogsToken: process.env.DISCOGS_TOKEN,
formspreeId: process.env.FORMSPREE_ID,
matomoUrl: process.env.MATOMO_URL || "",
matomoId: process.env.MATOMO_ID || "",
siteName: process.env.SITE_NAME || "MusicTopus",
};

View File

@ -4,9 +4,10 @@
class ErrorEvent extends Error {
/**
* @param {Number} errorCode
* @param {String} title
* @param {Mixed} ...params
*/
constructor(errorCode, ...params) {
constructor(errorCode, title, ...params) {
super(...params);
if (Error.captureStackTrace) {
@ -14,6 +15,7 @@ class ErrorEvent extends Error {
}
this.errorCode = parseInt(errorCode, 10);
this.title = title;
this.date = new Date();
}
}

View File

@ -5,12 +5,19 @@ import xl from "excel4node";
import Pages from "./Pages";
import AlbumsModel from "../models/albums";
import UsersModel from "../models/users";
import ErrorEvent from "../libs/error";
/**
* Classe permettant la gestion des albums d'un utilisateur
*/
class Albums extends Pages {
/**
* Méthode permettant de remplacer certains cartactères par leur équivalents html
* @param {String} str
*
* @return {String}
*/
static replaceSpecialChars(str) {
if (!str) {
return "";
@ -487,7 +494,7 @@ class Albums extends Pages {
static async getAllDistincts(field, user) {
const distincts = await AlbumsModel.find(
{
user,
User: user,
},
[],
{
@ -513,8 +520,11 @@ class Albums extends Pages {
order = "asc",
artists_sort,
format,
userId: collectionUserId,
} = this.req.query;
let userId = this.req.user?._id;
const where = {};
if (artists_sort) {
@ -524,8 +534,35 @@ class Albums extends Pages {
where["formats.name"] = format;
}
if (!this.req.user && !collectionUserId) {
throw new ErrorEvent(
401,
"Cette collection n'est pas publique",
"Cette collection n'est pas publique"
);
}
if (collectionUserId) {
const userIsSharingCollection = await UsersModel.findById(
collectionUserId
);
if (
!userIsSharingCollection ||
!userIsSharingCollection.isPublicCollection
) {
throw new ErrorEvent(
401,
"Cette collection n'est pas publique",
"Cette collection n'est pas publique"
);
}
userId = userIsSharingCollection._id;
}
const count = await AlbumsModel.count({
user: this.req.user._id,
User: userId,
...where,
});
@ -547,7 +584,7 @@ class Albums extends Pages {
const rows = await AlbumsModel.find(
{
user: this.req.user._id,
User: userId,
...where,
},
[],
@ -604,6 +641,7 @@ class Albums extends Pages {
this.setPageContent("artists", artists);
this.setPageContent("formats", formats);
this.setPageTitle("Ma collection");
}
/**
@ -618,6 +656,33 @@ class Albums extends Pages {
});
this.setPageContent("item", item);
this.setPageTitle(
`Détails de l'album ${item.title} de ${item.artists_sort}`
);
}
/**
* Méthode permettant de créer la page "collection/:userId"
*/
async loadPublicCollection() {
const { userId } = this.req.params;
const user = await UsersModel.findById(userId);
if (!user || !user.isPublicCollection) {
throw new ErrorEvent(
401,
"Cet utilisateur ne souhaite pas partager sa collection"
);
}
const artists = await Albums.getAllDistincts("artists_sort", userId);
const formats = await Albums.getAllDistincts("formats.name", userId);
this.setPageContent("username", user.username);
this.setPageTitle(`Collection publique de ${user.username}`);
this.setPageContent("artists", artists);
this.setPageContent("formats", formats);
}
}

45
src/middleware/Me.js Normal file
View File

@ -0,0 +1,45 @@
import Joi from "joi";
import UsersModel from "../models/users";
/**
* Classe permettant la gestion de l'utilisateur connecté
*/
class Me {
constructor(req) {
this.req = req;
}
/**
* Méthode permettant de modifier le profil d'un utilisateur
* @return {Object}
*/
async patchMe() {
const { body, user } = this.req;
const schema = Joi.object({
isPublicCollection: Joi.boolean(),
});
const value = await schema.validateAsync(body);
const update = await UsersModel.findByIdAndUpdate(
user._id,
{ $set: value },
{ new: true }
);
await new Promise((resolve, reject) => {
this.req.login(update, (err) => {
if (err) {
return reject(err);
}
return resolve(null);
});
});
return update;
}
}
export default Me;

View File

@ -34,6 +34,10 @@ class Pages {
}
}
setPageTitle(title) {
this.pageContent.page.title = title;
}
setPageContent(field, value) {
this.pageContent.page[field] = value;
}

View File

@ -1,5 +1,7 @@
/* eslint-disable func-names */
/* eslint-disable no-invalid-this */
/* eslint-disable no-param-reassign */
import mongoose from "mongoose";
import uniqueValidator from "mongoose-unique-validator";
import crypto from "crypto";
@ -23,8 +25,20 @@ const UserSchema = new mongoose.Schema(
},
hash: String,
salt: String,
isPublicCollection: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
{
timestamps: true,
toJSON: {
transform(doc, ret) {
delete ret.hash;
delete ret.salt;
},
},
}
);
UserSchema.plugin(uniqueValidator, { message: "est déjà utilisé" });

View File

@ -9,7 +9,7 @@ const router = express.Router();
router
.route("/")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
.get(async (req, res, next) => {
try {
const albums = new Albums(req);
const data = await albums.getAll();
@ -19,9 +19,11 @@ router
case "csv":
case "musictopus":
res.header("Content-Type", "text/csv");
res.attachment("export-musictopus.csv");
return res.status(200).send(data);
case "xml":
res.header("Content-type", "text/xml");
res.attachment("export-musictopus.xml");
return res.status(200).send(data);
case "xls":
return data.write("musictopus.xls", res);

24
src/routes/api/v1/me.js Normal file
View File

@ -0,0 +1,24 @@
import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import { sendResponse } from "../../../libs/format";
import Me from "../../../middleware/Me";
// eslint-disable-next-line new-cap
const router = express.Router();
router
.route("/")
.patch(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const me = new Me(req);
const data = await me.patchMe();
return sendResponse(req, res, data);
} catch (err) {
return next(err);
}
});
export default router;

22
src/routes/collection.js Normal file
View File

@ -0,0 +1,22 @@
import express from "express";
import Albums from "../middleware/Albums";
import render from "../libs/format";
// eslint-disable-next-line new-cap
const router = express.Router();
router.route("/:userId").get(async (req, res, next) => {
try {
const page = new Albums(req, "collection");
await page.loadPublicCollection();
render(res, page);
} catch (err) {
next(err);
}
});
export default router;

View File

@ -14,6 +14,8 @@ router.route("/").get((req, res, next) => {
try {
const page = new Pages(req, "home");
page.setPageTitle("Présentation du projet");
render(res, page);
} catch (err) {
next(err);
@ -26,6 +28,8 @@ router
try {
const page = new Pages(req, "connexion");
page.setPageTitle("Connexion");
render(res, page);
} catch (err) {
next(err);
@ -61,6 +65,8 @@ router
try {
const page = new Pages(req, "inscription");
page.setPageTitle("Inscription");
render(res, page);
} catch (err) {
next(err);
@ -82,6 +88,8 @@ router
try {
const page = new Pages(req, "ajouter-un-album");
page.setPageTitle("Ajouter un album");
render(res, page);
} catch (err) {
next(err);
@ -92,6 +100,8 @@ router.route("/nous-contacter").get(async (req, res, next) => {
try {
const page = new Pages(req, "nous-contacter");
page.setPageTitle("Nous contacter");
render(res, page);
} catch (err) {
next(err);
@ -102,6 +112,8 @@ router.route("/composants").get(async (req, res, next) => {
try {
const page = new Pages(req, "composants");
page.setPageTitle("Les composants");
render(res, page);
} catch (err) {
next(err);

View File

@ -10,7 +10,7 @@ const router = express.Router();
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const page = new Albums(req, "mon-compte/ma-collection");
const page = new Albums(req, "mon-compte/ma-collection/index");
await page.loadMyCollection();
@ -30,6 +30,8 @@ router
try {
const page = new Albums(req, "mon-compte/ma-collection/exporter");
page.setPageTitle("Exporter ma collection");
render(res, page);
} catch (err) {
next(err);

View File

@ -5,7 +5,9 @@
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
</p>
<% } %>
<% if ( process.env.NODE_ENV !== 'production' ) { %>
<div>
<pre><%= page.error %></pre>
</div>
<% } %>
</main>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><% if (page.title) { %><%= page.title %> <% } else { %> DarKou - MusicTopus <% } %></title>
<title><%= config.siteName %> :: <%= page.title %></title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
@ -19,6 +19,24 @@
<script src="/libs/axios/axios.min.js"></script>
<script src="/libs/vue/vue.global.prod.js"></script>
<script src="/js/main.js"></script>
<% if ( config.matomoUrl ) { %>
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="<%= config.matomoUrl %>";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', <%= config.matomoId %>]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
<% } %>
</head>
<body>
<nav class="navbar" aria-label="Navigation principale">

View File

@ -15,7 +15,7 @@
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 list">
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
<div class="item" v-if="!loading" v-for="item in items">
<a @click="loadDetails(item.id)" class="title">{{ item.artists_sort }} {{ item.title }}</a>
<div class="grid grid-cols-2 md:grid-cols-4">

View File

@ -1,5 +1,8 @@
<main class="layout-maxed ma-collection" id="app">
<h1>Ma collection</h1>
<main class="layout-maxed collection" id="app">
<h1>
Collection de <%= page.username %>
</h1>
<div class="filters">
<div class="field">
<label for="artist">Artiste</label>
@ -40,12 +43,11 @@
<div class="grid grid-cols-1 md:grid-cols-2 list">
<div class="item" v-if="!loading" v-for="item in items">
<span class="title">
<a :href="'/ma-collection/' + item._id">{{ item.artists_sort}} - {{ item.title }}</a>
<i class="icon-trash" @click="showConfirmDelete(item._id)"></i>
{{ item.artists_sort}} - {{ item.title }}
</span>
<div class="grid grid-cols-2 md:grid-cols-4">
<div>
<a :href="'/ma-collection/' + item._id"><img :src="item.thumb" :alt="item.title" /></a>
<img :src="item.thumb" :alt="item.title" />
</div>
<div class="md:col-span-3">
<span><strong>Année :</strong> {{ item.year }}</span>
@ -91,23 +93,14 @@
</template>
</ul>
</nav>
<div class="modal" :class="{'is-visible': showModalDelete}">
<div class="modal-background"></div>
<div class="modal-card">
<header></header>
<section>
Êtes-vous sûr de vouloir supprimer cet album ?
</section>
<footer>
<button class="button is-primary" @click="deleteItem">Supprimer</button>
<button class="button" @click="toggleModal">Annuler</button>
</footer>
</div>
</div>
</main>
<script>
const {
protocol,
host
} = window.location;
Vue.createApp({
data() {
return {
@ -122,8 +115,7 @@
sortOrder: 'artists_sort-asc',
sort: 'artists_sort',
order: 'asc',
itemId: null,
showModalDelete: false,
userId: "<%= params.userId %>",
}
},
created() {
@ -133,7 +125,7 @@
fetch() {
this.loading = true;
let url = `/api/v1/albums?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`;
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}`;
}
@ -149,7 +141,7 @@
})
.catch((err) => {
showToastr(err.response?.data?.message || "Impossible de charger votre collection");
showToastr(err.response?.data?.message || "Impossible de charger cette collection");
})
.finally(() => {
this.loading = false;
@ -187,25 +179,6 @@
this.fetch();
},
toggleModal() {
this.showModalDelete = !this.showModalDelete;
},
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();
});
}
}
}).mount('#app');
</script>

View File

@ -1,8 +1,9 @@
<main class="layout-maxed" id="app">
<main class="layout-maxed composants" id="app">
<h1>Les composants</h1>
<ul>
<li><a href="#titres">Les titres</a></li>
<li><a href="#couleurs">Les couleurs</a></li>
<li><a href="#grilles">Les grilles</a></li>
<li><a href="#boutons">Les boutons</a></li>
<li><a href="#formulaires">Les formulaires</a></li>
@ -24,11 +25,92 @@
<h5>Titre de niveau 5</h5>
<h6>Titre de niveau 6</h6>
<h2 id="couleurs">Les couleurs</h2>
<h3>Polar Night</h3>
<div class="grid grid-cols-5 gap-5">
<div class="couleur">
<div style="background-color: var(--nord0);">&nbsp;</div>
nord0
</div>
<div class="couleur">
<div style="background-color: var(--nord1);">&nbsp;</div>
nord1
</div>
<div class="couleur">
<div style="background-color: var(--nord2);">&nbsp;</div>
nord2
</div>
<div class="couleur">
<div style="background-color: var(--nord3);">&nbsp;</div>
nord3
</div>
</div>
<h3>Snow Storm</h3>
<div class="grid grid-cols-5 gap-5">
<div class="couleur">
<div style="background-color: var(--nord4);">&nbsp;</div>
nord4
</div>
<div class="couleur">
<div style="background-color: var(--nord5);">&nbsp;</div>
nord5
</div>
<div class="couleur">
<div style="background-color: var(--nord6);">&nbsp;</div>
nord6
</div>
</div>
<h3>Frost</h3>
<div class="grid grid-cols-5 gap-5">
<div class="couleur">
<div style="background-color: var(--nord7);">&nbsp;</div>
nord7
</div>
<div class="couleur">
<div style="background-color: var(--nord8);">&nbsp;</div>
nord8
</div>
<div class="couleur">
<div style="background-color: var(--nord9);">&nbsp;</div>
nord9
</div>
<div class="couleur">
<div style="background-color: var(--nord10);">&nbsp;</div>
nord10
</div>
</div>
<h3>Aurora</h3>
<div class="grid grid-cols-5 gap-5">
<div class="couleur">
<div style="background-color: var(--nord11);">&nbsp;</div>
nord11
</div>
<div class="couleur">
<div style="background-color: var(--nord12);">&nbsp;</div>
nord12
</div>
<div class="couleur">
<div style="background-color: var(--nord13);">&nbsp;</div>
nord13
</div>
<div class="couleur">
<div style="background-color: var(--nord14);">&nbsp;</div>
nord14
</div>
<div class="couleur">
<div style="background-color: var(--nord15);">&nbsp;</div>
nord15
</div>
</div>
<p>
Vous pourrez trouver plus d'informations sur le <a href="https://www.nordtheme.com/" target="_blank" rel="noopener noreferrer">site offciel</a> du projet nord.
</p>
<h2 id="grilles">Les grilles</h2>
<p>
Se référer à la documentation de <a href="https://www.knacss.com/doc.html#grid" target="_blank" rel="noopener noreferrer">Knacss</a>.
</p>
<h2 id="boutons">Les boutons</h2>
<div type="button" class="button">.button</div>
<div type="button" class="button is-link">.button.is-link</div>
@ -39,13 +121,13 @@
<h2 id="formulaires">Les formulaires</h2>
<div class="field">
<label for="email">Adresse e-mail</label>
<input type="email" name="email" id="email" placeholder="ex : damien@darkou.fr">
<label for="demo-email">Adresse e-mail</label>
<input type="email" name="email" id="demo-email" placeholder="ex : damien@darkou.fr">
</div>
<pre>
&lt;div class="field"&gt;
&lt;label for="email"&gt;Adresse e-mail&lt;/label&gt;
&lt;input type="email" name="email" id="email" placeholder="ex : damien@darkou.fr"&gt;
&lt;label for="demo-email"&gt;Adresse e-mail&lt;/label&gt;
&lt;input type="email" name="email" id="demo-email" placeholder="ex : damien@darkou.fr"&gt;
&lt;/div&gt;
</pre>
<div class="field">
@ -86,30 +168,38 @@
&lt;/select&gt;
&lt;/div&gt;
</pre>
<div class="field">
<form>
<fieldset>
<div class="field inline">
<label for="choix1">choix 1</label>
<input type="radio" id="choix1" name="choix" value="choix1" checked>
<input type="radio" id="choix1" name="choix" value="choix1" checked>
</div>
<div class="field inline">
<label for="choix2">choix 2</label>
<input type="radio" id="choix2" name="choix" value="choix2">
</div>
</fieldset>
</form>
<pre>
&lt;div class="field"&gt;
&lt;div class="field inline"&gt;
&lt;label for="choix1"&gt;choix 1&lt;/label&gt;
&lt;input type="radio" id="choix1" name="choix" value="choix1" checked&gt;
&lt;input type="radio" id="choix1" name="choix" value="choix1" checked&gt;
&lt;/div&gt;
&lt;div class="field inline"&gt;
&lt;label for="choix2"&gt;choix 2&lt;/label&gt;
&lt;input type="radio" id="choix2" name="choix" value="choix2"&gt;
&lt;/div&gt;
</pre>
<div class="field">
<label for="checkbox1">choix 1</label>
<input type="checkbox" id="checkbox1" name="checkbox" value="checkbox1" checked>
<input type="checkbox" id="checkbox1" name="checkbox" value="checkbox1" checked>
<label for="checkbox2">choix 2</label>
<input type="checkbox" id="checkbox2" name="checkbox" value="checkbox2">
</div>
<pre>
&lt;div class="field"&gt;
&lt;label for="checkbox1"&gt;choix 1&lt;/label&gt;
&lt;input type="checkbox" id="checkbox1" name="checkbox" value="checkbox1" checked&gt;
&lt;input type="checkbox" id="checkbox1" name="checkbox" value="checkbox1" checked&gt;
&lt;label for="checkbox2"&gt;choix 2&lt;/label&gt;
&lt;input type="checkbox" id="checkbox2" name="checkbox" value="checkbox2"&gt;
&lt;/div&gt;
@ -161,11 +251,11 @@
<label for="password">Mot de passe</label>
<input type="password" name="password" id="password" placeholder="********">
</div>
<div class="text-right mt-10">
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
</div>
<button type="submit" class="button is-primary">Connexion</button>
</form>
</div>
@ -196,20 +286,41 @@
</pre>
<h2 id="notifications">Les notifications</h2>
<h3>Erreur</h3>
<button type="button" class="button is-primary" onclick="showToastr('Ceci est une notification');">Afficher une notification</button>
<div id="toastr">
<button class="delete" onclick="hideToastr()" aria-label="Masquer la notification"></button>
<span></span>
</div>
<pre>
&lt;button
&lt;button
type="button"
class="button is-primary"
class="button is-primary"
onclick="showToastr('Ceci est une notification');"
&gt;
Afficher une notification
&lt;/button&gt;
&lt;div id="toastr"&gt;
&lt;button class="delete" onclick="hideToastr()" aria-label="Masquer la notification"&gt;&lt;/button&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/div&gt;
</pre>
<h3>Succès</h3>
<button type="button" class="button is-primary" onclick="showToastr('Ceci est une notification', true);">Afficher une notification</button>
<div id="toastr">
<button class="delete" onclick="hideToastr()" aria-label="Masquer la notification"></button>
<span></span>
</div>
<pre>
&lt;button
type="button"
class="button is-primary"
onclick="showToastr('Ceci est une notification', true);"
&gt;
Afficher une notification
&lt;/button&gt;
&lt;div id="toastr"&gt;
&lt;button class="delete" onclick="hideToastr()" aria-label="Masquer la notification"&gt;&lt;/button&gt;
&lt;span&gt;&lt;/span&gt;
@ -225,15 +336,18 @@
<i class="icon-link-ext">.icon-link-ext</i>
<i class="icon-heart">.icon-heart</i>
<i class="icon-eye">.icon-eye</i>
<i class="icon-left-open">.icon-left-open</i>
<i class="icon-right-open">.icon-right-open</i>
<i class="icon-export">.icon-export</i>
<i class="icon-share">.icon-share</i>
<i class="icon-spin">.icon-spin</i>
<i class="icon-sun">.icon-sun</i>
<i class="icon-moon">.icon-moon</i>
<i class="icon-trash">.icon-trash</i>
<i class="icon-blind">.icon-blind</i>
<i class="icon-left-open">.icon-left-open</i>
<i class="icon-right-open">.icon-right-open</i>
<h2 id="listes">Les listes</h2>
<h3>Liste normale</h3>
<div class="grid grid-cols-1 md:grid-cols-2 list">
<div class="item" v-for="item in items">
<span class="title">
@ -256,6 +370,29 @@
&lt;/div&gt;
&lt;/div&gt;
</pre>
<h3>Liste avec effet au hover</h3>
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
<div class="item" v-for="item in items">
<span class="title">
{{ item.title }}
</span>
<div class="grid grid-cols-2 md:grid-cols-4">
<div>
<img :src="item.thumb" :alt="item.title" />
</div>
<div class="md:col-span-3">
{{ item.lorem }}
</div>
</div>
</div>
</div>
<pre>
&lt;div class="grid grid-cols-1 md:grid-cols-2 list hover"&gt;
&lt;div class="item"&gt;
Contenu
&lt;/div&gt;
&lt;/div&gt;
</pre>
<h2 id="paginations">Les paginations</h2>
<nav class="pagination" role="navigation" aria-label="Pagination">

View File

@ -1,4 +1,4 @@
<main class="layout-maxed ma-collection-exporter" id="app">
<main class="layout-maxed" id="app">
<h1>Exporter ma collection</h1>
<p>
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 :
@ -38,7 +38,7 @@
<input type="radio" name="format" v-model="format" value="musictopus" id="musictopus">
<label for="musictopus">MusicTopus</label>
</div>
<button type="submit" class="button is-primary my-16">
<i class="icon-export"></i>
Exporter

View File

@ -0,0 +1,279 @@
<main class="layout-maxed collection" id="app">
<h1>
Ma collection
<i class="icon-share" @click="toggleModalShare" aria-label="Partager ma collection" title="Votre collection sera visible en lecture aux personnes ayant le lien de partage"></i>
</h1>
<a :href="shareLink" v-if="isPublicCollection" target="_blank">
<i class="icon-share"></i> Voir ma collection partagée
</a>
<div class="filters">
<div class="field">
<label for="artist">Artiste</label>
<select id="artist" v-model="artist" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.artists.length; i += 1 ) {
__append(`<option value="${page.artists[i]}">${page.artists[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="format">Format</label>
<select id="format" v-model="format" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.formats.length; i += 1 ) {
__append(`<option value="${page.formats[i]}">${page.formats[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="sortOrder">Trier par</label>
<select id="sortOrder" v-model="sortOrder" @change="changeSort">
<option value="artists_sort-asc">Artiste (A-Z)</option>
<option value="artists_sort-desc">Artiste (Z-A)</option>
<option value="year-asc">Année (A-Z)</option>
<option value="year-desc">Année (Z-A)</option>
<option value="country-asc">Pays (A-Z)</option>
<option value="country-desc">Pays (Z-A)</option>
<option value="formats.name-asc">Format (A-Z)</option>
<option value="formats.name-desc">Format (Z-A)</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
<div class="item" v-if="!loading" v-for="item in items">
<span class="title">
<a :href="'/ma-collection/' + item._id">{{ item.artists_sort}} - {{ item.title }}</a>
<i class="icon-trash" @click="showConfirmDelete(item._id)"></i>
</span>
<div class="grid grid-cols-2 md:grid-cols-4">
<div>
<a :href="'/ma-collection/' + item._id"><img :src="item.thumb" :alt="item.title" /></a>
</div>
<div class="md:col-span-3">
<span><strong>Année :</strong> {{ item.year }}</span>
<br />
<span><strong>Pays :</strong> {{ item.country }}</span>
<br />
<span>
<strong>Format : </strong>
<span v-for="(format, index) in item.formats">
{{ format.name }}
<template v-if="format.descriptions">
(<template v-for="(description, j) in format.descriptions">
{{description}}<template v-if="j < format.descriptions.length - 1">, </template>
</template>)
</template>
<template v-if="index < item.formats.length - 1">, </template>
</span>
</span>
<br />
<span><strong>Genre :</strong> <template v-for="(genre, index) in item.genres">{{ genre }}<template v-if="index < item.genres.length - 1">, </template></template></span>
<br />
<span><strong>Style :</strong> <template v-for="(style, index) in item.styles">{{ style }}<template v-if="index < item.styles.length - 1">, </template></template></span>
</div>
</div>
</div>
</div>
<div class="total">
<strong>Nombre total d'éléments : </strong>{{total}}
</div>
<nav class="pagination" role="navigation" aria-label="Pagination">
<ul class="pagination-list">
<template v-for="p in Array.from({length: totalPages}, (v, i) => (i+1))">
<template v-if="p < 2 || p > (totalPages - 1) || (page - 1) <= p && page + 1 >= p">
<li>
<a class="pagination-link" :class="{'is-current': p === page}" @click="goTo(p)" aria-label="Aller à la page {{p}}">{{ p }}</a>
</li>
</template>
<template v-if="(page - 3 === p && page - 2 > 1) || (page + 2 === p && page + 2 < totalPages - 1)">
<li>
<a class="pagination-link is-disabled">…</a>
</li>
</template>
</template>
</ul>
</nav>
<div class="modal" :class="{'is-visible': showModalDelete}">
<div class="modal-background"></div>
<div class="modal-card">
<header></header>
<section>
Êtes-vous sûr de vouloir supprimer cet album ?
</section>
<footer>
<button class="button is-primary" @click="deleteItem">Supprimer</button>
<button class="button" @click="toggleModal">Annuler</button>
</footer>
</div>
</div>
<div class="modal" :class="{'is-visible': showModalShare}">
<div class="modal-background"></div>
<div class="modal-card">
<header>
Partager ma collection
</header>
<section>
<template v-if="!isPublicCollection">
Votre collection sera visible de toute personne disposant du lien suivant :
<br />
<a :href="shareLink" target="_blank">{{shareLink}}</a>
<br />
Ce lien permet uniquement de visualiser l'ensemble de votre collection mais ne perment <strong class="is-danger">en aucun cas</strong> de la modifier.
<br />
Vous pourrez à tout moment supprimer le lien de partage en cliquant à nouveau sur l'icône <i class="icon-share"></i> sur votre collection.
</template>
<template v-if="isPublicCollection">
Vous êtes sur le point de rendre votre collection privée.
<br />
Toute les personnes ayant le lien partagé ne pourront plus accéder à votre collection.
<br />
Vous pourrez à tout moment rendre à nouveau votre collection publique en cliquant sur l'icône <i class="icon-share"></i>.
</template>
</section>
<footer>
<button v-if="!isPublicCollection" class="button is-primary" @click="shareCollection">Partager</button>
<button v-if="isPublicCollection" class="button is-danger" @click="shareCollection">Supprimer</button>
<button class="button" @click="toggleModalShare">Annuler</button>
</footer>
</div>
</div>
</main>
<script>
const {
protocol,
host
} = window.location;
Vue.createApp({
data() {
return {
loading: false,
items: [],
total: 0,
page: 1,
totalPages: 1,
limit: 16,
artist: '',
format: '',
sortOrder: 'artists_sort-asc',
sort: 'artists_sort',
order: 'asc',
itemId: null,
showModalDelete: false,
showModalShare: false,
shareLink: `${protocol}//${host}/collection/<%= user._id %>`,
isPublicCollection: <%= user.isPublicCollection ? 'true' : 'false' %>,
}
},
created() {
this.fetch();
},
methods: {
fetch() {
this.loading = true;
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}`;
}
if ( this.format ) {
url += `&format=${this.format}`;
}
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 votre collection");
})
.finally(() => {
this.loading = false;
});
},
next(event) {
event.preventDefault();
this.page += 1;
this.fetch();
},
previous(event) {
event.preventDefault();
this.page -= 1;
this.fetch();
},
goTo(page) {
this.page = page;
this.fetch();
},
changeSort() {
const [sort,order] = this.sortOrder.split('-');
this.sort = sort;
this.order = order;
this.page = 1;
this.fetch();
},
changeFilter() {
this.page = 1;
this.fetch();
},
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('#app');
</script>