Compare commits
No commits in common. "master" and "v1.0" have entirely different histories.
77
.eslintrc.js
77
.eslintrc.js
|
@ -1,49 +1,36 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2020: true,
|
es2020: true,
|
||||||
node: true,
|
node: true,
|
||||||
jquery: true,
|
jquery: true,
|
||||||
},
|
},
|
||||||
extends: ["airbnb-base", "prettier"],
|
extends: ['airbnb-base', 'prettier'],
|
||||||
plugins: ["prettier"],
|
plugins: ['prettier'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 11,
|
ecmaVersion: 11,
|
||||||
sourceType: "module",
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"prettier/prettier": ["error"],
|
'prettier/prettier': ['error'],
|
||||||
"no-underscore-dangle": [
|
'no-underscore-dangle': [
|
||||||
"error",
|
'error',
|
||||||
{
|
{
|
||||||
allow: ["_id", "artists_sort", "type_"],
|
allow: ['_id', 'artists_sort', 'type_'],
|
||||||
},
|
},
|
||||||
],
|
|
||||||
camelcase: [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
allow: [
|
|
||||||
"artists_sort",
|
|
||||||
"access_token",
|
|
||||||
"api_url",
|
|
||||||
"media_ids",
|
|
||||||
"release_id",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
ignorePatterns: ["public/libs/**/*.js", "public/js/main.js", "dist/**"],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["**/*.js"],
|
|
||||||
excludedFiles: "*.ejs",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
globals: {
|
'camelcase': [
|
||||||
Vue: true,
|
'error',
|
||||||
axios: true,
|
{
|
||||||
showToastr: true,
|
allow: ['artists_sort',]
|
||||||
protocol: true,
|
},
|
||||||
host: true,
|
],
|
||||||
|
},
|
||||||
|
ignorePatterns: ['public/libs/**/*.js', 'public/js/main.js', 'dist/**'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['**/*.js'],
|
||||||
|
excludedFiles: '*.ejs',
|
||||||
},
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,6 +121,6 @@ dist
|
||||||
dist
|
dist
|
||||||
yarn.lock
|
yarn.lock
|
||||||
public/css
|
public/css
|
||||||
public/js
|
public/css
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
dump
|
dump
|
||||||
|
|
53
README.md
53
README.md
|
@ -18,9 +18,7 @@ Vous pouvez, si vous le souhaitez héberger l'application sur votre propre serve
|
||||||
|
|
||||||
### Prérequis
|
### Prérequis
|
||||||
|
|
||||||
Il existe 2 méthodes d'installation, soit via docker soit en mode standalone.
|
Il existe 2 méthodes d'installation, soit via docker soit en mode standalone. Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle.
|
||||||
|
|
||||||
Peu importe la méthode il vous faudra un compte sur [https://formspree.io/](https://formspree.io/) afin d'avoir une page nous-contacter fonctionnelle ou configurer le SMTP tel que défini dans la section [variables d'environnements](#env-file).
|
|
||||||
|
|
||||||
Pour la méthode docker il ne vous faudra rien de plus que `docker` et `docker-compose`.
|
Pour la méthode docker il ne vous faudra rien de plus que `docker` et `docker-compose`.
|
||||||
|
|
||||||
|
@ -64,7 +62,7 @@ Le site est accessible sur [http://localhost:PORT](http://localhost:PORT).
|
||||||
|
|
||||||
#### Standalone
|
#### Standalone
|
||||||
|
|
||||||
Pour la version standalone je vous conseille de faire un script embarquant les variables d'environnement que vous souhaitez modifier ([voir à la fin pour la liste des variables](#env-file)) :
|
Pour la version standalone je vous conseille de faire un script embarquant les variables d'environnement que vous souhaitez modifier :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
@ -92,7 +90,7 @@ C'est terminé !
|
||||||
|
|
||||||
Le site est accessible sur [http://localhost:3001](http://localhost:3001).
|
Le site est accessible sur [http://localhost:3001](http://localhost:3001).
|
||||||
|
|
||||||
:information_source: Information : Vous pouvez, et vous devriez, également regarder du côté de `systemd`, `pm2` ou encore `supervisor` pour que le service démarre en même temps que votre serveur.
|
:information_source: Information : Vous pouvez, et vous dreviez, également regarder du côté de `systemd`, `pm2` ou encore `supervisor` pour que le service démarre en même temps que votre serveur.
|
||||||
|
|
||||||
### Aller plus loin
|
### Aller plus loin
|
||||||
|
|
||||||
|
@ -186,59 +184,24 @@ server {
|
||||||
|
|
||||||
Une fois le vhost activé (lien symbolique dans le dossier site-enable) et nginx rechargé votre site sera alors accessible en https.
|
Une fois le vhost activé (lien symbolique dans le dossier site-enable) et nginx rechargé votre site sera alors accessible en https.
|
||||||
|
|
||||||
### Jobs
|
|
||||||
|
|
||||||
Par défaut toute les images des albums sont affichées depuis Discogs. Cependant avec les temps les urls deviennent invalides. Pour éviter cela lors de l'ajout d'un album à votre collection un job est créé. Ce job a pour rôle de stocker les images sur un bucket s3.
|
|
||||||
|
|
||||||
Pour lancer les jobs il faut mettre en place une tâche cron qui sera éxécutée toute les heures (par exemple).
|
|
||||||
|
|
||||||
Exemple de crontab :
|
|
||||||
```crontab
|
|
||||||
0 * * * * curl 'http://localhost:3001/jobs' \
|
|
||||||
-H 'JOBS_HEADER_KEY: JOBS_HEADER_VALUE' \
|
|
||||||
-H 'Accept: application/json'
|
|
||||||
30 * * * * curl 'http://localhost:3001/jobs?state=ERROR' \
|
|
||||||
-H 'JOBS_HEADER_KEY: JOBS_HEADER_VALUE' \
|
|
||||||
-H 'Accept: application/json'
|
|
||||||
```
|
|
||||||
|
|
||||||
N'oubliez pas de remplacer `localhost:30001`, `JOBS_HEADER_KEY` et `JOBS_HEADER_VALUE` par les bonnes valeurs.
|
|
||||||
|
|
||||||
La première ligne permet de parcourir tous les nouveaux jobs alors que la seconde permet de relancer les jobs en erreurs (après 5 tentatives le job est marqué comme définitivement perdu).
|
|
||||||
|
|
||||||
### Fichier .env {#env-file}
|
### Fichier .env {#env-file}
|
||||||
|
|
||||||
Voici la liste des variables configurables :
|
Voici la liste des variables configurables :
|
||||||
|
|
||||||
```
|
```
|
||||||
NODE_ENV # Environnement dans lequel exécuter le projet (development ou production)
|
NODE_ENV # Environnement dans lequel exécuter le projet (development ou production)
|
||||||
PORT # Port sur lequel éxécuter le serveur (3001 par défaut)
|
PORT # Port sur lequel éxécuter le serveur (par défaut 3001)
|
||||||
MONGODB_URI # Url du serveur mongo (mongodb://musictopus-db/musictopus par défaut)
|
MONGODB_URI # Url du serveur mongo (par défaut mongodb://musictopus-db/musictopus)
|
||||||
SECRET # Hash utilisé pour pour sauvegardé les dessions (waemaeMe5ahc6ce1chaeKohKa6Io8Eik par défault)
|
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)
|
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"
|
FORMSPREE_ID # Id du formulaire formspree pour la page "nous-contacter"
|
||||||
MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/)
|
MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/)
|
||||||
MATOMO_ID # Id du site sur votre instance matomo (exemple: 1)
|
MATOMO_ID # Id du site sur votre instance matomo (exemple: 1)
|
||||||
SITE_NAME # Nom du site utilisé dans le titre des pages (MusicTopus par défaut)
|
SITE_NAME # Nom du site (utilisé dans le titre des pages)
|
||||||
AWS_ACCESS_KEY_ID # Clé d'accès AWS
|
|
||||||
AWS_SECRET_ACCESS_KEY # Clé secrète AWS
|
|
||||||
S3_ENDPOINT # Url de l'instance aws (s3.fr-par.scw.cloud par défaut)
|
|
||||||
S3_SIGNATURE # Version de la signature AWS (s3v4 par défaut)
|
|
||||||
S3_BASEFOLDER # Nom du sous dossier dans lequel seront mis les pochettes des albums (dev par défaut)
|
|
||||||
S3_BUCKET # Nom du bucket (musictopus par défaut, à changer impérativement si vous voulez que cela fonctionne)
|
|
||||||
JOBS_HEADER_KEY # Nom du header utilisé pour l'identification des tâches cron (musictopus par défaut)
|
|
||||||
JOBS_HEADER_VALUE # Valeur de la clé (ooYee9xok7eigo2shiePohyoGh1eepew par défaut)
|
|
||||||
REGISTRATION_OPEN # true/false en fonction de si vous souhaitez activer ou non l'inscription à votre instance (true par défaut)
|
|
||||||
MAIL_METHOD # permet de définir la façon dont les mails de la page contact sont envoyés (formspree ou smtp)
|
|
||||||
MAIL_HOST # Adresse du server mail (dams le cas ou MAIL_METHOD est défini sur smtp)
|
|
||||||
MAIL_PORT # Port d'écoute du serveur smtp (dams le cas ou MAIL_METHOD est défini sur smtp)
|
|
||||||
MAIL_USER # Adresse mail du compte permettant d'envoyer les mails (dams le cas ou MAIL_METHOD est défini sur smtp)
|
|
||||||
MAIL_PASSWORD # Mot de passe du compte email (dams le cas ou MAIL_METHOD est défini sur smtp)
|
|
||||||
MAIL_TO # Adresse mail du contact qui recevra les messages de la page "nous contacter" (dams le cas ou MAIL_METHOD est défini sur smtp)
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributeurs
|
## Contributeurs
|
||||||
|
|
||||||
- Damien Broqua (développeur principal du projet)
|
- Damien Broqua (développeur principal du projet)
|
||||||
- Brunus (Logo et fournisseur d'idées :wink: )
|
- Brunus (Logo et fournisseur d'idées :wink: )
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: "2.4"
|
||||||
services:
|
services:
|
||||||
musictopus-www:
|
musictopus-www:
|
||||||
container_name: musictopus-www
|
container_name: musictopus-www
|
||||||
image: "node:18"
|
image: "node:16"
|
||||||
restart: always
|
restart: always
|
||||||
user: "node"
|
user: "node"
|
||||||
working_dir: /home/node/app
|
working_dir: /home/node/app
|
||||||
|
@ -28,21 +28,6 @@ services:
|
||||||
MATOMO_URL: ${MATOMO_URL}
|
MATOMO_URL: ${MATOMO_URL}
|
||||||
MATOMO_ID: ${MATOMO_ID}
|
MATOMO_ID: ${MATOMO_ID}
|
||||||
SITE_NAME: ${SITE_NAME}
|
SITE_NAME: ${SITE_NAME}
|
||||||
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
|
|
||||||
S3_BASEFOLDER: ${S3_BASEFOLDER}
|
|
||||||
S3_BUCKET: ${S3_BUCKET}
|
|
||||||
S3_ENDPOINT: ${S3_ENDPOINT}
|
|
||||||
S3_SIGNATURE: ${S3_SIGNATURE}
|
|
||||||
JOBS_HEADER_KEY: ${JOBS_HEADER_KEY}
|
|
||||||
JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE}
|
|
||||||
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
|
|
||||||
MAIL_METHOD: ${MAIL_METHOD}
|
|
||||||
MAIL_HOST: ${MAIL_HOST}
|
|
||||||
MAIL_PORT: ${MAIL_PORT}
|
|
||||||
MAIL_USER: ${MAIL_USER}
|
|
||||||
MAIL_PASSWORD: ${MAIL_PASSWORD}
|
|
||||||
MAIL_TO: ${MAIL_TO}
|
|
||||||
networks:
|
networks:
|
||||||
- musictopus
|
- musictopus
|
||||||
musictopus-db:
|
musictopus-db:
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: "2.4"
|
||||||
services:
|
services:
|
||||||
musictopus-www:
|
musictopus-www:
|
||||||
container_name: musictopus-www
|
container_name: musictopus-www
|
||||||
image: "node:18"
|
image: "node:16"
|
||||||
restart: always
|
restart: always
|
||||||
user: "node"
|
user: "node"
|
||||||
working_dir: /home/node/app
|
working_dir: /home/node/app
|
||||||
|
@ -28,15 +28,6 @@ services:
|
||||||
MATOMO_URL: ${MATOMO_URL}
|
MATOMO_URL: ${MATOMO_URL}
|
||||||
MATOMO_ID: ${MATOMO_ID}
|
MATOMO_ID: ${MATOMO_ID}
|
||||||
SITE_NAME: ${SITE_NAME}
|
SITE_NAME: ${SITE_NAME}
|
||||||
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
|
|
||||||
S3_BASEFOLDER: ${S3_BASEFOLDER}
|
|
||||||
S3_BUCKET: ${S3_BUCKET}
|
|
||||||
S3_ENDPOINT: ${S3_ENDPOINT}
|
|
||||||
S3_SIGNATURE: ${S3_SIGNATURE}
|
|
||||||
JOBS_HEADER_KEY: ${JOBS_HEADER_KEY}
|
|
||||||
JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE}
|
|
||||||
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
|
|
||||||
networks:
|
networks:
|
||||||
- musictopus
|
- musictopus
|
||||||
musictopus-db:
|
musictopus-db:
|
||||||
|
|
124
fontello.json
124
fontello.json
|
@ -1,124 +0,0 @@
|
||||||
{
|
|
||||||
"name": "icon",
|
|
||||||
"css_prefix_text": "icon-",
|
|
||||||
"css_use_suffix": false,
|
|
||||||
"hinting": true,
|
|
||||||
"units_per_em": 1000,
|
|
||||||
"ascent": 850,
|
|
||||||
"glyphs": [
|
|
||||||
{
|
|
||||||
"uid": "ca90da02d2c6a3183f2458e4dc416285",
|
|
||||||
"css": "adjust",
|
|
||||||
"code": 59408,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "44e04715aecbca7f266a17d5a7863c68",
|
|
||||||
"css": "plus",
|
|
||||||
"code": 59392,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "8b80d36d4ef43889db10bc1f0dc9a862",
|
|
||||||
"css": "user",
|
|
||||||
"code": 59393,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "9dd9e835aebe1060ba7190ad2b2ed951",
|
|
||||||
"css": "search",
|
|
||||||
"code": 59394,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "bf882b30900da12fca090d9796bc3030",
|
|
||||||
"css": "mail",
|
|
||||||
"code": 59395,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
|
|
||||||
"css": "link",
|
|
||||||
"code": 59396,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "e15f0d620a7897e2035c18c80142f6d9",
|
|
||||||
"css": "link-ext",
|
|
||||||
"code": 61582,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "9bc2902722abb366a213a052ade360bc",
|
|
||||||
"css": "spin",
|
|
||||||
"code": 59449,
|
|
||||||
"src": "fontelico"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "bbfb51903f40597f0b70fd75bc7b5cac",
|
|
||||||
"css": "trash",
|
|
||||||
"code": 61944,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "d73eceadda1f594cec0536087539afbf",
|
|
||||||
"css": "heart",
|
|
||||||
"code": 59397,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "cce5e05853d0798a4d10077ef613387c",
|
|
||||||
"css": "blind",
|
|
||||||
"code": 62109,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "567e3e257f2cc8fba2c12bf691c9f2d8",
|
|
||||||
"css": "moon",
|
|
||||||
"code": 61830,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "aa035df0908c4665c269b7b09a5596f3",
|
|
||||||
"css": "sun",
|
|
||||||
"code": 61829,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "c5fd349cbd3d23e4ade333789c29c729",
|
|
||||||
"css": "eye",
|
|
||||||
"code": 59398,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "d870630ff8f81e6de3958ecaeac532f2",
|
|
||||||
"css": "left-open",
|
|
||||||
"code": 59399,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "399ef63b1e23ab1b761dfbb5591fa4da",
|
|
||||||
"css": "right-open",
|
|
||||||
"code": 59400,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "895405dfac8a3b7b2f23b183c6608ee6",
|
|
||||||
"css": "export",
|
|
||||||
"code": 59401,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "4aad6bb50b02c18508aae9cbe14e784e",
|
|
||||||
"css": "share",
|
|
||||||
"code": 61920,
|
|
||||||
"src": "fontawesome"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "a73c5deb486c8d66249811642e5d719a",
|
|
||||||
"css": "refresh",
|
|
||||||
"code": 59402,
|
|
||||||
"src": "fontawesome"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
47
gulpfile.js
47
gulpfile.js
|
@ -1,47 +0,0 @@
|
||||||
const { parallel, src, dest } = require("gulp");
|
|
||||||
|
|
||||||
const sourcemaps = require("gulp-sourcemaps");
|
|
||||||
const concat = require("gulp-concat");
|
|
||||||
|
|
||||||
const gulp = require("gulp");
|
|
||||||
const uglify = require("gulp-uglify");
|
|
||||||
const babel = require("gulp-babel");
|
|
||||||
|
|
||||||
const sourceJs = "javascripts/**/*.js";
|
|
||||||
const sourceRemoteJS = [
|
|
||||||
"./node_modules/vue/dist/vue.global.prod.js",
|
|
||||||
"./node_modules/chart.js/dist/chart.umd.js",
|
|
||||||
"./node_modules/axios/dist/axios.min.js",
|
|
||||||
];
|
|
||||||
|
|
||||||
const destination = "public/js";
|
|
||||||
|
|
||||||
// TASKS ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
const compileJs = function () {
|
|
||||||
return gulp
|
|
||||||
.src(sourceJs)
|
|
||||||
.pipe(sourcemaps.init())
|
|
||||||
.pipe(concat("main.js"))
|
|
||||||
.pipe(
|
|
||||||
babel({
|
|
||||||
presets: ["@babel/env"],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(sourcemaps.write("."))
|
|
||||||
.pipe(gulp.dest(destination));
|
|
||||||
};
|
|
||||||
const compileRemoteJs = function () {
|
|
||||||
return gulp
|
|
||||||
.src(sourceRemoteJS)
|
|
||||||
.pipe(sourcemaps.init())
|
|
||||||
.pipe(concat("libs.js"))
|
|
||||||
.pipe(sourcemaps.write("."))
|
|
||||||
.pipe(gulp.dest(destination));
|
|
||||||
};
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// COMMANDS -------------------------------------------------------------------
|
|
||||||
exports.default = parallel(compileJs, compileRemoteJs);
|
|
||||||
// ----------------------------------------------------------------------------
|
|
|
@ -1,207 +0,0 @@
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
share: canPublish,
|
|
||||||
q: "",
|
|
||||||
year: "",
|
|
||||||
country: "",
|
|
||||||
format: "",
|
|
||||||
loading: false,
|
|
||||||
items: [],
|
|
||||||
details: {},
|
|
||||||
modalIsVisible: false,
|
|
||||||
submitting: false,
|
|
||||||
formats: [
|
|
||||||
"Vinyl",
|
|
||||||
"Acetate",
|
|
||||||
"Flexi-disc",
|
|
||||||
"Lathe Cut",
|
|
||||||
"Mighty Tiny",
|
|
||||||
"Shellac",
|
|
||||||
"Sopic",
|
|
||||||
"Pathé Disc",
|
|
||||||
"Edison Disc",
|
|
||||||
"Cylinder",
|
|
||||||
"CD",
|
|
||||||
"CDr",
|
|
||||||
"CDV",
|
|
||||||
"DVD",
|
|
||||||
"DVDr",
|
|
||||||
"HD DVD",
|
|
||||||
"HD DVD-R",
|
|
||||||
"Blu-ray",
|
|
||||||
"Blu-ray-R",
|
|
||||||
"Ultra HD Blu-ray",
|
|
||||||
"SACD",
|
|
||||||
"4-Track Cartridge",
|
|
||||||
"8-Track Cartridge",
|
|
||||||
"Cassette",
|
|
||||||
"DC-International",
|
|
||||||
"Elcaset",
|
|
||||||
"PlayTape",
|
|
||||||
"RCA Tape Cartridge",
|
|
||||||
"DAT",
|
|
||||||
"DCC",
|
|
||||||
"Microcassette",
|
|
||||||
"NT Cassette",
|
|
||||||
"Pocket Rocker",
|
|
||||||
"Revere Magnetic Stereo Tape Ca",
|
|
||||||
"Tefifon",
|
|
||||||
"Reel-To-Reel",
|
|
||||||
"Sabamobil",
|
|
||||||
"Betacam",
|
|
||||||
"Betacam SP",
|
|
||||||
"Betamax",
|
|
||||||
"Cartrivision",
|
|
||||||
"MiniDV",
|
|
||||||
"Super VHS",
|
|
||||||
"U-matic",
|
|
||||||
"VHS",
|
|
||||||
"Video 2000",
|
|
||||||
"Video8",
|
|
||||||
"Film Reel",
|
|
||||||
"HitClips",
|
|
||||||
"Laserdisc",
|
|
||||||
"SelectaVision",
|
|
||||||
"VHD",
|
|
||||||
"Wire Recording",
|
|
||||||
"Minidisc",
|
|
||||||
"MVD",
|
|
||||||
"UMD",
|
|
||||||
"Floppy Disk",
|
|
||||||
"File",
|
|
||||||
"Memory Stick",
|
|
||||||
"Hybrid",
|
|
||||||
"All Media",
|
|
||||||
"Box Set",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
window.addEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
window.removeEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
search(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (this.loading) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
let url = `/api/v1/search?q=${this.q}`;
|
|
||||||
|
|
||||||
if (this.year) {
|
|
||||||
url += `&year=${this.year}`;
|
|
||||||
}
|
|
||||||
if (this.country) {
|
|
||||||
url += `&country=${this.country}`;
|
|
||||||
}
|
|
||||||
if (this.format) {
|
|
||||||
url += `&format=${this.format}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(url)
|
|
||||||
.then((response) => {
|
|
||||||
const { results } = response.data;
|
|
||||||
const items = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i += 1) {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
thumb,
|
|
||||||
year,
|
|
||||||
country,
|
|
||||||
format,
|
|
||||||
genre,
|
|
||||||
style,
|
|
||||||
inCollection,
|
|
||||||
} = results[i];
|
|
||||||
items.push({
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
thumb,
|
|
||||||
year,
|
|
||||||
country,
|
|
||||||
format,
|
|
||||||
genre,
|
|
||||||
style,
|
|
||||||
inCollection,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.items = items;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Aucun résultat trouvé :/"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toggleModal() {
|
|
||||||
this.modalIsVisible = !this.modalIsVisible;
|
|
||||||
},
|
|
||||||
loadDetails(discogsId) {
|
|
||||||
axios
|
|
||||||
.get(`/api/v1/search/${discogsId}`)
|
|
||||||
.then((response) => {
|
|
||||||
const { data } = response;
|
|
||||||
|
|
||||||
this.details = data;
|
|
||||||
this.toggleModal();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de charger les détails de cet album"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
add() {
|
|
||||||
if (this.submitting) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.submitting = true;
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.post("/api/v1/albums", {
|
|
||||||
album: this.details,
|
|
||||||
share: this.share,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
window.location.href = "/ma-collection";
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.submitting = false;
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible d'ajouter cet album pour le moment…"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
orderedItems(items) {
|
|
||||||
return items.sort();
|
|
||||||
},
|
|
||||||
keyDown(event) {
|
|
||||||
const keycode = event.code;
|
|
||||||
|
|
||||||
if (this.modalIsVisible && keycode === "Escape") {
|
|
||||||
event.preventDefault();
|
|
||||||
this.modalIsVisible = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#ajouter-album");
|
|
|
@ -1,261 +0,0 @@
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
moreFilters: false,
|
|
||||||
items: [],
|
|
||||||
total: 0,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
page: query.page || 1,
|
|
||||||
limit: 16,
|
|
||||||
totalPages: 1,
|
|
||||||
artist: "",
|
|
||||||
format: "",
|
|
||||||
year: "",
|
|
||||||
genre: "",
|
|
||||||
style: "",
|
|
||||||
sortOrder: "artists_sort-asc",
|
|
||||||
sort: "artists_sort",
|
|
||||||
order: "asc",
|
|
||||||
itemId: null,
|
|
||||||
showModalDelete: false,
|
|
||||||
showModalShare: false,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
shareLink: `/collection/${userId}`,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
isPublicCollection,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
userId,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
vueType,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
query,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fetch();
|
|
||||||
|
|
||||||
window.addEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
window.removeEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
formatParams(param) {
|
|
||||||
return param.replace("&", "%26").replace("+", "%2B");
|
|
||||||
},
|
|
||||||
fetch() {
|
|
||||||
this.loading = true;
|
|
||||||
this.total = 0;
|
|
||||||
|
|
||||||
const queryString = window.location.search;
|
|
||||||
const urlParams = new URLSearchParams(queryString);
|
|
||||||
const entries = urlParams.entries();
|
|
||||||
|
|
||||||
const sortOrder = {
|
|
||||||
sort: "artists_sort",
|
|
||||||
order: "asc",
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
for (const entry of entries) {
|
|
||||||
const [key, value] = entry;
|
|
||||||
switch (key) {
|
|
||||||
case "artists_sort":
|
|
||||||
this.artist = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (["order", "sort"].indexOf(key) !== -1) {
|
|
||||||
sortOrder[key] = value;
|
|
||||||
}
|
|
||||||
this[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sortOrder = `${sortOrder.sort}-${sortOrder.order}`;
|
|
||||||
|
|
||||||
let url = `/api/v1/albums?page=${this.page}&sort=${this.sort}&order=${this.order}`;
|
|
||||||
if (this.artist) {
|
|
||||||
url += `&artist=${this.formatParams(this.artist)}`;
|
|
||||||
}
|
|
||||||
if (this.format) {
|
|
||||||
url += `&format=${this.formatParams(this.format)}`;
|
|
||||||
}
|
|
||||||
if (this.year) {
|
|
||||||
url += `&year=${this.year}`;
|
|
||||||
}
|
|
||||||
if (this.genre) {
|
|
||||||
url += `&genre=${this.formatParams(this.genre)}`;
|
|
||||||
}
|
|
||||||
if (this.style) {
|
|
||||||
url += `&style=${this.formatParams(this.style)}`;
|
|
||||||
}
|
|
||||||
// INFO: Cas d'une collection partagée
|
|
||||||
if (this.vueType === "public" && this.userId) {
|
|
||||||
url += `&userId=${this.userId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
axios
|
|
||||||
.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) +
|
|
||||||
(response.data.count % this.limit > 0 ? 1 : 0);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de charger votre collection"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
changeUrl() {
|
|
||||||
let url = `?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`;
|
|
||||||
if (this.artist) {
|
|
||||||
url += `&artists_sort=${this.formatParams(this.artist)}`;
|
|
||||||
}
|
|
||||||
if (this.format) {
|
|
||||||
url += `&format=${this.formatParams(this.format)}`;
|
|
||||||
}
|
|
||||||
if (this.year) {
|
|
||||||
url += `&year=${this.year}`;
|
|
||||||
}
|
|
||||||
if (this.genre) {
|
|
||||||
url += `&genre=${this.formatParams(this.genre)}`;
|
|
||||||
}
|
|
||||||
if (this.style) {
|
|
||||||
url += `&style=${this.formatParams(this.style)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = url;
|
|
||||||
},
|
|
||||||
next(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.page += 1;
|
|
||||||
|
|
||||||
this.changeUrl();
|
|
||||||
},
|
|
||||||
previous(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.page -= 1;
|
|
||||||
|
|
||||||
this.changeUrl();
|
|
||||||
},
|
|
||||||
goTo(page) {
|
|
||||||
this.page = page;
|
|
||||||
|
|
||||||
this.changeUrl();
|
|
||||||
},
|
|
||||||
changeSort() {
|
|
||||||
const [sort, order] = this.sortOrder.split("-");
|
|
||||||
this.sort = sort;
|
|
||||||
this.order = order;
|
|
||||||
this.page = 1;
|
|
||||||
|
|
||||||
this.changeUrl();
|
|
||||||
},
|
|
||||||
changeFilter() {
|
|
||||||
this.page = 1;
|
|
||||||
|
|
||||||
this.changeUrl();
|
|
||||||
},
|
|
||||||
showMoreFilters() {
|
|
||||||
this.moreFilters = !this.moreFilters;
|
|
||||||
},
|
|
||||||
toggleModal() {
|
|
||||||
this.showModalDelete = !this.showModalDelete;
|
|
||||||
},
|
|
||||||
toggleModalShare() {
|
|
||||||
this.showModalShare = !this.showModalShare;
|
|
||||||
},
|
|
||||||
showConfirmDelete(itemId) {
|
|
||||||
this.itemId = itemId;
|
|
||||||
this.toggleModal();
|
|
||||||
},
|
|
||||||
deleteItem() {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
if (vueType !== "private") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return 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() {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
if (vueType !== "private") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return 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();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
renderAlbumTitle(item) {
|
|
||||||
let render = "";
|
|
||||||
|
|
||||||
for (let i = 0; i < item.artists.length; i += 1) {
|
|
||||||
const { name, join } = item.artists[i];
|
|
||||||
render += `${name} ${join ? `${join} ` : ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
render += `- ${item.title}`;
|
|
||||||
|
|
||||||
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,43 +0,0 @@
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
if (typeof contactMethod !== "undefined" && contactMethod === "smtp") {
|
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
email: "",
|
|
||||||
name: "",
|
|
||||||
message: "",
|
|
||||||
captcha: "",
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
send(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (this.loading) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
const { email, message, name, captcha } = this;
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.post("/api/v1/contact", { email, name, message, captcha })
|
|
||||||
.then(() => {
|
|
||||||
showToastr("Message correctement envoyé", true);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible d'envoyer votre message",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#contact");
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/* eslint-disable no-unused-vars */
|
|
||||||
const { protocol, host } = window.location;
|
|
||||||
|
|
||||||
let timeout = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fonction permettant d'afficher un message dans un toastr
|
|
||||||
* @param {String} message
|
|
||||||
*/
|
|
||||||
function showToastr(message, success = false) {
|
|
||||||
const x = document.getElementById("toastr");
|
|
||||||
if (message) {
|
|
||||||
x.getElementsByTagName("SPAN")[0].innerHTML = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
x.classList.remove("show");
|
|
||||||
}
|
|
||||||
|
|
||||||
x.classList.remove("success");
|
|
||||||
x.classList.remove("error");
|
|
||||||
if (success) {
|
|
||||||
x.classList.add("success");
|
|
||||||
} else {
|
|
||||||
x.classList.add("error");
|
|
||||||
}
|
|
||||||
|
|
||||||
x.classList.add("show");
|
|
||||||
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
x.classList.remove("show");
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fonction permettant de masquer le toastr
|
|
||||||
*/
|
|
||||||
function hideToastr() {
|
|
||||||
const x = document.getElementById("toastr");
|
|
||||||
|
|
||||||
x.className = x.className.replace("show", "");
|
|
||||||
x.getElementsByTagName("SPAN")[0].innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensemble d'actions effectuées au chargement de la page
|
|
||||||
*/
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
const $navbarBurgers = Array.prototype.slice.call(
|
|
||||||
document.querySelectorAll(".navbar-burger"),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
if ($navbarBurgers.length > 0) {
|
|
||||||
$navbarBurgers.forEach((el) => {
|
|
||||||
el.addEventListener("click", () => {
|
|
||||||
const { target } = el.dataset;
|
|
||||||
const $target = document.getElementById(target);
|
|
||||||
|
|
||||||
el.classList.toggle("is-active");
|
|
||||||
$target.classList.toggle("is-active");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,106 +0,0 @@
|
||||||
if (typeof email !== "undefined" && typeof username !== "undefined") {
|
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
formData: {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
email,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
username,
|
|
||||||
oldPassword: "",
|
|
||||||
password: "",
|
|
||||||
passwordConfirm: "",
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
pagination,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
mastodon: mastodon || {
|
|
||||||
publish: false,
|
|
||||||
url: "",
|
|
||||||
token: "",
|
|
||||||
message:
|
|
||||||
"Je viens d'ajouter {artist} - {album} à ma collection !",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loading: false,
|
|
||||||
errors: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
async testMastodon() {
|
|
||||||
const { url, token } = this.formData.mastodon;
|
|
||||||
|
|
||||||
if (!url) {
|
|
||||||
this.errors.push("emptyUrl");
|
|
||||||
}
|
|
||||||
if (!token) {
|
|
||||||
this.errors.push("emptyToken");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.errors.length > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.post(`/api/v1/mastodon`, { url, token });
|
|
||||||
|
|
||||||
showToastr("Configuration valide !", true);
|
|
||||||
} catch (err) {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de tester cette configuration",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
async updateProfil() {
|
|
||||||
this.errors = [];
|
|
||||||
const { oldPassword, password, passwordConfirm, mastodon, pagination } =
|
|
||||||
this.formData;
|
|
||||||
|
|
||||||
if (password && !oldPassword) {
|
|
||||||
this.errors.push("emptyPassword");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password !== passwordConfirm) {
|
|
||||||
this.errors.push("passwordsDiffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.errors.length > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
mastodon,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (password) {
|
|
||||||
data.password = password;
|
|
||||||
data.oldPassword = oldPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.pagination = pagination;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.patch(`/api/v1/me`, data);
|
|
||||||
|
|
||||||
showToastr("Profil mis à jour", true);
|
|
||||||
} catch (err) {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de mettre à jour votre profil"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#mon-compte");
|
|
||||||
}
|
|
|
@ -1,264 +0,0 @@
|
||||||
if (typeof item !== "undefined") {
|
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
item,
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
canShareItem,
|
|
||||||
tracklist: [],
|
|
||||||
identifiers: [],
|
|
||||||
modalIsVisible: false,
|
|
||||||
identifiersMode: "preview",
|
|
||||||
identifiersPreviewLength: 16,
|
|
||||||
preview: null,
|
|
||||||
index: null,
|
|
||||||
showModalDelete: false,
|
|
||||||
showModalShare: false,
|
|
||||||
shareMessage: "",
|
|
||||||
shareMessageTransformed: "",
|
|
||||||
shareMessageLength: 0,
|
|
||||||
shareSubmiting: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.setTrackList();
|
|
||||||
this.setIdentifiers();
|
|
||||||
|
|
||||||
window.addEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
window.removeEventListener("keydown", this.keyDown);
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
shareMessage(message) {
|
|
||||||
const video =
|
|
||||||
this.item.videos && this.item.videos.length > 0
|
|
||||||
? this.item.videos[0].uri
|
|
||||||
: "";
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
this.shareMessageLength = this.shareMessageTransformed.replace(
|
|
||||||
video,
|
|
||||||
new Array(36).join("#")
|
|
||||||
).length;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setIdentifiers() {
|
|
||||||
this.identifiers = [];
|
|
||||||
|
|
||||||
const max =
|
|
||||||
this.identifiersMode === "preview" &&
|
|
||||||
this.item.identifiers.length > this.identifiersPreviewLength
|
|
||||||
? this.identifiersPreviewLength
|
|
||||||
: this.item.identifiers.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < max; i += 1) {
|
|
||||||
this.identifiers.push(this.item.identifiers[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setTrackList() {
|
|
||||||
this.tracklist = [];
|
|
||||||
let subTrack = {
|
|
||||||
type: null,
|
|
||||||
title: null,
|
|
||||||
tracks: [],
|
|
||||||
};
|
|
||||||
for (let i = 0; i < this.item.tracklist.length; i += 1) {
|
|
||||||
const {
|
|
||||||
type_,
|
|
||||||
title,
|
|
||||||
position,
|
|
||||||
duration,
|
|
||||||
artists,
|
|
||||||
extraartists,
|
|
||||||
} = this.item.tracklist[i];
|
|
||||||
|
|
||||||
if (type_ === "heading") {
|
|
||||||
if (subTrack.type) {
|
|
||||||
this.tracklist.push(subTrack);
|
|
||||||
subTrack = {
|
|
||||||
type: null,
|
|
||||||
title: null,
|
|
||||||
tracks: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
subTrack.type = type_;
|
|
||||||
subTrack.title = title;
|
|
||||||
} else {
|
|
||||||
subTrack.tracks.push({
|
|
||||||
title,
|
|
||||||
position,
|
|
||||||
duration,
|
|
||||||
extraartists,
|
|
||||||
artists,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.tracklist.push(subTrack);
|
|
||||||
},
|
|
||||||
setImage() {
|
|
||||||
this.preview = this.item.images[this.index].uri;
|
|
||||||
},
|
|
||||||
showGallery(event) {
|
|
||||||
const item =
|
|
||||||
event.target.tagName === "IMG"
|
|
||||||
? event.target.parentElement
|
|
||||||
: event.target;
|
|
||||||
|
|
||||||
const { index } = item.dataset;
|
|
||||||
|
|
||||||
this.index = Number(index);
|
|
||||||
this.modalIsVisible = true;
|
|
||||||
|
|
||||||
this.setImage();
|
|
||||||
},
|
|
||||||
toggleModal() {
|
|
||||||
this.modalIsVisible = !this.modalIsVisible;
|
|
||||||
},
|
|
||||||
previous() {
|
|
||||||
this.index =
|
|
||||||
this.index > 0
|
|
||||||
? this.index - 1
|
|
||||||
: this.item.images.length - 1;
|
|
||||||
this.setImage();
|
|
||||||
},
|
|
||||||
next() {
|
|
||||||
this.index =
|
|
||||||
this.index + 1 === this.item.images.length
|
|
||||||
? 0
|
|
||||||
: this.index + 1;
|
|
||||||
this.setImage();
|
|
||||||
},
|
|
||||||
changeImage(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const direction = event.code;
|
|
||||||
|
|
||||||
if (
|
|
||||||
["ArrowRight", "ArrowLeft", "Escape"].indexOf(direction) !==
|
|
||||||
-1
|
|
||||||
) {
|
|
||||||
switch (direction) {
|
|
||||||
case "ArrowRight":
|
|
||||||
return this.next();
|
|
||||||
case "ArrowLeft":
|
|
||||||
return this.previous();
|
|
||||||
default:
|
|
||||||
this.modalIsVisible = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
showLessIdentifiers() {
|
|
||||||
this.identifiersMode = "preview";
|
|
||||||
this.setIdentifiers();
|
|
||||||
|
|
||||||
document
|
|
||||||
.querySelector("#identifiers")
|
|
||||||
.scrollIntoView({ behavior: "smooth" });
|
|
||||||
},
|
|
||||||
showConfirmDelete() {
|
|
||||||
this.toggleModalDelete();
|
|
||||||
},
|
|
||||||
toggleModalDelete() {
|
|
||||||
this.showModalDelete = !this.showModalDelete;
|
|
||||||
},
|
|
||||||
updateItem() {
|
|
||||||
showToastr("Mise à jour en cours…", true);
|
|
||||||
axios
|
|
||||||
.patch(`/api/v1/albums/${this.item._id}`)
|
|
||||||
.then((res) => {
|
|
||||||
showToastr("Mise à jour réalisée avec succès", true);
|
|
||||||
this.item = res.data;
|
|
||||||
|
|
||||||
this.setTrackList();
|
|
||||||
this.setIdentifiers();
|
|
||||||
this.showLessIdentifiers();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de mettre à jour cet album",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteItem() {
|
|
||||||
axios
|
|
||||||
.delete(`/api/v1/albums/${this.item._id}`)
|
|
||||||
.then(() => {
|
|
||||||
window.location.href = "/ma-collection";
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de supprimer cet album"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.toggleModalDelete();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
goToArtist() {
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
shareAlbum() {
|
|
||||||
if (this.shareSubmiting) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.shareSubmiting = true;
|
|
||||||
axios
|
|
||||||
.post(`/api/v1/albums/${this.item._id}/share`, {
|
|
||||||
message: this.shareMessageTransformed,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
showToastr("Album partagé", true);
|
|
||||||
this.shareMessage = "";
|
|
||||||
this.showModalShare = false;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
showToastr(
|
|
||||||
err.response?.data?.message ||
|
|
||||||
"Impossible de partager cet album",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.shareSubmiting = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#ma-collection-details");
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
format: "xml",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {},
|
|
||||||
destroyed() {},
|
|
||||||
methods: {
|
|
||||||
exportCollection(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
window.open(`/api/v1/albums?exportFormat=${this.format}`, "_blank");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#exporter");
|
|
|
@ -1,106 +0,0 @@
|
||||||
Vue.createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
file: "",
|
|
||||||
content: [],
|
|
||||||
parsed: false,
|
|
||||||
imported: 0,
|
|
||||||
disabled: true,
|
|
||||||
state: "default",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {},
|
|
||||||
destroyed() {},
|
|
||||||
methods: {
|
|
||||||
handleFileUpload(event) {
|
|
||||||
const { files } = event.target;
|
|
||||||
const [csv] = files;
|
|
||||||
|
|
||||||
this.file = csv;
|
|
||||||
|
|
||||||
this.file = csv;
|
|
||||||
// this.parseFile();
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = (content) => {
|
|
||||||
this.content = [];
|
|
||||||
this.state = "parse";
|
|
||||||
const lines = content.target.result.split(/\r\n|\n/);
|
|
||||||
for (let line = 1; line < lines.length - 1; line += 1) {
|
|
||||||
this.parseLine(lines[0], lines[line]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = "default";
|
|
||||||
this.disabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsText(csv);
|
|
||||||
},
|
|
||||||
parseLine(header, line) {
|
|
||||||
const row = {};
|
|
||||||
let currentHeaderIndex = 0;
|
|
||||||
|
|
||||||
let separant = ",";
|
|
||||||
let value = "";
|
|
||||||
for (let i = 0; i < line.length; i += 1) {
|
|
||||||
const char = line[i];
|
|
||||||
|
|
||||||
if (char !== separant) {
|
|
||||||
if (char === '"') {
|
|
||||||
separant = '"';
|
|
||||||
} else {
|
|
||||||
value += char;
|
|
||||||
}
|
|
||||||
} else if (char === '"') {
|
|
||||||
separant = ",";
|
|
||||||
} else {
|
|
||||||
row[header.split(",")[currentHeaderIndex]] = value;
|
|
||||||
currentHeaderIndex += 1;
|
|
||||||
value = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.content.push(row);
|
|
||||||
},
|
|
||||||
async addOne(index) {
|
|
||||||
const { Artist, Title, release_id } = this.content[index];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await axios.get(
|
|
||||||
`/api/v1/albums?discogsId=${release_id}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.status === 204) {
|
|
||||||
await axios.post("/api/v1/albums", {
|
|
||||||
discogsId: release_id,
|
|
||||||
share: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.imported += 1;
|
|
||||||
if (this.content.length > index + 1) {
|
|
||||||
await this.addOne(index + 1);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
showToastr(
|
|
||||||
`Impossible d'ajouter l'album ${Title} de ${Artist}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
async importCollection(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.disabled = true;
|
|
||||||
this.state = "submit";
|
|
||||||
this.imported = 0;
|
|
||||||
|
|
||||||
const imported = await this.addOne(0);
|
|
||||||
|
|
||||||
this.disabled = false;
|
|
||||||
this.state = imported ? "done" : "default";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).mount("#importer");
|
|
|
@ -1,87 +0,0 @@
|
||||||
/**
|
|
||||||
* Fonction permettant de récupérer le thème du système
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
function getPreferredColorScheme() {
|
|
||||||
if (window.matchMedia) {
|
|
||||||
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
||||||
return "dark";
|
|
||||||
}
|
|
||||||
return "light";
|
|
||||||
}
|
|
||||||
return "light";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
if (window.matchMedia) {
|
|
||||||
const colorSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
||||||
colorSchemeQuery.addEventListener("change", () => {
|
|
||||||
const selectedColorScheme = localStorage.getItem("theme") || "system";
|
|
||||||
|
|
||||||
if (selectedColorScheme === "system") {
|
|
||||||
const preferedColorScheme = getPreferredColorScheme();
|
|
||||||
setColorScheme(preferedColorScheme);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// INFO: Au chargement de la page on détecte le thème à charger
|
|
||||||
setColorScheme(currentTheme);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
|
@ -4,17 +4,16 @@
|
||||||
"description": "Simple application to manage your CD/Vinyl collection",
|
"description": "Simple application to manage your CD/Vinyl collection",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./dist/bin/www",
|
"start": "node ./dist/bin/www",
|
||||||
"run:all": "npm-run-all build sass uglify start",
|
"run:all": "npm-run-all build sass start",
|
||||||
"watch": "npx nodemon -e js,scss",
|
"watch": "nodemon -e js,scss",
|
||||||
"sass": "npx sass sass/index.scss public/css/main.css -s compressed --color",
|
"sass": "npx sass sass/index.scss public/css/main.css -s compressed --color",
|
||||||
"uglify": "npx gulp",
|
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "npx babel ./src --out-dir dist --copy-files",
|
"build": "babel ./src --out-dir dist --copy-files",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prepare": "npx husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "16.x || 18.x",
|
"node": "16.x",
|
||||||
"yarn": "1.x"
|
"yarn": "1.x"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -24,7 +23,7 @@
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Damien Broqua",
|
"name": "Damien Broqua",
|
||||||
"email": "contact@darkou.fr",
|
"email": "contact@darkou.fr",
|
||||||
"url": "https://www.darkou.link"
|
"url": "https://www.darkou.fr"
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -39,53 +38,39 @@
|
||||||
"prettier": "^2.5.1"
|
"prettier": "^2.5.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.490.0",
|
|
||||||
"@aws-sdk/lib-storage": "^3.490.0",
|
|
||||||
"@babel/cli": "^7.17.0",
|
"@babel/cli": "^7.17.0",
|
||||||
"@babel/core": "^7.17.2",
|
"@babel/core": "^7.17.2",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"axios": "^0.26.0",
|
"axios": "^0.26.0",
|
||||||
"chart.js": "^4.4.1",
|
|
||||||
"connect-ensure-login": "^0.1.1",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"date-fns": "^2.28.0",
|
|
||||||
"date-fns-tz": "^1.3.3",
|
|
||||||
"debug": "^4.3.3",
|
"debug": "^4.3.3",
|
||||||
"disconnect": "^1.2.2",
|
"disconnect": "^1.2.2",
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
"excel4node": "^1.7.2",
|
"excel4node": "^1.7.2",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"gulp": "^4.0.2",
|
|
||||||
"gulp-babel": "^8.0.0",
|
|
||||||
"gulp-concat": "^2.6.1",
|
|
||||||
"gulp-sourcemaps": "^3.0.0",
|
|
||||||
"gulp-uglify": "^3.0.2",
|
|
||||||
"joi": "^17.6.0",
|
"joi": "^17.6.0",
|
||||||
"knacss": "^8.0.4",
|
"knacss": "^8.0.4",
|
||||||
"mastodon": "^1.2.2",
|
"moment": "^2.29.1",
|
||||||
|
"moment-timezone": "^0.5.34",
|
||||||
"mongoose": "^6.2.1",
|
"mongoose": "^6.2.1",
|
||||||
"mongoose-unique-validator": "^3.0.0",
|
"mongoose-unique-validator": "^3.0.0",
|
||||||
"nodemailer": "^6.7.8",
|
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"passport": "^0.5.2",
|
"passport": "^0.5.2",
|
||||||
"passport-custom": "^1.1.1",
|
|
||||||
"passport-http": "^0.3.0",
|
"passport-http": "^0.3.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "^1.49.7",
|
"sass": "^1.49.7",
|
||||||
"svg-captcha": "^1.4.0",
|
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"vue": "^3.2.31"
|
"vue": "^3.2.31"
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
"exec": "npm run run:all",
|
"exec": "yarn run:all",
|
||||||
"watch": [
|
"watch": [
|
||||||
"src/*",
|
"src/*",
|
||||||
"sass/*",
|
"sass/*"
|
||||||
"javascripts/*"
|
|
||||||
],
|
],
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"**/__tests__/**",
|
"**/__tests__/**",
|
||||||
|
|
Binary file not shown.
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" standalone="no"?>
|
<?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">
|
<!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">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<metadata>Copyright (C) 2024 by original authors @ fontello.com</metadata>
|
<metadata>Copyright (C) 2022 by original authors @ fontello.com</metadata>
|
||||||
<defs>
|
<defs>
|
||||||
<font id="icon" horiz-adv-x="1000" >
|
<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" />
|
<font-face font-family="icon" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||||
|
@ -26,10 +26,6 @@
|
||||||
|
|
||||||
<glyph glyph-name="export" unicode="" d="M786 298v-144q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h142q7 0 13-6t5-12q0-15-15-18-43-15-74-34-5-2-9-2h-62q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v119q0 11 10 16 16 7 31 21 8 9 19 4 12-5 12-16z m132 277l-214-214q-10-11-25-11-7 0-14 3-22 9-22 33v107h-89q-181 0-245-73-66-77-41-264 2-13-11-19-5-1-7-1-9 0-14 7-6 8-12 17t-22 39-28 55-21 64-10 68q0 27 2 51t8 50 15 49 27 45 38 42 52 34 70 27 89 17 110 6h89v107q0 24 22 33 7 3 14 3 14 0 25-11l214-214q11-10 11-25t-11-25z" horiz-adv-x="928.6" />
|
<glyph glyph-name="export" unicode="" d="M786 298v-144q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h142q7 0 13-6t5-12q0-15-15-18-43-15-74-34-5-2-9-2h-62q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v119q0 11 10 16 16 7 31 21 8 9 19 4 12-5 12-16z m132 277l-214-214q-10-11-25-11-7 0-14 3-22 9-22 33v107h-89q-181 0-245-73-66-77-41-264 2-13-11-19-5-1-7-1-9 0-14 7-6 8-12 17t-22 39-28 55-21 64-10 68q0 27 2 51t8 50 15 49 27 45 38 42 52 34 70 27 89 17 110 6h89v107q0 24 22 33 7 3 14 3 14 0 25-11l214-214q11-10 11-25t-11-25z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
<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="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" />
|
<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.3 KiB After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,50 +6,50 @@
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
width="100%"
|
viewBox="0 0 168.85766 133.4734"
|
||||||
height="100%"
|
|
||||||
id="MusicTopus"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
viewBox="0 0 168.85766 133.4734">
|
id="MusicTopus"
|
||||||
|
height="100%"
|
||||||
|
width="100%">
|
||||||
<defs
|
<defs
|
||||||
id="defs2">
|
id="defs2">
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="linearGradient3016">
|
id="linearGradient3016">
|
||||||
<stop
|
<stop
|
||||||
id="stop3018-4"
|
offset="0"
|
||||||
stop-color="#949494"
|
stop-color="#949494"
|
||||||
offset="0" />
|
id="stop3018-4" />
|
||||||
<stop
|
<stop
|
||||||
id="stop3020-0"
|
offset="1"
|
||||||
stop-color="#949494"
|
|
||||||
stop-opacity="0"
|
stop-opacity="0"
|
||||||
offset="1" />
|
stop-color="#949494"
|
||||||
|
id="stop3020-0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="linearGradient1417"
|
x1="57.074001"
|
||||||
y2="104.59"
|
|
||||||
xlink:href="#linearGradient3016"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="103.29"
|
|
||||||
gradientTransform="translate(-19.041285,-22.505715)"
|
|
||||||
y1="27.309999"
|
y1="27.309999"
|
||||||
x1="57.074001" />
|
gradientTransform="translate(-19.041285,-22.505715)"
|
||||||
<radialGradient
|
x2="103.29"
|
||||||
id="radialGradient10835"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
cy="98.385002"
|
xlink:href="#linearGradient3016"
|
||||||
cx="16.073999"
|
y2="104.59"
|
||||||
|
id="linearGradient1417" />
|
||||||
|
<radialGradient
|
||||||
|
r="7.395"
|
||||||
gradientTransform="matrix(2.2777,1.8145,-1.5547,2.3139,262.42,-105.22857)"
|
gradientTransform="matrix(2.2777,1.8145,-1.5547,2.3139,262.42,-105.22857)"
|
||||||
r="7.395">
|
cx="16.073999"
|
||||||
|
cy="98.385002"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient10835">
|
||||||
<stop
|
<stop
|
||||||
id="stop10767-64"
|
offset="0"
|
||||||
stop-color="#989898"
|
stop-color="#989898"
|
||||||
offset="0" />
|
id="stop10767-64" />
|
||||||
<stop
|
<stop
|
||||||
id="stop10769-6"
|
offset="1"
|
||||||
stop-color="#989898"
|
|
||||||
stop-opacity="0"
|
stop-opacity="0"
|
||||||
offset="1" />
|
stop-color="#989898"
|
||||||
|
id="stop10769-6" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<metadata
|
<metadata
|
||||||
|
@ -65,276 +65,279 @@
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
transform="translate(-4.0461145,-24.740973)"
|
id="layer1"
|
||||||
id="layer1">
|
transform="translate(-4.0461145,-24.740973)">
|
||||||
<g
|
<g
|
||||||
id="g4845"
|
transform="matrix(-1,0,0,1,16.909353,13.841748)"
|
||||||
transform="matrix(-1,0,0,1,16.909353,13.841748)">
|
id="g4845">
|
||||||
<path
|
|
||||||
id="path4839"
|
|
||||||
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="path4841"
|
|
||||||
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"
|
|
||||||
style="fill:#7b2121;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
id="path4843"
|
|
||||||
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>
|
|
||||||
<g
|
|
||||||
transform="translate(195.33147,13.841748)"
|
|
||||||
id="g4837">
|
|
||||||
<path
|
<path
|
||||||
style="fill:#301818;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
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"
|
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="path4828" />
|
id="path4839" />
|
||||||
<path
|
<path
|
||||||
style="fill:#7b2121;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
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 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"
|
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="path4830" />
|
id="path4841" />
|
||||||
<path
|
<path
|
||||||
style="fill:#f3e8d4;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
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"
|
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="path4832" />
|
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>
|
</g>
|
||||||
<path
|
<path
|
||||||
id="path4668"
|
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"
|
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"
|
||||||
style="fill:#ec8479;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4668" />
|
||||||
<path
|
<path
|
||||||
id="path4723"
|
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"
|
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"
|
||||||
style="fill:#fbb9b8;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4723" />
|
||||||
<path
|
<path
|
||||||
id="path4689"
|
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"
|
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"
|
||||||
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4689" />
|
||||||
<path
|
<ellipse
|
||||||
|
ry="20.250488"
|
||||||
|
rx="22.683598"
|
||||||
|
cy="99.451027"
|
||||||
|
cx="39.641644"
|
||||||
id="circle4790"
|
id="circle4790"
|
||||||
d="m 62.325241,99.451027 c 0,11.184033 -10.15579,20.250493 -22.683597,20.250493 -7.402258,0 -0.829184,-8.01428 -4.969517,-12.91144 -0.612097,-0.72399 -1.770309,-5.66385 -3.213537,-6.11122 -5.316459,-1.647971 -14.500545,2.37098 -14.500544,-1.227833 3e-6,-11.184036 6.023828,-20.350467 18.551633,-20.350467 0.220732,3.973948 2.052425,13.960818 7.154907,14.623465 7.156786,-4.199604 19.410793,-0.618035 19.660655,5.727002 z"
|
|
||||||
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" />
|
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
|
<path
|
||||||
style="fill:#212121;fill-opacity:1;stroke-width:0.205;stroke:none;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
id="path4"
|
||||||
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"
|
|
||||||
transform="translate(4.0461145,24.740973)"
|
transform="translate(4.0461145,24.740973)"
|
||||||
id="path4" />
|
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
|
<ellipse
|
||||||
cy="99.451027"
|
ry="5.6584005"
|
||||||
cx="39.641644"
|
|
||||||
id="path4786"
|
|
||||||
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"
|
|
||||||
rx="6.7384396"
|
rx="6.7384396"
|
||||||
ry="5.6584005" />
|
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"
|
||||||
<ellipse
|
id="path4786"
|
||||||
cy="99.451027"
|
|
||||||
cx="39.641644"
|
cx="39.641644"
|
||||||
id="path4788"
|
cy="99.451027" />
|
||||||
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"
|
<ellipse
|
||||||
|
ry="0.96285915"
|
||||||
rx="1.1466434"
|
rx="1.1466434"
|
||||||
ry="0.96285915" />
|
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
|
<path
|
||||||
id="path4693"
|
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"
|
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"
|
||||||
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4693" />
|
||||||
<path
|
<path
|
||||||
id="path4695"
|
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"
|
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"
|
||||||
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4695" />
|
||||||
<path
|
<path
|
||||||
id="path4697"
|
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"
|
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"
|
||||||
style="fill:#feb6b9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4697" />
|
||||||
<path
|
<path
|
||||||
id="path4699"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4699" />
|
||||||
<path
|
<path
|
||||||
id="path4701"
|
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"
|
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"
|
||||||
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4701" />
|
||||||
<path
|
<path
|
||||||
id="path4703"
|
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"
|
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"
|
||||||
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4703" />
|
||||||
<path
|
<path
|
||||||
id="path4705"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4705" />
|
||||||
<path
|
<path
|
||||||
id="path4707"
|
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"
|
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"
|
||||||
style="fill:#eea6a7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4707" />
|
||||||
<path
|
<path
|
||||||
id="path4709"
|
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"
|
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"
|
||||||
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4709" />
|
||||||
<path
|
<path
|
||||||
id="path4711"
|
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"
|
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"
|
||||||
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4711" />
|
||||||
<path
|
<path
|
||||||
id="path4713"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4713" />
|
||||||
<path
|
<path
|
||||||
id="path4715"
|
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"
|
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"
|
||||||
style="fill:#feb6b7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4715" />
|
||||||
<path
|
<path
|
||||||
id="path4717"
|
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"
|
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"
|
||||||
style="fill:#ffd7d7;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4717" />
|
||||||
<path
|
<path
|
||||||
id="path4719"
|
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"
|
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"
|
||||||
style="fill:#feb6b9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4719" />
|
||||||
<path
|
<path
|
||||||
id="path4721"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4721" />
|
||||||
<g
|
<g
|
||||||
transform="rotate(19.617168,87.590538,52.720911)"
|
id="g4740"
|
||||||
id="g4740">
|
transform="rotate(19.617168,87.590538,52.720911)">
|
||||||
<path
|
<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"
|
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"
|
|
||||||
id="path4725" />
|
|
||||||
<path
|
|
||||||
id="path4730"
|
|
||||||
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="path4732"
|
|
||||||
cx="101.24133"
|
|
||||||
cy="91.80127"
|
|
||||||
r="3.4367433" />
|
|
||||||
<circle
|
|
||||||
r="1.0470117"
|
|
||||||
cy="91.470535"
|
|
||||||
cx="100.24915"
|
|
||||||
id="circle4734"
|
|
||||||
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="circle4657"
|
|
||||||
cx="102.3987"
|
|
||||||
cy="93.070198"
|
|
||||||
r="0.6970861" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4750"
|
|
||||||
transform="matrix(-0.84292907,0.5380247,0.5380247,0.84292907,153.98869,-37.720137)">
|
|
||||||
<path
|
|
||||||
id="path4742"
|
|
||||||
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"
|
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" />
|
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
|
<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"
|
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"
|
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="path4744" />
|
id="path4730" />
|
||||||
<circle
|
<circle
|
||||||
r="3.4367433"
|
r="3.4367433"
|
||||||
cy="90.845963"
|
cy="91.80127"
|
||||||
cx="105.85611"
|
cx="101.24133"
|
||||||
id="circle4746"
|
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" />
|
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
|
<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"
|
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="circle4748"
|
id="circle4734"
|
||||||
cx="106.96864"
|
cx="100.24915"
|
||||||
cy="89.537895"
|
cy="91.470535"
|
||||||
r="1.0470117" />
|
r="1.0470117" />
|
||||||
<circle
|
<circle
|
||||||
r="0.59710735"
|
r="0.6970861"
|
||||||
cy="92.300102"
|
cy="93.070198"
|
||||||
cx="105.70718"
|
cx="102.3987"
|
||||||
id="circle4655"
|
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" />
|
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>
|
||||||
<path
|
<g
|
||||||
id="path4752"
|
transform="matrix(-0.84292907,0.5380247,0.5380247,0.84292907,153.98869,-37.720137)"
|
||||||
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="g4750">
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
<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
|
<path
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
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"
|
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"
|
||||||
id="path4754" />
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
<path
|
<path
|
||||||
id="path4756"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4756" />
|
||||||
<path
|
<path
|
||||||
id="path4758"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4758" />
|
||||||
<path
|
<path
|
||||||
id="path4760"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4760" />
|
||||||
<path
|
<path
|
||||||
id="path4762"
|
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"
|
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"
|
||||||
style="fill:#dd6258;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4762" />
|
||||||
<path
|
<path
|
||||||
id="path4764"
|
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"
|
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"
|
||||||
style="fill:#7f2625;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
id="path4764" />
|
||||||
<path
|
<path
|
||||||
id="path4847"
|
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"
|
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"
|
||||||
style="fill:none;stroke:#301818;stroke-width:4.46500015;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
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
|
<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"
|
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 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"
|
|
||||||
id="path4651" />
|
|
||||||
<path
|
|
||||||
id="path4653"
|
|
||||||
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"
|
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"
|
||||||
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" />
|
id="path4653" />
|
||||||
<g
|
<g
|
||||||
transform="matrix(0.02360188,0,0,0.02360188,113.18825,105.15104)"
|
style="stroke-width:42.36951447"
|
||||||
id="g6"
|
id="g6"
|
||||||
style="stroke-width:42.36951447">
|
transform="matrix(0.02360188,0,0,0.02360188,113.18825,105.15104)">
|
||||||
<path
|
<path
|
||||||
style="fill:#29abe2;stroke-width:42.36951447"
|
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"
|
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="path2" />
|
style="fill:#29abe2;stroke-width:42.36951447" />
|
||||||
<path
|
<path
|
||||||
style="fill:#ffffff;stroke-width:42.36951447"
|
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"
|
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="path4-9" />
|
style="fill:#ffffff;stroke-width:42.36951447" />
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
style="stroke-width:79.67237854"
|
transform="matrix(-0.0125514,0,0,0.0125514,96.697579,101.08859)"
|
||||||
id="g4688"
|
id="g4688"
|
||||||
transform="matrix(-0.0125514,0,0,0.0125514,96.697579,101.08859)">
|
style="stroke-width:79.67237854">
|
||||||
<path
|
<path
|
||||||
id="path4684"
|
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"
|
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:79.67237854" />
|
id="path4684" />
|
||||||
<path
|
<path
|
||||||
id="path4686"
|
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"
|
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:79.67237854" />
|
id="path4686" />
|
||||||
</g>
|
</g>
|
||||||
<path
|
<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"
|
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"
|
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"
|
||||||
id="path4692" />
|
|
||||||
<path
|
|
||||||
id="path4694"
|
|
||||||
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"
|
|
||||||
style="fill:#29ebe2;fill-opacity:1;stroke-width:1.00000024" />
|
|
||||||
<path
|
|
||||||
id="path4698"
|
|
||||||
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"
|
|
||||||
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" />
|
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
|
<path
|
||||||
style="fill:#29ebe2;fill-opacity:1;stroke-width:1.00000024"
|
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"
|
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"
|
||||||
id="path4700" />
|
style="fill:#29ebe2;fill-opacity:1;stroke-width:1.00000024" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Binary file not shown.
Before Width: | Height: | Size: 60 KiB |
Binary file not shown.
Before Width: | Height: | Size: 60 KiB |
|
@ -0,0 +1,138 @@
|
||||||
|
/**
|
||||||
|
* Fonction permettant d'afficher un message dans un toastr
|
||||||
|
* @param {String} message
|
||||||
|
*/
|
||||||
|
function showToastr(message, success = false) {
|
||||||
|
let x = document.getElementById("toastr");
|
||||||
|
if ( message ) {
|
||||||
|
x.getElementsByTagName("SPAN")[0].innerHTML = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
x.className = `${x.className} show`.replace("sucess", "");
|
||||||
|
if ( success ) {
|
||||||
|
x.className = `${x.className} success`;
|
||||||
|
}
|
||||||
|
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de masquer le toastr
|
||||||
|
*/
|
||||||
|
function hideToastr() {
|
||||||
|
let x = document.getElementById("toastr");
|
||||||
|
|
||||||
|
x.className = x.className.replace("show", "");
|
||||||
|
x.getElementsByTagName("SPAN")[0].innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de récupérer la valeur d'un cookie
|
||||||
|
* @param {String} cname
|
||||||
|
* @param {String} defaultValue
|
||||||
|
*
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function getCookie(cname, defaultValue = 'false') {
|
||||||
|
let name = cname + "=";
|
||||||
|
let decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
let ca = decodedCookie.split(';');
|
||||||
|
for(let i = 0; i < ca.length; i+=1) {
|
||||||
|
let c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
if (c.indexOf(name) == 0) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de créer un cookie
|
||||||
|
* @param {String} cname
|
||||||
|
* @param {String} cvalue
|
||||||
|
* @param {Number} exdays
|
||||||
|
*/
|
||||||
|
function setCookie(cname, cvalue, exdays = 30) {
|
||||||
|
const d = new Date();
|
||||||
|
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||||
|
let expires = "expires="+ d.toUTCString();
|
||||||
|
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction de (dé)charger le thème accessible
|
||||||
|
* @param {String} value
|
||||||
|
*/
|
||||||
|
function setAriaTheme(value) {
|
||||||
|
let body = document.body;
|
||||||
|
if ( value === 'true' ) {
|
||||||
|
let classesString = body.className || "";
|
||||||
|
if (classesString.indexOf("is-accessible") === -1) {
|
||||||
|
body.classList.add("is-accessible");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body.classList.remove("is-accessible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction de (dé)charger le thème accessible
|
||||||
|
*/
|
||||||
|
function switchAriaTheme() {
|
||||||
|
let body = document.body;
|
||||||
|
|
||||||
|
body.classList.toggle("is-accessible");
|
||||||
|
|
||||||
|
setCookie('ariatheme', body.classList.contains("is-accessible"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de switcher de thème clair/sombre
|
||||||
|
* @param {Object} e
|
||||||
|
*/
|
||||||
|
function switchTheme(e) {
|
||||||
|
const theme = e.target.checked ? 'dark' : 'light';
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
setCookie('theme', theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensemble d'actions effectuées au chargement de la page
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
$navbarBurgers.forEach( el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const switchAriaThemeBtn = document.querySelector("#switchAriaTheme");
|
||||||
|
if ( switchAriaThemeBtn ) {
|
||||||
|
switchAriaThemeBtn.addEventListener("click", switchAriaTheme);
|
||||||
|
}
|
||||||
|
setAriaTheme(getCookie('ariatheme'));
|
||||||
|
|
||||||
|
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
|
||||||
|
if ( toggleSwitch ) {
|
||||||
|
toggleSwitch.addEventListener('change', switchTheme, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentThemeIsDark = getCookie('theme');
|
||||||
|
if ( currentThemeIsDark === 'false' && window.matchMedia ) {
|
||||||
|
currentThemeIsDark = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
switchTheme({target: {checked: currentThemeIsDark === 'dark'}});
|
||||||
|
if ( toggleSwitch) {
|
||||||
|
toggleSwitch.checked = currentThemeIsDark === 'dark';
|
||||||
|
}
|
||||||
|
});
|
|
@ -7,18 +7,10 @@
|
||||||
.list {
|
.list {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
|
||||||
.item {
|
.item{
|
||||||
img {
|
img {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.in-collection {
|
|
||||||
opacity: 0.6;
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,19 +5,15 @@
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
display: block;
|
display: block;
|
||||||
padding: 1.25rem;
|
padding: 1.25rem;
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
margin: auto;
|
||||||
@include transition() {}
|
@include transition() {}
|
||||||
|
|
||||||
&.mini {
|
@include respond-to("small-up") {
|
||||||
margin: auto;
|
width: 65%;
|
||||||
width: calc(100% - 2rem);
|
}
|
||||||
|
@include respond-to("medium-up") {
|
||||||
@include respond-to("small-up") {
|
width: 35%;
|
||||||
width: 65%;
|
|
||||||
}
|
|
||||||
@include respond-to("medium-up") {
|
|
||||||
width: 35%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to("small-up") {
|
@include respond-to("small-up") {
|
||||||
width: 33%;
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
@ -29,17 +28,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.showMoreFilters {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.up::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
.down::before {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list{
|
.list{
|
||||||
.title {
|
.title {
|
||||||
.icon-trash {
|
.icon-trash {
|
||||||
|
|
|
@ -22,12 +22,10 @@ $nord15: #b48ead;
|
||||||
|
|
||||||
$primary-color: $nord8;
|
$primary-color: $nord8;
|
||||||
$danger-color: $nord11;
|
$danger-color: $nord11;
|
||||||
$error-color: $nord12;
|
|
||||||
$warning-color: $nord13;
|
$warning-color: $nord13;
|
||||||
$success-color: $nord14;
|
$success-color: $nord14;
|
||||||
$primary-color-hl: darken($primary-color, $hoverAmount);
|
$primary-color-hl: darken($primary-color, $hoverAmount);
|
||||||
$danger-color-hl: darken($danger-color, $hoverAmount);
|
$danger-color-hl: darken($danger-color, $hoverAmount);
|
||||||
$error-color-hl: darken($error-color, $hoverAmount);
|
|
||||||
$warning-color-hl: darken($warning-color, $hoverAmount);
|
$warning-color-hl: darken($warning-color, $hoverAmount);
|
||||||
$success-color-hl: darken($success-color, $hoverAmount);
|
$success-color-hl: darken($success-color, $hoverAmount);
|
||||||
|
|
||||||
|
@ -37,15 +35,11 @@ $button-alternate-color: #01103C;
|
||||||
$pagination-border-color: $nord3;
|
$pagination-border-color: $nord3;
|
||||||
$pagination-hover-color: rgb(115, 151, 186);
|
$pagination-hover-color: rgb(115, 151, 186);
|
||||||
|
|
||||||
$close-background: rgba(10,10,10,.6);
|
|
||||||
$close-background-dark: rgba(240,240,240,.6);
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--default-color: #{$white};
|
--default-color: #{$white};
|
||||||
--bg-color: #{darken($white, 5%)};
|
--bg-color: #{darken($white, 5%)};
|
||||||
--bg-alternate-color: #{darken($white, 8%)};
|
--bg-alternate-color: #{darken($white, 8%)};
|
||||||
--font-color: #{$nord3};
|
--font-color: #{$nord3};
|
||||||
--hover-font-color: #{lighten($nord3, 16%)};
|
|
||||||
--footer-color: #{$darken-white};
|
--footer-color: #{$darken-white};
|
||||||
--link-color: #{$nord1};
|
--link-color: #{$nord1};
|
||||||
|
|
||||||
|
@ -62,10 +56,6 @@ $close-background-dark: rgba(240,240,240,.6);
|
||||||
|
|
||||||
--button-link-text-color: #2C364A;
|
--button-link-text-color: #2C364A;
|
||||||
|
|
||||||
--close-background: #{$close-background};
|
|
||||||
|
|
||||||
--loader-img: url('/img/loading-light.gif');
|
|
||||||
|
|
||||||
--nord0: #{$nord0};
|
--nord0: #{$nord0};
|
||||||
--nord1: #{$nord1};
|
--nord1: #{$nord1};
|
||||||
--nord2: #{$nord2};
|
--nord2: #{$nord2};
|
||||||
|
@ -89,7 +79,6 @@ $close-background-dark: rgba(240,240,240,.6);
|
||||||
--bg-color: #{lighten($nord0, 2%)};
|
--bg-color: #{lighten($nord0, 2%)};
|
||||||
--bg-alternate-color: #{lighten($nord3, 8%)};
|
--bg-alternate-color: #{lighten($nord3, 8%)};
|
||||||
--font-color: #{$nord6};
|
--font-color: #{$nord6};
|
||||||
--hover-font-color: #{darken($nord6, 16%)};
|
|
||||||
--footer-color: #{$nord1};
|
--footer-color: #{$nord1};
|
||||||
--link-color: #{$nord4};
|
--link-color: #{$nord4};
|
||||||
|
|
||||||
|
@ -105,8 +94,4 @@ $close-background-dark: rgba(240,240,240,.6);
|
||||||
--border-color: #{$nord1};
|
--border-color: #{$nord1};
|
||||||
|
|
||||||
--button-link-text-color: #{$white};
|
--button-link-text-color: #{$white};
|
||||||
|
|
||||||
--close-background: #{$nord3};
|
|
||||||
|
|
||||||
--loader-img: url('/img/loading-dark.gif');
|
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
main {
|
.error {
|
||||||
&.error {
|
min-height: calc(100vh - 3.25rem - 100px);
|
||||||
min-height: calc(100vh - 3.25rem - 100px);
|
padding-top: 4rem;
|
||||||
padding-top: 4rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -9,14 +9,6 @@
|
||||||
margin: 2rem auto;
|
margin: 2rem auto;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
}
|
|
||||||
|
|
||||||
&.info {
|
|
||||||
background-color: $warning-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.success {
|
|
||||||
background-color: $success-color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
|
@ -24,6 +24,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select {
|
||||||
|
@ -31,12 +34,13 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--input-color);
|
background-color: var(--input-color);
|
||||||
border: 1px solid var(--input-active-color) !important;
|
border: 1px solid transparent !important;
|
||||||
color: var(--input-font-color);
|
color: var(--input-font-color);
|
||||||
@include transition() {}
|
@include transition() {}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: unset;
|
outline: unset;
|
||||||
|
border-color: var(--input-active-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,4 +74,78 @@
|
||||||
background-size: 1.2rem;
|
background-size: 1.2rem;
|
||||||
padding-right: 2.4rem;
|
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%;
|
||||||
|
}
|
||||||
|
|
|
@ -7,10 +7,19 @@ html {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-top: 3.5rem;
|
padding-top: 3.5rem;
|
||||||
font-family: 'lucioleregular';
|
font-family: 'open_sansregular';
|
||||||
|
font-weight: 400;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
@include transition();
|
@include transition() {}
|
||||||
|
|
||||||
|
&.is-accessible {
|
||||||
|
font-family: 'lucioleregular';
|
||||||
|
|
||||||
|
.text-justify {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
footer.footer {
|
footer.footer {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
|
|
@ -1,76 +1,65 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'icon';
|
font-family: 'icon';
|
||||||
src: url('/font/icon.eot?15219908');
|
src: url('/font/icon.eot?80770511');
|
||||||
src: url('/font/icon.eot?15219908#iefix') format('embedded-opentype'),
|
src: url('/font/icon.eot?80770511#iefix') format('embedded-opentype'),
|
||||||
url('/font/icon.woff2?15219908') format('woff2'),
|
url('/font/icon.woff2?80770511') format('woff2'),
|
||||||
url('/font/icon.woff?15219908') format('woff'),
|
url('/font/icon.woff?80770511') format('woff'),
|
||||||
url('/font/icon.ttf?15219908') format('truetype'),
|
url('/font/icon.ttf?80770511') format('truetype'),
|
||||||
url('/font/icon.svg?15219908#icon') format('svg');
|
url('/font/icon.svg?80770511#icon') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: 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 */
|
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||||
/*
|
|
||||||
@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-family: "icon";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
speak: never;
|
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: inherit;
|
text-decoration: inherit;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
margin-right: .2em;
|
margin-right: .2em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* opacity: .8; */
|
|
||||||
|
|
||||||
/* For safety - reset parent styles, that can break glyph codes*/
|
|
||||||
font-variant: normal;
|
font-variant: normal;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
||||||
/* fix buttons height, for twitter bootstrap */
|
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
|
|
||||||
/* Animation center compensation - margins should be symmetric */
|
|
||||||
/* remove if not needed */
|
|
||||||
margin-left: .2em;
|
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;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
/* Uncomment for 3D effect */
|
.icon-plus:before { content: '\e800'; } /* '' */
|
||||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
.icon-user:before { content: '\e801'; } /* '' */
|
||||||
}
|
.icon-search:before { content: '\e802'; } /* '' */
|
||||||
|
.icon-mail:before { content: '\e803'; } /* '' */
|
||||||
|
.icon-link:before { content: '\e804'; } /* '' */
|
||||||
|
.icon-heart:before { content: '\e805'; } /* '' */
|
||||||
|
.icon-eye:before { content: '\e806'; } /* '' */
|
||||||
|
.icon-left-open:before { content: '\e807'; } /* '' */
|
||||||
|
.icon-right-open:before { content: '\e808'; } /* '' */
|
||||||
|
.icon-export:before { content: '\e809'; } /* '' */
|
||||||
|
.icon-spin:before { content: '\e839'; } /* '' */
|
||||||
|
.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'; } /* '' */
|
||||||
|
|
||||||
.icon-plus:before { content: '\e800'; } /* '' */
|
.animate-spin {
|
||||||
.icon-user:before { content: '\e801'; } /* '' */
|
animation: spin 2s infinite linear;
|
||||||
.icon-search:before { content: '\e802'; } /* '' */
|
display: inline-block;
|
||||||
.icon-mail:before { content: '\e803'; } /* '' */
|
}
|
||||||
.icon-link:before { content: '\e804'; } /* '' */
|
@keyframes spin {
|
||||||
.icon-heart:before { content: '\e805'; } /* '' */
|
0% {
|
||||||
.icon-eye:before { content: '\e806'; } /* '' */
|
transform: rotate(0deg);
|
||||||
.icon-left-open:before { content: '\e807'; } /* '' */
|
}
|
||||||
.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'; } /* '' */
|
|
||||||
.icon-moon:before { content: '\f186'; } /* '' */
|
|
||||||
.icon-share:before { content: '\f1e0'; } /* '' */
|
|
||||||
.icon-trash:before { content: '\f1f8'; } /* '' */
|
|
||||||
.icon-blind:before { content: '\f29d'; } /* '' */
|
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(359deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,28 @@
|
||||||
@import '../node_modules/knacss/sass/knacss.scss';
|
// @use '../node_modules/knacss/sass/knacss.scss';
|
||||||
|
|
||||||
|
// NOYAU
|
||||||
|
@import "../node_modules/knacss/sass/abstracts/variables-sass";
|
||||||
|
@import "../node_modules/knacss/sass/abstracts/mixins-sass";
|
||||||
|
|
||||||
|
@import "../node_modules/knacss/sass/base/reset-base";
|
||||||
|
@import "../node_modules/knacss/sass/base/reset-accessibility";
|
||||||
|
@import "../node_modules/knacss/sass/base/reset-forms";
|
||||||
|
@import "../node_modules/knacss/sass/base/reset-print";
|
||||||
|
@import "../node_modules/knacss/sass/base/layout";
|
||||||
|
|
||||||
|
// UTILITAIRES
|
||||||
|
@import "../node_modules/knacss/sass/utils/utils-global";
|
||||||
|
@import "../node_modules/knacss/sass/utils/utils-font-sizes";
|
||||||
|
@import "../node_modules/knacss/sass/utils/utils-spacers";
|
||||||
|
@import "../node_modules/knacss/sass/utils/grillade";
|
||||||
|
|
||||||
|
// COMPOSANTS (à ajouter au besoin)
|
||||||
|
// @import "../node_modules/knacss/sass/components/button";
|
||||||
|
// @import "components/burger";
|
||||||
|
// @import "../node_modules/knacss/sass/components/checkbox";
|
||||||
|
@import "../node_modules/knacss/sass/components/radio";
|
||||||
|
// @import "../node_modules/knacss/sass/components/select";
|
||||||
|
// @import "components/quote";
|
||||||
|
|
||||||
// SPÉCIFIQUE AU SITE
|
// SPÉCIFIQUE AU SITE
|
||||||
@import './fonts';
|
@import './fonts';
|
||||||
|
@ -15,14 +39,11 @@
|
||||||
@import './icons';
|
@import './icons';
|
||||||
@import './list';
|
@import './list';
|
||||||
@import './box';
|
@import './box';
|
||||||
@import './loader';
|
|
||||||
@import './table';
|
|
||||||
|
|
||||||
@import './error';
|
@import './error';
|
||||||
@import './messages.scss';
|
|
||||||
@import './500';
|
@import './500';
|
||||||
@import './home';
|
@import './home';
|
||||||
@import './ajouter-un-album';
|
@import './ajouter-un-album';
|
||||||
@import './collection';
|
@import './collection';
|
||||||
@import './ma-collection-details';
|
@import './ma-collection-details';
|
||||||
@import './composants';
|
@import './composants';
|
|
@ -23,12 +23,6 @@
|
||||||
background-color: var(--default-color);
|
background-color: var(--default-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(4n),
|
|
||||||
&:nth-child(4n-1)
|
|
||||||
{
|
|
||||||
background-color: var(--default-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child,
|
&:first-child,
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
border-top: 2px solid var(--border-color);
|
border-top: 2px solid var(--border-color);
|
||||||
|
@ -42,6 +36,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
font-weight: 800;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
.loader {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.animation {
|
|
||||||
background-image: var(--loader-img);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +1,4 @@
|
||||||
.ma-collection-details {
|
.ma-collection-details {
|
||||||
h1 {
|
|
||||||
i {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.icon-trash {
|
|
||||||
color: $danger-color;
|
|
||||||
@include transition() {}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $danger-color-hl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.icon-refresh {
|
|
||||||
color: $primary-color;
|
|
||||||
@include transition() {}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $primary-color-hl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.galerie {
|
.galerie {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -45,44 +23,33 @@
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
button.close {
|
button.close {
|
||||||
height: 42px;
|
height: 36px;
|
||||||
max-height: 42px;
|
max-height: 36px;
|
||||||
max-width: 42px;
|
max-width: 36px;
|
||||||
min-height: 42px;
|
min-height: 36px;
|
||||||
min-width: 42px;
|
min-width: 36px;
|
||||||
width: 42px;
|
width: 36px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--close-background);
|
background-color: rgba(10,10,10,.6);
|
||||||
right: 12px;
|
right: 12px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
background-color: $white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto 80vw auto;
|
|
||||||
z-index: 1;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 80vh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
i {
|
z-index: 10;
|
||||||
font-size: 1rem;
|
|
||||||
color: $nord4;
|
|
||||||
|
|
||||||
@include respond-to("small-up") {
|
&.previous {
|
||||||
font-size: 2rem;
|
left: 12px;
|
||||||
}
|
}
|
||||||
|
&.next {
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: $nord4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
.message {
|
|
||||||
margin: 8px 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
|
|
||||||
&.error {
|
|
||||||
color: $error-color-hl;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2;
|
z-index: 40;
|
||||||
|
|
||||||
&.is-visible {
|
&.is-visible {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -84,11 +84,6 @@
|
||||||
width: 1200;
|
width: 1200;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.for-image {
|
|
||||||
display: initial;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header,
|
header,
|
||||||
footer {
|
footer {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -121,25 +116,10 @@
|
||||||
border-bottom-left-radius: 6px;
|
border-bottom-left-radius: 6px;
|
||||||
border-bottom-right-radius: 6px;
|
border-bottom-right-radius: 6px;
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
justify-content: end;
|
|
||||||
align-items: baseline;
|
|
||||||
|
|
||||||
.field {
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
padding: 6px;
|
|
||||||
span {
|
|
||||||
padding-left: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:not(:last-child) {
|
.button:not(:last-child) {
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 80vh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,25 +1,21 @@
|
||||||
.navbar {
|
.navbar {
|
||||||
min-height: 3.5rem;
|
min-height: 3.25rem;
|
||||||
background-color: var(--navbar-color);
|
background-color: var(--navbar-color);
|
||||||
box-shadow: rgba(216, 222, 233, 0.15) 0px 5px 10px 0px;
|
box-shadow: rgba(216, 222, 233, 0.15) 0px 5px 10px 0px;
|
||||||
color: rgba(0,0,0,.7);
|
color: rgba(0,0,0,.7);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1;
|
z-index: 30;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@include transition() {}
|
@include transition() {}
|
||||||
|
|
||||||
@include respond-to("medium-up") {
|
@include respond-to("medium-up") {
|
||||||
|
min-height: 3.25rem;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.container {
|
|
||||||
max-width: 1330px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -37,6 +33,7 @@
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
line-height: 1.125;
|
line-height: 1.125;
|
||||||
margin-left: .5rem !important;
|
margin-left: .5rem !important;
|
||||||
@include transition() {}
|
@include transition() {}
|
||||||
|
@ -57,7 +54,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 3.25rem;
|
width: 3.25rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
color: var(--font-color);
|
color: rgba(0,0,0,.7);
|
||||||
|
|
||||||
@include respond-to("medium-up") {
|
@include respond-to("medium-up") {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -117,6 +114,7 @@
|
||||||
@include respond-to("medium-up") {
|
@include respond-to("medium-up") {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
color: rgba(0,0,0,.7);
|
||||||
|
|
||||||
.navbar-dropdown {
|
.navbar-dropdown {
|
||||||
background-color: var(--default-color);
|
background-color: var(--default-color);
|
||||||
|
@ -126,17 +124,17 @@
|
||||||
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
||||||
display: none;
|
display: none;
|
||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
|
left: 0;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--default-color);
|
|
||||||
|
|
||||||
.navbar-link {
|
.navbar-link {
|
||||||
background-color: var(--default-hl-color);
|
background-color: var(--default-hl-color);
|
||||||
color: var(--hover-font-color);
|
color: rgba(0,0,0,.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dropdown {
|
.navbar-dropdown {
|
||||||
|
@ -165,29 +163,6 @@
|
||||||
@include respond-to("medium-up") {
|
@include respond-to("medium-up") {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&::after {
|
|
||||||
border: 3px solid transparent;
|
|
||||||
border-radius: 2px;
|
|
||||||
border-right: 0;
|
|
||||||
border-top: 0;
|
|
||||||
content: " ";
|
|
||||||
display: block;
|
|
||||||
height: .625em;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
transform-origin: center;
|
|
||||||
width: .625em;
|
|
||||||
border-color: var(--secondary-color);
|
|
||||||
margin-top: -0.375em;
|
|
||||||
right: 1.125em;
|
|
||||||
|
|
||||||
@include respond-to("medium-up") {
|
|
||||||
border-color: var(--font-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -198,7 +173,28 @@
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: 3px solid transparent;
|
||||||
|
border-radius: 2px;
|
||||||
|
border-right: 0;
|
||||||
|
border-top: 0;
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
height: .625em;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
transform-origin: center;
|
||||||
|
width: .625em;
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
margin-top: -0.375em;
|
||||||
|
right: 1.125em;
|
||||||
|
|
||||||
|
@include respond-to("medium-up") {
|
||||||
|
border-color: rgba(0,0,0,.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-menu {
|
.navbar-menu {
|
||||||
|
@ -256,13 +252,6 @@
|
||||||
padding-bottom: .5rem;
|
padding-bottom: .5rem;
|
||||||
padding-top: .5rem;
|
padding-top: .5rem;
|
||||||
|
|
||||||
hr {
|
|
||||||
background-color: var(--font-color);
|
|
||||||
border: none;
|
|
||||||
height: 2px;
|
|
||||||
margin: .5rem 0 0 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-item {
|
.navbar-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
|
@ -277,14 +266,11 @@
|
||||||
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
box-shadow: 0 8px 8px rgba(10,10,10,.1);
|
||||||
display: none;
|
display: none;
|
||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
right: 0;
|
left: 0;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
|
z-index: 20;
|
||||||
hr {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-item {
|
.navbar-item {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,19 +3,16 @@
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
max-width: 360px;
|
max-width: 360px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 10;
|
z-index: 31;
|
||||||
right: 30px;
|
right: 30px;
|
||||||
top: 30px;
|
top: 30px;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
|
||||||
padding: 1.25rem 2.5rem 1.25rem 1.5rem;
|
padding: 1.25rem 2.5rem 1.25rem 1.5rem;
|
||||||
|
background-color: $danger-color;
|
||||||
|
color: $button-alternate-color;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
&.error {
|
|
||||||
background-color: $danger-color;
|
|
||||||
color: $button-alternate-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
background-color: $success-color;
|
background-color: $success-color;
|
||||||
color: $button-font-color;
|
color: $button-font-color;
|
||||||
|
|
82
src/app.js
82
src/app.js
|
@ -7,28 +7,24 @@ import flash from "connect-flash";
|
||||||
import session from "express-session";
|
import session from "express-session";
|
||||||
import MongoStore from "connect-mongo";
|
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";
|
import { isXhr } from "./helpers";
|
||||||
|
|
||||||
import indexRouter from "./routes";
|
import indexRouter from "./routes";
|
||||||
import maCollectionRouter from "./routes/ma-collection";
|
import maCollectionRouter from "./routes/ma-collection";
|
||||||
import monCompteRouter from "./routes/mon-compte";
|
|
||||||
import collectionRouter from "./routes/collection";
|
import collectionRouter from "./routes/collection";
|
||||||
|
|
||||||
import importJobsRouter from "./routes/jobs";
|
|
||||||
|
|
||||||
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
|
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
|
||||||
import importSearchRouterApiV1 from "./routes/api/v1/search";
|
import importSearchRouterApiV1 from "./routes/api/v1/search";
|
||||||
import importMastodonRouterApiV1 from "./routes/api/v1/mastodon";
|
|
||||||
import importMeRouterApiV1 from "./routes/api/v1/me";
|
import importMeRouterApiV1 from "./routes/api/v1/me";
|
||||||
import importContactRouterApiV1 from "./routes/api/v1/contact";
|
|
||||||
|
|
||||||
passportConfig(passport);
|
// Mongoose schema init
|
||||||
|
require("./models/users");
|
||||||
|
require("./models/albums");
|
||||||
|
|
||||||
|
require("./libs/passport")(passport);
|
||||||
|
|
||||||
mongoose.set("strictQuery", false);
|
|
||||||
mongoose
|
mongoose
|
||||||
.connect(mongoDbUri, { useNewUrlParser: true, useUnifiedTopology: true })
|
.connect(mongoDbUri, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@ -37,7 +33,7 @@ mongoose
|
||||||
|
|
||||||
const sess = {
|
const sess = {
|
||||||
cookie: {
|
cookie: {
|
||||||
maxAge: 604800000, // INFO: 7 jours
|
maxAge: 86400000,
|
||||||
},
|
},
|
||||||
secret,
|
secret,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
|
@ -50,10 +46,10 @@ const sess = {
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(flash());
|
app.use(flash());
|
||||||
app.use(express.json({ limit: "50mb" }));
|
|
||||||
app.use(express.urlencoded({ extended: false, limit: "50mb" }));
|
|
||||||
|
|
||||||
app.use(session(sess));
|
app.use(session(sess));
|
||||||
|
|
||||||
|
@ -77,17 +73,21 @@ app.set("views", path.join(__dirname, "../views"));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
|
||||||
app.use(express.static(path.join(__dirname, "../public")));
|
app.use(express.static(path.join(__dirname, "../public")));
|
||||||
|
app.use(
|
||||||
|
"/libs/vue",
|
||||||
|
express.static(path.join(__dirname, "../node_modules/vue/dist"))
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
"/libs/axios",
|
||||||
|
express.static(path.join(__dirname, "../node_modules/axios/dist"))
|
||||||
|
);
|
||||||
|
|
||||||
app.use("/", indexRouter);
|
app.use("/", indexRouter);
|
||||||
app.use("/mon-compte", monCompteRouter);
|
|
||||||
app.use("/ma-collection", maCollectionRouter);
|
app.use("/ma-collection", maCollectionRouter);
|
||||||
app.use("/collection", collectionRouter);
|
app.use("/collection", collectionRouter);
|
||||||
app.use("/jobs", importJobsRouter);
|
|
||||||
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
||||||
app.use("/api/v1/search", importSearchRouterApiV1);
|
app.use("/api/v1/search", importSearchRouterApiV1);
|
||||||
app.use("/api/v1/mastodon", importMastodonRouterApiV1);
|
|
||||||
app.use("/api/v1/me", importMeRouterApiV1);
|
app.use("/api/v1/me", importMeRouterApiV1);
|
||||||
app.use("/api/v1/contact", importContactRouterApiV1);
|
|
||||||
|
|
||||||
// Handle 404
|
// Handle 404
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
|
@ -96,22 +96,15 @@ app.use((req, res) => {
|
||||||
} else {
|
} else {
|
||||||
res.status(404).render("index", {
|
res.status(404).render("index", {
|
||||||
page: { title: `404: Cette page n'existe pas.` },
|
page: { title: `404: Cette page n'existe pas.` },
|
||||||
viewname: "error",
|
|
||||||
session: req.session || null,
|
|
||||||
flash: {
|
|
||||||
info: req.flash("info"),
|
|
||||||
error: [
|
|
||||||
...req.flash("error"),
|
|
||||||
...(req.session?.flash?.error || []),
|
|
||||||
],
|
|
||||||
success: req.flash("success"),
|
|
||||||
},
|
|
||||||
query: req.query,
|
|
||||||
params: req.params,
|
|
||||||
user: req.user,
|
|
||||||
config,
|
|
||||||
getBaseUrl: null,
|
|
||||||
errorCode: 404,
|
errorCode: 404,
|
||||||
|
viewname: "error",
|
||||||
|
user: req.user || null,
|
||||||
|
config,
|
||||||
|
session: req.session || null,
|
||||||
|
flashInfo: null,
|
||||||
|
query: null,
|
||||||
|
params: null,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -128,28 +121,19 @@ app.use((error, req, res, next) => {
|
||||||
title: error.title || "500: Oups… le serveur a crashé !",
|
title: error.title || "500: Oups… le serveur a crashé !",
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
viewname: "error",
|
|
||||||
session: req.session || null,
|
|
||||||
flash: {
|
|
||||||
info: req.flash("info"),
|
|
||||||
error: [
|
|
||||||
...req.flash("error"),
|
|
||||||
...(req.session?.flash?.error || []),
|
|
||||||
],
|
|
||||||
success: req.flash("success"),
|
|
||||||
},
|
|
||||||
query: req.query,
|
|
||||||
params: req.params,
|
|
||||||
user: req.user,
|
|
||||||
config,
|
|
||||||
getBaseUrl: null,
|
|
||||||
errorCode: error.errorCode || 500,
|
errorCode: error.errorCode || 500,
|
||||||
|
viewname: "error",
|
||||||
|
user: req.user || null,
|
||||||
|
config,
|
||||||
|
session: req.session || null,
|
||||||
|
flashInfo: null,
|
||||||
|
query: null,
|
||||||
|
params: null,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Server listening on port ${port}!`);
|
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|
|
@ -8,25 +8,4 @@ module.exports = {
|
||||||
matomoUrl: process.env.MATOMO_URL || "",
|
matomoUrl: process.env.MATOMO_URL || "",
|
||||||
matomoId: process.env.MATOMO_ID || "",
|
matomoId: process.env.MATOMO_ID || "",
|
||||||
siteName: process.env.SITE_NAME || "MusicTopus",
|
siteName: process.env.SITE_NAME || "MusicTopus",
|
||||||
awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
awsSecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
s3BaseFolder: process.env.S3_BASEFOLDER || "dev",
|
|
||||||
s3Bucket: process.env.S3_BUCKET || "musictopus",
|
|
||||||
s3Endpoint: process.env.S3_ENDPOINT || "s3.fr-par.scw.cloud",
|
|
||||||
s3Signature: process.env.S3_SIGNATURE || "s3v4",
|
|
||||||
jobsHeaderKey: process.env.JOBS_HEADER_KEY || "musictopus",
|
|
||||||
jobsHeaderValue:
|
|
||||||
process.env.JOBS_HEADER_VALUE || "ooYee9xok7eigo2shiePohyoGh1eepew",
|
|
||||||
registrationOpen:
|
|
||||||
(process.env.REGISTRATION_OPEN || "true").toLowerCase() === "true",
|
|
||||||
mailMethod: process.env.MAIL_METHOD || "formspree",
|
|
||||||
smtpConfig: {
|
|
||||||
host: process.env.MAIL_HOST,
|
|
||||||
port: process.env.MAIL_PORT,
|
|
||||||
auth: {
|
|
||||||
user: process.env.MAIL_USER,
|
|
||||||
pass: process.env.MAIL_PASSWORD,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mailTo: process.env.MAIL_TO,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,25 +5,13 @@ import { discogsToken } from "../config";
|
||||||
|
|
||||||
export const getBaseUrl = (req) => `${req.protocol}://${req.get("host")}`;
|
export const getBaseUrl = (req) => `${req.protocol}://${req.get("host")}`;
|
||||||
|
|
||||||
export const searchSong = async (q, format, year, country) => {
|
export const searchSong = async (q) => {
|
||||||
const dis = new Discogs({ userToken: discogsToken }).database();
|
const dis = new Discogs({ userToken: discogsToken }).database();
|
||||||
|
|
||||||
const params = {
|
const res = await dis.search({
|
||||||
q,
|
q,
|
||||||
type: "release",
|
type: "release",
|
||||||
};
|
});
|
||||||
|
|
||||||
if (format) {
|
|
||||||
params.format = format;
|
|
||||||
}
|
|
||||||
if (year) {
|
|
||||||
params.year = year;
|
|
||||||
}
|
|
||||||
if (country) {
|
|
||||||
params.country = country;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await dis.search(params);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
@ -33,11 +21,6 @@ export const getAlbumDetails = async (id) => {
|
||||||
|
|
||||||
const res = await dis.getRelease(id);
|
const res = await dis.getRelease(id);
|
||||||
|
|
||||||
if (res.released && res.released.includes("-00")) {
|
|
||||||
const [year, month] = res.released.split("-");
|
|
||||||
res.released = new Date(year, parseInt(month, 10) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
import { S3Client } from "@aws-sdk/client-s3";
|
|
||||||
import { Upload } from "@aws-sdk/lib-storage";
|
|
||||||
import fs from "fs";
|
|
||||||
import path from "path";
|
|
||||||
import axios from "axios";
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
|
|
||||||
import {
|
|
||||||
awsAccessKeyId,
|
|
||||||
awsSecretAccessKey,
|
|
||||||
s3BaseFolder,
|
|
||||||
s3Endpoint,
|
|
||||||
s3Bucket,
|
|
||||||
// s3Signature,
|
|
||||||
} from "../config";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fonction permettant de stocker un fichier local sur S3
|
|
||||||
* @param {String} filename
|
|
||||||
* @param {String} file
|
|
||||||
* @param {Boolean} deleteFile
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
export const uploadFromFile = async (filename, file, deleteFile = false) => {
|
|
||||||
const data = await fs.readFileSync(file);
|
|
||||||
const base64data = Buffer.from(data, "binary");
|
|
||||||
const dest = path.join(s3BaseFolder, filename);
|
|
||||||
|
|
||||||
const multipartUpload = new Upload({
|
|
||||||
client: new S3Client({
|
|
||||||
region: "fr-par",
|
|
||||||
endpoint: `https://${s3Endpoint}`,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: awsAccessKeyId,
|
|
||||||
secretAccessKey: awsSecretAccessKey,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
params: {
|
|
||||||
Bucket: s3Bucket,
|
|
||||||
Key: dest,
|
|
||||||
Body: base64data,
|
|
||||||
ACL: "public-read",
|
|
||||||
endpoint: s3Endpoint,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await multipartUpload.done();
|
|
||||||
|
|
||||||
if (deleteFile) {
|
|
||||||
fs.unlinkSync(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return `https://${s3Bucket}.${s3Endpoint}/${dest}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fonction permettant de stocker un fichier provenant d'une URL sur S3
|
|
||||||
* @param {String} url
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
export const uploadFromUrl = async (url) => {
|
|
||||||
const filename = `${uuid()}.jpg`;
|
|
||||||
const file = `/tmp/${filename}`;
|
|
||||||
|
|
||||||
const { data } = await axios.get(url, {
|
|
||||||
headers: {
|
|
||||||
"User-Agent":
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/117.0",
|
|
||||||
},
|
|
||||||
responseType: "arraybuffer",
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.writeFileSync(file, data);
|
|
||||||
|
|
||||||
return uploadFromFile(filename, file, true);
|
|
||||||
};
|
|
|
@ -1,13 +1,11 @@
|
||||||
/* eslint-disable func-names */
|
/* eslint-disable func-names */
|
||||||
import { Strategy as LocalStrategy } from "passport-local";
|
const mongoose = require("mongoose");
|
||||||
import { BasicStrategy } from "passport-http";
|
const LocalStrategy = require("passport-local").Strategy;
|
||||||
import { Strategy as CustomStrategy } from "passport-custom";
|
const { BasicStrategy } = require("passport-http");
|
||||||
|
|
||||||
import Users from "../models/users";
|
const Users = mongoose.model("Users");
|
||||||
|
|
||||||
import { jobsHeaderKey, jobsHeaderValue } from "../config";
|
module.exports = function (passport) {
|
||||||
|
|
||||||
export default (passport) => {
|
|
||||||
passport.serializeUser((user, done) => {
|
passport.serializeUser((user, done) => {
|
||||||
done(null, user);
|
done(null, user);
|
||||||
});
|
});
|
||||||
|
@ -57,17 +55,4 @@ export default (passport) => {
|
||||||
.catch(done);
|
.catch(done);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
passport.use(
|
|
||||||
"jobs",
|
|
||||||
new CustomStrategy((req, next) => {
|
|
||||||
const apiKey = req.headers[jobsHeaderKey];
|
|
||||||
|
|
||||||
if (apiKey === jobsHeaderValue) {
|
|
||||||
return next(null, {
|
|
||||||
username: "jobs",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return next(null, false, "Oops! Identifiants incorrects");
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,456 +0,0 @@
|
||||||
import { utcToZonedTime } from "date-fns-tz";
|
|
||||||
import setHours from "date-fns/setHours";
|
|
||||||
import xl from "excel4node";
|
|
||||||
|
|
||||||
class Export {
|
|
||||||
/**
|
|
||||||
* Méthode permettant de remplacer certains cartactères par leur équivalents html
|
|
||||||
* @param {String} str
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
static replaceSpecialChars(str) {
|
|
||||||
if (!str) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
let final = str.toString();
|
|
||||||
const find = ["&", "<", ">"];
|
|
||||||
const replace = ["&", "<", ">"];
|
|
||||||
|
|
||||||
for (let i = 0; i < find.length; i += 1) {
|
|
||||||
final = final.replace(new RegExp(find[i], "g"), replace[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return final;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Méthode permettant de convertir les rows en csv
|
|
||||||
* @param {Array} rows
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
static async convertToCsv(rows) {
|
|
||||||
let data =
|
|
||||||
"Artiste;Titre;Genre;Styles;Pays;Année;Date de sortie;Format\n\r";
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i += 1) {
|
|
||||||
const {
|
|
||||||
artists_sort,
|
|
||||||
title,
|
|
||||||
genres,
|
|
||||||
styles,
|
|
||||||
country,
|
|
||||||
year,
|
|
||||||
released,
|
|
||||||
formats,
|
|
||||||
} = rows[i];
|
|
||||||
|
|
||||||
let format = "";
|
|
||||||
for (let j = 0; j < formats.length; j += 1) {
|
|
||||||
format += `${format !== "" ? ", " : ""}${formats[j].name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
data += `${artists_sort};${title};${genres.join()};${styles.join()};${country};${year};${released};${format}\n\r`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Méthode permettant de convertir les rows en fichier xls
|
|
||||||
* @param {Array} rows
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
static async convertToXls(rows) {
|
|
||||||
const wb = new xl.Workbook();
|
|
||||||
const ws = wb.addWorksheet("MusicTopus");
|
|
||||||
|
|
||||||
const headerStyle = wb.createStyle({
|
|
||||||
font: {
|
|
||||||
color: "#FFFFFF",
|
|
||||||
size: 11,
|
|
||||||
},
|
|
||||||
fill: {
|
|
||||||
type: "pattern",
|
|
||||||
patternType: "solid",
|
|
||||||
bgColor: "#595959",
|
|
||||||
fgColor: "#595959",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const style = wb.createStyle({
|
|
||||||
font: {
|
|
||||||
color: "#000000",
|
|
||||||
size: 11,
|
|
||||||
},
|
|
||||||
numberFormat: "0000",
|
|
||||||
});
|
|
||||||
|
|
||||||
const header = [
|
|
||||||
"Artiste",
|
|
||||||
"Titre",
|
|
||||||
"Genre",
|
|
||||||
"Styles",
|
|
||||||
"Pays",
|
|
||||||
"Année",
|
|
||||||
"Date de sortie",
|
|
||||||
"Format",
|
|
||||||
];
|
|
||||||
for (let i = 0; i < header.length; i += 1) {
|
|
||||||
ws.cell(1, i + 1)
|
|
||||||
.string(header[i])
|
|
||||||
.style(headerStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i += 1) {
|
|
||||||
const currentRow = i + 2;
|
|
||||||
const {
|
|
||||||
artists_sort,
|
|
||||||
title,
|
|
||||||
genres,
|
|
||||||
styles,
|
|
||||||
country,
|
|
||||||
year,
|
|
||||||
released,
|
|
||||||
formats,
|
|
||||||
} = rows[i];
|
|
||||||
|
|
||||||
let format = "";
|
|
||||||
for (let j = 0; j < formats.length; j += 1) {
|
|
||||||
format += `${format !== "" ? ", " : ""}${formats[j].name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.cell(currentRow, 1).string(artists_sort).style(style);
|
|
||||||
ws.cell(currentRow, 2).string(title).style(style);
|
|
||||||
ws.cell(currentRow, 3).string(genres.join()).style(style);
|
|
||||||
ws.cell(currentRow, 4).string(styles.join()).style(style);
|
|
||||||
if (country) {
|
|
||||||
ws.cell(currentRow, 5).string(country).style(style);
|
|
||||||
}
|
|
||||||
if (year) {
|
|
||||||
ws.cell(currentRow, 6).number(year).style(style);
|
|
||||||
}
|
|
||||||
if (released) {
|
|
||||||
ws.cell(currentRow, 7)
|
|
||||||
.date(
|
|
||||||
setHours(utcToZonedTime(released, "Europe/Paris"), 12)
|
|
||||||
)
|
|
||||||
.style({ numberFormat: "dd/mm/yyyy" });
|
|
||||||
}
|
|
||||||
ws.cell(currentRow, 8).string(format).style(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Méthode permettant de convertir les rows en csv pour importer dans MusicTopus
|
|
||||||
* @param {Array} rows
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
static async convertToXml(rows) {
|
|
||||||
let data = '<?xml version="1.0" encoding="UTF-8"?>\n\r<albums>';
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i += 1) {
|
|
||||||
const {
|
|
||||||
discogsId,
|
|
||||||
year,
|
|
||||||
released,
|
|
||||||
uri,
|
|
||||||
artists,
|
|
||||||
artists_sort,
|
|
||||||
labels,
|
|
||||||
series,
|
|
||||||
companies,
|
|
||||||
formats,
|
|
||||||
title,
|
|
||||||
country,
|
|
||||||
notes,
|
|
||||||
identifiers,
|
|
||||||
videos,
|
|
||||||
genres,
|
|
||||||
styles,
|
|
||||||
tracklist,
|
|
||||||
extraartists,
|
|
||||||
images,
|
|
||||||
thumb,
|
|
||||||
} = rows[i];
|
|
||||||
|
|
||||||
let artistsList = "";
|
|
||||||
let labelList = "";
|
|
||||||
let serieList = "";
|
|
||||||
let companiesList = "";
|
|
||||||
let formatsList = "";
|
|
||||||
let identifiersList = "";
|
|
||||||
let videosList = "";
|
|
||||||
let genresList = "";
|
|
||||||
let stylesList = "";
|
|
||||||
let tracklistList = "";
|
|
||||||
let extraartistsList = "";
|
|
||||||
let imagesList = "";
|
|
||||||
|
|
||||||
for (let j = 0; j < artists.length; j += 1) {
|
|
||||||
artistsList += `<artist>
|
|
||||||
<name>${Export.replaceSpecialChars(artists[j].name)}</name>
|
|
||||||
<anv>${Export.replaceSpecialChars(artists[j].anv)}</anv>
|
|
||||||
<join>${Export.replaceSpecialChars(artists[j].join)}</join>
|
|
||||||
<role>${Export.replaceSpecialChars(artists[j].role)}</role>
|
|
||||||
<tracks>${Export.replaceSpecialChars(artists[j].tracks)}</tracks>
|
|
||||||
<id>${Export.replaceSpecialChars(artists[j].id)}</id>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
artists[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<thumbnail_url>${Export.replaceSpecialChars(
|
|
||||||
artists[j].thumbnail_url
|
|
||||||
)}</thumbnail_url>
|
|
||||||
</artist>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < labels.length; j += 1) {
|
|
||||||
labelList += `<label>
|
|
||||||
<name>${Export.replaceSpecialChars(labels[j].name)}</name>
|
|
||||||
<catno>${Export.replaceSpecialChars(labels[j].catno)}</catno>
|
|
||||||
<entity_type>${Export.replaceSpecialChars(
|
|
||||||
labels[j].entity_type
|
|
||||||
)}</entity_type>
|
|
||||||
<entity_type_name>${Export.replaceSpecialChars(
|
|
||||||
labels[j].entity_type
|
|
||||||
)}</entity_type_name>
|
|
||||||
<id>${Export.replaceSpecialChars(labels[j].id)}</id>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
labels[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<thumbnail_url>${Export.replaceSpecialChars(
|
|
||||||
labels[j].thumbnail_url
|
|
||||||
)}</thumbnail_url>
|
|
||||||
</label>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < series.length; j += 1) {
|
|
||||||
serieList += `<serie>
|
|
||||||
<name>${Export.replaceSpecialChars(series[j].name)}</name>
|
|
||||||
<catno>${Export.replaceSpecialChars(series[j].catno)}</catno>
|
|
||||||
<entity_type>${Export.replaceSpecialChars(
|
|
||||||
series[j].entity_type
|
|
||||||
)}</entity_type>
|
|
||||||
<entity_type_name>${Export.replaceSpecialChars(
|
|
||||||
series[j].entity_type_name
|
|
||||||
)}</entity_type_name>
|
|
||||||
<id>${Export.replaceSpecialChars(series[j].id)}</id>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
series[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<thumbnail_url>${Export.replaceSpecialChars(
|
|
||||||
series[j].thumbnail_url
|
|
||||||
)}</thumbnail_url>
|
|
||||||
</serie>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < companies.length; j += 1) {
|
|
||||||
companiesList += `<company>
|
|
||||||
<name>${Export.replaceSpecialChars(companies[j].name)}</name>
|
|
||||||
<catno>${Export.replaceSpecialChars(companies[j].catno)}</catno>
|
|
||||||
<entity_type>${Export.replaceSpecialChars(
|
|
||||||
companies[j].entity_type
|
|
||||||
)}</entity_type>
|
|
||||||
<entity_type_name>${Export.replaceSpecialChars(
|
|
||||||
companies[j].entity_type_name
|
|
||||||
)}</entity_type_name>
|
|
||||||
<id>${Export.replaceSpecialChars(companies[j].id)}</id>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
companies[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<thumbnail_url>${Export.replaceSpecialChars(
|
|
||||||
companies[j].thumbnail_url
|
|
||||||
)}</thumbnail_url>
|
|
||||||
</company>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < formats.length; j += 1) {
|
|
||||||
let descriptions = "";
|
|
||||||
if (formats[j].descriptions) {
|
|
||||||
for (
|
|
||||||
let k = 0;
|
|
||||||
k < formats[j].descriptions.length;
|
|
||||||
k += 1
|
|
||||||
) {
|
|
||||||
descriptions += `<description>${formats[j].descriptions[k]}</description>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
formatsList += `<format>
|
|
||||||
<name>${Export.replaceSpecialChars(formats[j].name)}</name>
|
|
||||||
<qte>${Export.replaceSpecialChars(formats[j].qty)}</qte>
|
|
||||||
<text>${Export.replaceSpecialChars(formats[j].text)}</text>
|
|
||||||
<descriptions>
|
|
||||||
${descriptions}
|
|
||||||
</descriptions>
|
|
||||||
</format>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < identifiers.length; j += 1) {
|
|
||||||
identifiersList += `<identifier>
|
|
||||||
<type>${Export.replaceSpecialChars(identifiers[j].type)}</type>
|
|
||||||
<value>${Export.replaceSpecialChars(identifiers[j].value)}</value>
|
|
||||||
<description>${Export.replaceSpecialChars(
|
|
||||||
identifiers[j].description
|
|
||||||
)}</description>
|
|
||||||
</identifier>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < videos.length; j += 1) {
|
|
||||||
videosList += `<video embed="${videos[j].embed}">
|
|
||||||
<uri>${Export.replaceSpecialChars(videos[j].uri)}</uri>
|
|
||||||
<title>${Export.replaceSpecialChars(videos[j].title)}</title>
|
|
||||||
<description>${Export.replaceSpecialChars(
|
|
||||||
videos[j].description
|
|
||||||
)}</description>
|
|
||||||
<duration>${Export.replaceSpecialChars(
|
|
||||||
videos[j].duration
|
|
||||||
)}</duration>
|
|
||||||
</video>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < genres.length; j += 1) {
|
|
||||||
genresList += `<genre>${Export.replaceSpecialChars(
|
|
||||||
genres[j]
|
|
||||||
)}</genre>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < styles.length; j += 1) {
|
|
||||||
stylesList += `<style>${Export.replaceSpecialChars(
|
|
||||||
styles[j]
|
|
||||||
)}</style>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < tracklist.length; j += 1) {
|
|
||||||
tracklistList += `<tracklist position="${
|
|
||||||
tracklist[j].position
|
|
||||||
}" type="${tracklist[j].type_}" duration="${
|
|
||||||
tracklist[j].duration
|
|
||||||
}">
|
|
||||||
${Export.replaceSpecialChars(tracklist[j].title)}
|
|
||||||
</tracklist>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < extraartists.length; j += 1) {
|
|
||||||
extraartistsList += `<extraartist>
|
|
||||||
<name>${Export.replaceSpecialChars(extraartists[j].name)}</name>
|
|
||||||
<anv>${Export.replaceSpecialChars(extraartists[j].anv)}</anv>
|
|
||||||
<join>${Export.replaceSpecialChars(extraartists[j].join)}</join>
|
|
||||||
<role>${Export.replaceSpecialChars(extraartists[j].role)}</role>
|
|
||||||
<tracks>${Export.replaceSpecialChars(
|
|
||||||
extraartists[j].tracks
|
|
||||||
)}</tracks>
|
|
||||||
<id>${Export.replaceSpecialChars(extraartists[j].id)}</id>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
extraartists[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<thumbnail_url>${Export.replaceSpecialChars(
|
|
||||||
extraartists[j].thumbnail_url
|
|
||||||
)}</thumbnail_url>
|
|
||||||
</extraartist>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < images.length; j += 1) {
|
|
||||||
imagesList += `<image type="${images[j].type}" width="${
|
|
||||||
images[j].width
|
|
||||||
}" height="${images[j].height}">
|
|
||||||
<uri>${Export.replaceSpecialChars(images[j].uri)}</uri>
|
|
||||||
<resource_url>${Export.replaceSpecialChars(
|
|
||||||
images[j].resource_url
|
|
||||||
)}</resource_url>
|
|
||||||
<uri150>${Export.replaceSpecialChars(
|
|
||||||
images[j].resource_url
|
|
||||||
)}</uri150>
|
|
||||||
</image>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
data += `
|
|
||||||
<album>
|
|
||||||
<discogId>${discogsId}</discogId>
|
|
||||||
<title>${Export.replaceSpecialChars(title)}</title>
|
|
||||||
<artists_sort>${Export.replaceSpecialChars(artists_sort)}</artists_sort>
|
|
||||||
<artists>
|
|
||||||
${artistsList}
|
|
||||||
</artists>
|
|
||||||
<year>${year}</year>
|
|
||||||
<country>${Export.replaceSpecialChars(country)}</country>
|
|
||||||
<released>${released}</released>
|
|
||||||
<uri>${uri}</uri>
|
|
||||||
<thumb>${thumb}</thumb>
|
|
||||||
<labels>
|
|
||||||
${labelList}
|
|
||||||
</labels>
|
|
||||||
<series>
|
|
||||||
${serieList}
|
|
||||||
</series>
|
|
||||||
<companies>
|
|
||||||
${companiesList}
|
|
||||||
</companies>
|
|
||||||
<formats>
|
|
||||||
${formatsList}
|
|
||||||
</formats>
|
|
||||||
<notes>${Export.replaceSpecialChars(notes)}</notes>
|
|
||||||
<identifiers>
|
|
||||||
${identifiersList}
|
|
||||||
</identifiers>
|
|
||||||
<videos>
|
|
||||||
${videosList}
|
|
||||||
</videos>
|
|
||||||
<genres>
|
|
||||||
${genresList}
|
|
||||||
</genres>
|
|
||||||
<styles>
|
|
||||||
${stylesList}
|
|
||||||
</styles>
|
|
||||||
<tracklist>
|
|
||||||
${tracklistList}
|
|
||||||
</tracklist>
|
|
||||||
<extraartists>
|
|
||||||
${extraartistsList}
|
|
||||||
</extraartists>
|
|
||||||
<images>
|
|
||||||
${imagesList}
|
|
||||||
</images>
|
|
||||||
</album>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${data}</albums>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Méthode permettant de convertir les rows en csv pour importer dans MusicTopus
|
|
||||||
* @param {Array} rows
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
static async convertToMusicTopus(rows) {
|
|
||||||
let data = "itemId;createdAt;updatedAt\n\r";
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i += 1) {
|
|
||||||
const { discogsId, createdAt, updatedAt } = rows[i];
|
|
||||||
|
|
||||||
data += `${discogsId};${createdAt};${updatedAt}\n\r`;
|
|
||||||
}
|
|
||||||
|
|
||||||
data += "v1.0";
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Export;
|
|
|
@ -1,128 +0,0 @@
|
||||||
/* eslint-disable no-await-in-loop */
|
|
||||||
import ErrorEvent from "../libs/error";
|
|
||||||
import { uploadFromUrl } from "../libs/aws";
|
|
||||||
import { getAlbumDetails } from "../helpers";
|
|
||||||
|
|
||||||
import JobsModel from "../models/jobs";
|
|
||||||
import AlbumsModel from "../models/albums";
|
|
||||||
|
|
||||||
class Jobs {
|
|
||||||
/**
|
|
||||||
* Méthode permettant de télécharger toute les images d'un album
|
|
||||||
* @param {ObjectId} itemId
|
|
||||||
*/
|
|
||||||
static async importAlbumAssets(itemId) {
|
|
||||||
const album = await AlbumsModel.findById(itemId);
|
|
||||||
|
|
||||||
if (!album) {
|
|
||||||
throw new ErrorEvent(
|
|
||||||
404,
|
|
||||||
"Item non trouvé",
|
|
||||||
`L'album avec l'id ${itemId} n'existe plus dans la collection`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = await getAlbumDetails(album.discogsId);
|
|
||||||
|
|
||||||
if (!item) {
|
|
||||||
throw new ErrorEvent(
|
|
||||||
404,
|
|
||||||
"Erreur de communication",
|
|
||||||
"Erreur lors de la récupération des informations sur Discogs"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.thumb) {
|
|
||||||
album.thumb = await uploadFromUrl(item.thumb);
|
|
||||||
album.thumbType = "local";
|
|
||||||
}
|
|
||||||
const { images } = item;
|
|
||||||
if (images && images.length > 0) {
|
|
||||||
for (let i = 0; i < images.length; i += 1) {
|
|
||||||
images[i].uri150 = await uploadFromUrl(images[i].uri150);
|
|
||||||
images[i].uri = await uploadFromUrl(images[i].uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
album.images = images;
|
|
||||||
|
|
||||||
await album.save();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Point d'entrée
|
|
||||||
* @param {String} state
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
async run(state = "NEW") {
|
|
||||||
const job = await JobsModel.findOne({
|
|
||||||
state,
|
|
||||||
tries: {
|
|
||||||
$lte: 5,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!job) {
|
|
||||||
return { message: "All jobs done" };
|
|
||||||
}
|
|
||||||
|
|
||||||
job.state = "IN-PROGRESS";
|
|
||||||
|
|
||||||
await job.save();
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (job.model) {
|
|
||||||
case "Albums":
|
|
||||||
await Jobs.importAlbumAssets(job.id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ErrorEvent(
|
|
||||||
500,
|
|
||||||
"Job inconnu",
|
|
||||||
`Le job avec l'id ${job._id} n'est pas un job valide`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
job.state = "SUCCESS";
|
|
||||||
|
|
||||||
await job.save();
|
|
||||||
|
|
||||||
return this.run(state);
|
|
||||||
} catch (err) {
|
|
||||||
job.state = "ERROR";
|
|
||||||
job.lastTry = new Date();
|
|
||||||
job.lastErrorMessage = err.message;
|
|
||||||
job.tries += 1;
|
|
||||||
|
|
||||||
await job.save();
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Méthode permettant de créer tous les jobs
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
static async populate() {
|
|
||||||
const albums = await AlbumsModel.find();
|
|
||||||
|
|
||||||
for (let i = 0; i < albums.length; i += 1) {
|
|
||||||
const jobData = {
|
|
||||||
model: "Albums",
|
|
||||||
id: albums[i]._id,
|
|
||||||
};
|
|
||||||
|
|
||||||
const job = new JobsModel(jobData);
|
|
||||||
await job.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return { message: `${albums.length} jobs ajouté à la file d'attente` };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Jobs;
|
|
|
@ -1,63 +1,35 @@
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
|
|
||||||
import UsersModel from "../models/users";
|
import UsersModel from "../models/users";
|
||||||
import Pages from "./Pages";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe permettant la gestion de l'utilisateur connecté
|
* Classe permettant la gestion de l'utilisateur connecté
|
||||||
*/
|
*/
|
||||||
class Me extends Pages {
|
class Me {
|
||||||
|
constructor(req) {
|
||||||
|
this.req = req;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Méthode permettant de modifier le profil d'un utilisateur
|
* Méthode permettant de modifier le profil d'un utilisateur
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
async patchMe() {
|
async patchMe() {
|
||||||
const { body } = this.req;
|
const { body, user } = this.req;
|
||||||
const { _id } = this.req.user;
|
|
||||||
|
|
||||||
const schema = Joi.object({
|
const schema = Joi.object({
|
||||||
pagination: Joi.number(),
|
|
||||||
isPublicCollection: Joi.boolean(),
|
isPublicCollection: Joi.boolean(),
|
||||||
oldPassword: Joi.string(),
|
|
||||||
password: Joi.string(),
|
|
||||||
passwordConfirm: Joi.ref("password"),
|
|
||||||
mastodon: {
|
|
||||||
publish: Joi.boolean(),
|
|
||||||
url: Joi.string().uri().allow(null, ""),
|
|
||||||
token: Joi.string().allow(null, ""),
|
|
||||||
message: Joi.string().allow(null, ""),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const value = await schema.validateAsync(body);
|
const value = await schema.validateAsync(body);
|
||||||
const user = await UsersModel.findById(_id);
|
const update = await UsersModel.findByIdAndUpdate(
|
||||||
|
user._id,
|
||||||
if (value.oldPassword) {
|
{ $set: value },
|
||||||
if (!user.validPassword(value.oldPassword)) {
|
{ new: true }
|
||||||
throw new Error("Votre ancien mot de passe n'est pas valide");
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.mastodon !== undefined) {
|
|
||||||
user.mastodon = value.mastodon;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.password) {
|
|
||||||
user.salt = value.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.pagination) {
|
|
||||||
user.pagination = value.pagination;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.isPublicCollection !== undefined) {
|
|
||||||
user.isPublicCollection = value.isPublicCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.save();
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
this.req.login(user, (err) => {
|
this.req.login(update, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +38,7 @@ class Me extends Pages {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return update;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,20 +52,21 @@ class Pages {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
this.pageContent.session = this.req.session;
|
this.pageContent.session = this.req.session;
|
||||||
this.pageContent.flash = {
|
this.pageContent.flashInfo = this.req.flash("info");
|
||||||
info: this.req.flash("info"),
|
this.pageContent.error = this.req.flash("error") || null;
|
||||||
error: [
|
|
||||||
...this.req.flash("error"),
|
|
||||||
...(this.req.session?.flash?.error || []),
|
|
||||||
],
|
|
||||||
success: this.req.flash("success"),
|
|
||||||
};
|
|
||||||
this.pageContent.query = this.req.query;
|
this.pageContent.query = this.req.query;
|
||||||
this.pageContent.params = this.req.params;
|
this.pageContent.params = this.req.params;
|
||||||
this.pageContent.user = this.user;
|
this.pageContent.user = this.user;
|
||||||
this.pageContent.config = config;
|
this.pageContent.config = config;
|
||||||
this.pageContent.getBaseUrl = getBaseUrl(this.req);
|
this.pageContent.getBaseUrl = getBaseUrl(this.req);
|
||||||
|
|
||||||
|
if (this.req.session.flash && this.req.session.flash.error) {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
this.pageContent.page.failureFlash =
|
||||||
|
this.req.session.flash.error[0];
|
||||||
|
this.req.session.flash = null;
|
||||||
|
}
|
||||||
|
|
||||||
return this.pageContent;
|
return this.pageContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ const AlbumSchema = new mongoose.Schema(
|
||||||
extraartists: Array,
|
extraartists: Array,
|
||||||
images: Array,
|
images: Array,
|
||||||
thumb: String,
|
thumb: String,
|
||||||
thumbType: String,
|
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import mongoose from "mongoose";
|
|
||||||
|
|
||||||
const { Schema } = mongoose;
|
|
||||||
|
|
||||||
const JobSchema = new mongoose.Schema(
|
|
||||||
{
|
|
||||||
model: String,
|
|
||||||
id: Schema.Types.ObjectId,
|
|
||||||
state: {
|
|
||||||
type: String,
|
|
||||||
enum: ["NEW", "IN-PROGRESS", "ERROR", "SUCCESS"],
|
|
||||||
default: "NEW",
|
|
||||||
},
|
|
||||||
lastTry: Date,
|
|
||||||
lastErrorMessage: String,
|
|
||||||
tries: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ timestamps: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
export default mongoose.model("Jobs", JobSchema);
|
|
|
@ -25,20 +25,10 @@ const UserSchema = new mongoose.Schema(
|
||||||
},
|
},
|
||||||
hash: String,
|
hash: String,
|
||||||
salt: String,
|
salt: String,
|
||||||
pagination: {
|
|
||||||
type: Number,
|
|
||||||
default: 16,
|
|
||||||
},
|
|
||||||
isPublicCollection: {
|
isPublicCollection: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
mastodon: {
|
|
||||||
publish: Boolean,
|
|
||||||
token: String,
|
|
||||||
url: String,
|
|
||||||
message: String,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true,
|
timestamps: true,
|
||||||
|
|
|
@ -47,16 +47,6 @@ router
|
||||||
|
|
||||||
router
|
router
|
||||||
.route("/:itemId")
|
.route("/:itemId")
|
||||||
.patch(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const albums = new Albums(req);
|
|
||||||
const data = await albums.patchOne();
|
|
||||||
|
|
||||||
sendResponse(req, res, data);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.delete(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
.delete(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const albums = new Albums(req);
|
const albums = new Albums(req);
|
||||||
|
@ -68,17 +58,4 @@ router
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router
|
|
||||||
.route("/:itemId/share")
|
|
||||||
.post(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const albums = new Albums(req);
|
|
||||||
const data = await albums.shareOne();
|
|
||||||
|
|
||||||
sendResponse(req, res, data);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import nodemailer from "nodemailer";
|
|
||||||
import svgCaptcha from "svg-captcha";
|
|
||||||
|
|
||||||
import { sendResponse } from "../../../libs/format";
|
|
||||||
|
|
||||||
import { mailMethod, smtpConfig, mailTo, siteName } from "../../../config";
|
|
||||||
import ErrorEvent from "../../../libs/error";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router
|
|
||||||
.route("/")
|
|
||||||
.get(async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const captcha = svgCaptcha.create({
|
|
||||||
size: 4,
|
|
||||||
noise: 2,
|
|
||||||
color: true,
|
|
||||||
});
|
|
||||||
req.session.captcha = captcha.text;
|
|
||||||
|
|
||||||
res.type("svg");
|
|
||||||
return res.status(200).send(captcha.data);
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.post(async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
if (mailMethod === "smtp") {
|
|
||||||
const { email, name, message, captcha } = req.body;
|
|
||||||
|
|
||||||
if (!captcha || captcha !== req.session.captcha) {
|
|
||||||
throw new ErrorEvent(
|
|
||||||
406,
|
|
||||||
"Captcha",
|
|
||||||
"Le captcha n'est pas valide"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!email || !message) {
|
|
||||||
throw new ErrorEvent(
|
|
||||||
406,
|
|
||||||
"Erreur de saisie",
|
|
||||||
"Le formulaire n'est pas correctement saisi"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport(smtpConfig);
|
|
||||||
|
|
||||||
const text = `Bonjour,
|
|
||||||
Vous venez de recevoir un nouveau message de ${name} (${email}) :
|
|
||||||
|
|
||||||
${message}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const data = await transporter.sendMail({
|
|
||||||
from: smtpConfig.auth.user,
|
|
||||||
to: mailTo,
|
|
||||||
subject: `${siteName} : Nouveau message`,
|
|
||||||
text,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { messageId, response } = data;
|
|
||||||
|
|
||||||
return sendResponse(req, res, { messageId, response });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ErrorEvent(500, "Routeur", "Méthode non configurée");
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -1,28 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import { ensureLoggedIn } from "connect-ensure-login";
|
|
||||||
|
|
||||||
import Mastodon from "mastodon";
|
|
||||||
import { sendResponse } from "../../../libs/format";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.route("/").post(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const { url, token } = req.body;
|
|
||||||
|
|
||||||
const M = new Mastodon({
|
|
||||||
access_token: token,
|
|
||||||
api_url: url,
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await M.post("statuses", {
|
|
||||||
status: "Test d'intégration de Mastodon sur mon compte #musictopus 👌",
|
|
||||||
});
|
|
||||||
return sendResponse(req, res, data);
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -3,43 +3,13 @@ import { ensureLoggedIn } from "connect-ensure-login";
|
||||||
|
|
||||||
import { sendResponse } from "../../../libs/format";
|
import { sendResponse } from "../../../libs/format";
|
||||||
import { searchSong, getAlbumDetails } from "../../../helpers";
|
import { searchSong, getAlbumDetails } from "../../../helpers";
|
||||||
import Albums from "../../../middleware/Albums";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
// eslint-disable-next-line new-cap
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const data = await searchSong(
|
const data = await searchSong(req.query.q);
|
||||||
req.query.q,
|
|
||||||
req.query.format || null,
|
|
||||||
req.query.year || null,
|
|
||||||
req.query.country || null
|
|
||||||
);
|
|
||||||
|
|
||||||
const discogsIds = [];
|
|
||||||
const foundIds = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < data.results.length; i += 1) {
|
|
||||||
discogsIds.push(data.results[i].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
req.query.discogsIds = discogsIds;
|
|
||||||
|
|
||||||
const albums = new Albums(req);
|
|
||||||
const myAlbums = await albums.getAll();
|
|
||||||
|
|
||||||
if (myAlbums.rows) {
|
|
||||||
for (let i = 0; i < myAlbums.rows.length; i += 1) {
|
|
||||||
foundIds.push(myAlbums.rows[i].discogsId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < data.results.length; i += 1) {
|
|
||||||
data.results[i].inCollection = foundIds.includes(
|
|
||||||
data.results[i].id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendResponse(req, res, data);
|
sendResponse(req, res, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -7,8 +7,6 @@ import Auth from "../middleware/Auth";
|
||||||
|
|
||||||
import render from "../libs/format";
|
import render from "../libs/format";
|
||||||
|
|
||||||
import { registrationOpen } from "../config";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
// eslint-disable-next-line new-cap
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -61,33 +59,11 @@ router
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (registrationOpen) {
|
router
|
||||||
router
|
.route("/inscription")
|
||||||
.route("/inscription")
|
.get((req, res, next) => {
|
||||||
.get((req, res, next) => {
|
|
||||||
try {
|
|
||||||
const page = new Pages(req, "inscription/index");
|
|
||||||
|
|
||||||
page.setPageTitle("Inscription");
|
|
||||||
|
|
||||||
render(res, page);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.post(async (req, res) => {
|
|
||||||
try {
|
|
||||||
await Auth.register(req);
|
|
||||||
|
|
||||||
res.redirect("/");
|
|
||||||
} catch (err) {
|
|
||||||
res.redirect("/inscription");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
router.route("/inscription").get((req, res, next) => {
|
|
||||||
try {
|
try {
|
||||||
const page = new Pages(req, "inscription/desactivee");
|
const page = new Pages(req, "inscription");
|
||||||
|
|
||||||
page.setPageTitle("Inscription");
|
page.setPageTitle("Inscription");
|
||||||
|
|
||||||
|
@ -95,8 +71,16 @@ if (registrationOpen) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.post(async (req, res) => {
|
||||||
|
try {
|
||||||
|
await Auth.register(req);
|
||||||
|
|
||||||
|
res.redirect("/");
|
||||||
|
} catch (err) {
|
||||||
|
res.redirect("/inscription");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
router
|
router
|
||||||
.route("/ajouter-un-album")
|
.route("/ajouter-un-album")
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import passport from "passport";
|
|
||||||
|
|
||||||
import Jobs from "../middleware/Jobs";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.route("/").get(
|
|
||||||
passport.authenticate(["jobs"], {
|
|
||||||
session: false,
|
|
||||||
}),
|
|
||||||
async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const job = new Jobs();
|
|
||||||
const data = await job.run(req.query.state);
|
|
||||||
|
|
||||||
return res.status(200).json(data).end();
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.route("/populate").get(
|
|
||||||
passport.authenticate(["jobs"], {
|
|
||||||
session: false,
|
|
||||||
}),
|
|
||||||
async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const data = await Jobs.populate();
|
|
||||||
|
|
||||||
return res.status(200).json(data).end();
|
|
||||||
} catch (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -10,7 +10,7 @@ const router = express.Router();
|
||||||
|
|
||||||
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const page = new Albums(req, "collection");
|
const page = new Albums(req, "mon-compte/ma-collection/index");
|
||||||
|
|
||||||
await page.loadMyCollection();
|
await page.loadMyCollection();
|
||||||
|
|
||||||
|
@ -24,37 +24,6 @@ router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router
|
|
||||||
.route("/on-air")
|
|
||||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const page = new Albums(req, "mon-compte/ma-collection/details");
|
|
||||||
|
|
||||||
await page.onAir();
|
|
||||||
|
|
||||||
render(res, page);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
router
|
||||||
.route("/exporter")
|
.route("/exporter")
|
||||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
|
@ -63,19 +32,6 @@ router
|
||||||
|
|
||||||
page.setPageTitle("Exporter ma collection");
|
page.setPageTitle("Exporter ma collection");
|
||||||
|
|
||||||
render(res, page);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router
|
|
||||||
.route("/importer")
|
|
||||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const page = new Albums(req, "mon-compte/ma-collection/importer");
|
|
||||||
|
|
||||||
page.setPageTitle("Importer une collection");
|
|
||||||
|
|
||||||
render(res, page);
|
render(res, page);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import { ensureLoggedIn } from "connect-ensure-login";
|
|
||||||
|
|
||||||
import Me from "../middleware/Me";
|
|
||||||
|
|
||||||
import render from "../libs/format";
|
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const page = new Me(req, "mon-compte/index");
|
|
||||||
|
|
||||||
page.setPageTitle("Mon compte");
|
|
||||||
|
|
||||||
render(res, page);
|
|
||||||
} catch (err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -1,133 +0,0 @@
|
||||||
<div class="grid md:grid-cols-3 gap-16">
|
|
||||||
<div>
|
|
||||||
<template v-for="album in tracklist">
|
|
||||||
<strong v-if="album.title">{{album.title}}</strong>
|
|
||||||
<ul>
|
|
||||||
<li v-for="(track, index) in album.tracks" class="ml-4">
|
|
||||||
{{track.position || (index+1)}} - {{ track.title }} <template v-if="track.duration">({{track.duration}})</template>
|
|
||||||
<ul v-if="track.artists && track.artists.length > 0" class="sm-hidden">
|
|
||||||
<li v-for="extra in track.artists" class=" ml-4">
|
|
||||||
<small>{{extra.name}}</small>
|
|
||||||
</li>
|
|
||||||
</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}} : <a :href="`/ma-collection?page=1&limit=16&sort=year&order=asc&artist=${extra.name}`">{{extra.name}}</a></small>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class="md:col-span-2">
|
|
||||||
<div class="grid grid-cols-2 gap-10">
|
|
||||||
<div>
|
|
||||||
<strong>Genres</strong>
|
|
||||||
<br />
|
|
||||||
<template v-for="(genre, index) in item.genres">
|
|
||||||
{{genre}}<template v-if="index < item.genres.length - 1">, </template>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>Styles</strong>
|
|
||||||
<br />
|
|
||||||
<span v-for="(style, index) in item.styles">
|
|
||||||
{{style}}<template v-if="index < item.styles.length - 1">, </template>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="grid grid-cols-3 gap-10">
|
|
||||||
<div>
|
|
||||||
<strong>Pays</strong>
|
|
||||||
<br />
|
|
||||||
<span>{{item.country}}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>Année</strong>
|
|
||||||
<br />
|
|
||||||
<span>{{item.year}}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>Date de sortie</strong>
|
|
||||||
<br />
|
|
||||||
<span>{{item.released}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="grid gap-10">
|
|
||||||
<div>
|
|
||||||
<strong>Format<template v-if="item.formats.length > 1">s</template></strong>
|
|
||||||
<ul class="ml-4">
|
|
||||||
<li v-for="(format) in item.formats">
|
|
||||||
{{format.name}}
|
|
||||||
<template v-if="format.text">
|
|
||||||
- <i>{{format.text}}</i>
|
|
||||||
</template>
|
|
||||||
<template v-if="format.descriptions && format.descriptions.length > 0">
|
|
||||||
(<span v-for="(description, index) in format.descriptions">
|
|
||||||
{{description}}<template v-if="index < format.descriptions.length - 1">, </template>
|
|
||||||
</span>)
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
|
||||||
<div>
|
|
||||||
<strong id="identifiers">Code<template v-if="item.identifiers.length > 1">s</template> barre<template v-if="item.identifiers.length > 1">s</template></strong>
|
|
||||||
<ol class="ml-4">
|
|
||||||
<li v-for="identifier in identifiers">
|
|
||||||
{{identifier.value}} ({{identifier.type}})
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<template v-if="item.identifiers.length > identifiersPreviewLength">
|
|
||||||
<button type="button" class="button is-link" v-if="identifiersMode === 'preview'" @click="showAllIdentifiers">
|
|
||||||
Voir la suite
|
|
||||||
</button>
|
|
||||||
<button type="button" class="button is-link" v-if="identifiersMode === 'all'" @click="showLessIdentifiers">
|
|
||||||
Voir moins
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>Label<template v-if="item.labels.length > 1">s</template></strong>
|
|
||||||
<ol class="ml-4">
|
|
||||||
<li v-for="label in item.labels">
|
|
||||||
{{label.name}} {{label.catno}}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<strong>Société<template v-if="item.companies.length > 1">s</template></strong>
|
|
||||||
<ol class="ml-4">
|
|
||||||
<li v-for="company in item.companies">
|
|
||||||
<strong>{{company.entity_type_name}}</strong> {{company.name}}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="grid gap-10">
|
|
||||||
<div>
|
|
||||||
<strong>Note</strong>
|
|
||||||
<div v-html="(item.notes || '').replaceAll('\n', '<br />')"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="grid gap-10">
|
|
||||||
<div>
|
|
||||||
<strong>Vidéos</strong>
|
|
||||||
<dl>
|
|
||||||
<template v-for="video in item.videos">
|
|
||||||
<dt>
|
|
||||||
<a :href="video.uri" target="_blank" rel="noopener noreferrer">{{video.title}}</a>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
{{video.description}}
|
|
||||||
</dd>
|
|
||||||
</template>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label for="genre">Genre</label>
|
|
||||||
<select id="genre" v-model="genre" @change="changeFilter">
|
|
||||||
<option value="">Tous</option>
|
|
||||||
<%
|
|
||||||
for (let i = 0; i < page.genres.length; i += 1 ) {
|
|
||||||
__append(`<option value="${page.genres[i]}">${page.genres[i]}</option>`);
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<div class="filters">
|
|
||||||
<%- include('./artist') %>
|
|
||||||
<%- include('./format') %>
|
|
||||||
<%- include('./sort') %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filters" v-if="moreFilters">
|
|
||||||
<%- include('./year') %>
|
|
||||||
<%- include('./genre') %>
|
|
||||||
<%- include('./style') %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span @click="showMoreFilters" class="showMoreFilters">
|
|
||||||
<template v-if="!moreFilters">Voir plus de filtres</template>
|
|
||||||
<template v-if="moreFilters">Voir moins de filtres</template>
|
|
||||||
<i class="icon-left-open down" v-if="!moreFilters"></i>
|
|
||||||
<i class="icon-left-open up" v-if="moreFilters"></i>
|
|
||||||
</span>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<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 (1-9)</option>
|
|
||||||
<option value="year-desc">Année (9-1)</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>
|
|
||||||
<option value="createdAt-asc">Date d'ajout (1-9)</option>
|
|
||||||
<option value="createdAt-desc">Date d'ajout (9-1)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<div class="field">
|
|
||||||
<label for="style">Style</label>
|
|
||||||
<select id="style" v-model="style" @change="changeFilter">
|
|
||||||
<option value="">Tous</option>
|
|
||||||
<%
|
|
||||||
for (let i = 0; i < page.styles.length; i += 1 ) {
|
|
||||||
__append(`<option value="${page.styles[i]}">${page.styles[i]}</option>`);
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<div class="field">
|
|
||||||
<label for="format">Année</label>
|
|
||||||
<select id="format" v-model="year" @change="changeFilter">
|
|
||||||
<option value="">Toutes</option>
|
|
||||||
<%
|
|
||||||
for (let i = 0; i < page.years.length; i += 1 ) {
|
|
||||||
__append(`<option value="${page.years[i]}">${page.years[i]}</option>`);
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
|
@ -1,8 +1,10 @@
|
||||||
<main class="layout-maxed error">
|
<main class="layout-maxed error">
|
||||||
<h1><%= page.title %></h1>
|
<h1><%= page.title %></h1>
|
||||||
|
<% if ( errorCode && errorCode === 404 ) { %>
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
|
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
|
||||||
</p>
|
</p>
|
||||||
|
<% } %>
|
||||||
<% if ( process.env.NODE_ENV !== 'production' ) { %>
|
<% if ( process.env.NODE_ENV !== 'production' ) { %>
|
||||||
<div>
|
<div>
|
||||||
<pre><%= page.error %></pre>
|
<pre><%= page.error %></pre>
|
||||||
|
|
247
views/index.ejs
247
views/index.ejs
|
@ -16,8 +16,9 @@
|
||||||
|
|
||||||
<link href="/css/main.css" rel="stylesheet" />
|
<link href="/css/main.css" rel="stylesheet" />
|
||||||
|
|
||||||
<script src="/js/libs.js"></script>
|
<script src="/libs/axios/axios.min.js"></script>
|
||||||
<script defer src="/js/main.js"></script>
|
<script src="/libs/vue/vue.global.prod.js"></script>
|
||||||
|
<script src="/js/main.js"></script>
|
||||||
|
|
||||||
<% if ( config.matomoUrl ) { %>
|
<% if ( config.matomoUrl ) { %>
|
||||||
<!-- Matomo -->
|
<!-- Matomo -->
|
||||||
|
@ -38,120 +39,85 @@
|
||||||
<% } %>
|
<% } %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
<nav class="navbar" aria-label="Navigation principale">
|
||||||
<nav class="navbar container" aria-label="Navigation principale">
|
<div class="navbar-brand">
|
||||||
<div class="navbar-brand">
|
<a class="navbar-item" href="/">
|
||||||
<a class="navbar-item" href="/">
|
<img src="/img/logo.png" alt="Logo MusicTopus">
|
||||||
<img src="/img/logo.png" alt="Logo MusicTopus">
|
<span>MusicTopus</span>
|
||||||
<span>MusicTopus</span>
|
</a>
|
||||||
</a>
|
|
||||||
|
|
||||||
<a role="button" class="navbar-burger" aria-label="Afficher le menu" aria-expanded="false" data-target="navbar">
|
<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>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="navbar" class="navbar-menu">
|
||||||
|
<% if ( user ) { %>
|
||||||
|
<div class="navbar-start">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="buttons">
|
||||||
|
<a class="button is-primary" href="/ajouter-un-album">
|
||||||
|
<i class="icon-plus"></i>
|
||||||
|
<span>
|
||||||
|
Ajouter un album
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<div id="navbar" class="navbar-menu">
|
<div class="navbar-end">
|
||||||
|
<a class="navbar-item" href="/nous-contacter">
|
||||||
|
Nous contacter
|
||||||
|
</a>
|
||||||
<% if ( user ) { %>
|
<% if ( user ) { %>
|
||||||
<div class="navbar-start">
|
<div class="navbar-item has-dropdown">
|
||||||
<div class="navbar-item">
|
<a class="navbar-link">
|
||||||
<div class="buttons">
|
<i class="icon-user"></i>
|
||||||
<a class="button is-primary" href="/ajouter-un-album">
|
<span>
|
||||||
<i class="icon-plus"></i>
|
<%= user.username %>
|
||||||
<span>
|
</span>
|
||||||
Ajouter un album
|
</a>
|
||||||
</span>
|
|
||||||
</a>
|
<div class="navbar-dropdown">
|
||||||
</div>
|
<a class="navbar-item" href="/ma-collection">
|
||||||
|
Ma collection
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="/ma-collection/exporter">
|
||||||
|
Exporter ma collection
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<div class="navbar-item apparence">
|
||||||
<div class="navbar-end">
|
<div class="theme-switch-wrapper">
|
||||||
<a class="navbar-item" href="/nous-contacter">
|
<label class="theme-switch" for="checkbox" aria-label="Passer du thème clair au thème sombre et inversement">
|
||||||
Nous contacter
|
<input type="checkbox" id="checkbox" />
|
||||||
</a>
|
<div class="slider round"></div>
|
||||||
<div class="navbar-item has-dropdown">
|
</label>
|
||||||
<a class="navbar-link">
|
</div>
|
||||||
<i class="icon-adjust theme-system icon-theme hidden"></i>
|
</div>
|
||||||
<i class="icon-sun theme-light icon-theme hidden"></i>
|
<div class="navbar-item">
|
||||||
<i class="icon-moon theme-dark icon-theme hidden"></i>
|
<div class="buttons">
|
||||||
<span>
|
<button type="button" class="button is-primary" id="switchAriaTheme" aria-label="Renforcer la visibilité de ce site" title="Renforcer la visibilité de ce site">
|
||||||
Thème
|
<i class="icon-eye"></i>
|
||||||
</span>
|
</button>
|
||||||
|
<% if ( !user ) { %>
|
||||||
|
<a class="button is-primary" href="/connexion">
|
||||||
|
<strong>Connexion</strong>
|
||||||
</a>
|
</a>
|
||||||
|
<% } else { %>
|
||||||
<div class="navbar-dropdown">
|
<a class="button is-danger" href="/se-deconnecter">
|
||||||
<button class="navbar-item theme" data-value="system">
|
Déconnexion
|
||||||
<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">
|
|
||||||
<i class="icon-user"></i>
|
|
||||||
<span>
|
|
||||||
<%= user.username %>
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
|
<% } %>
|
||||||
<div class="navbar-dropdown">
|
|
||||||
<a class="navbar-item" href="/mon-compte">
|
|
||||||
Mon compte
|
|
||||||
</a>
|
|
||||||
<hr />
|
|
||||||
<a class="navbar-item" href="/ma-collection">
|
|
||||||
Ma collection
|
|
||||||
</a>
|
|
||||||
<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>
|
|
||||||
<a class="navbar-item" href="/ma-collection/importer">
|
|
||||||
Importer une collection
|
|
||||||
</a>
|
|
||||||
<hr />
|
|
||||||
<a class="navbar-item is-danger" href="/se-deconnecter">
|
|
||||||
Déconnexion
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
|
||||||
<% if ( !user ) { %>
|
|
||||||
<div class="navbar-item">
|
|
||||||
<div class="buttons">
|
|
||||||
<a class="button is-primary" href="/connexion">
|
|
||||||
<strong>Connexion</strong>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="toastr">
|
<div id="toastr">
|
||||||
|
@ -159,61 +125,40 @@
|
||||||
<span></span>
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%
|
<% if ( page.failureFlash || (error && error.length > 0 ) ) {%>
|
||||||
if ( flash.error.length > 0 ) {
|
<div class="flash">
|
||||||
for ( let i = 0 ; i < flash.error.length ; i += 1 ) {
|
<% if ( page.failureFlash ) {%>
|
||||||
%>
|
<div class="header">
|
||||||
<div class="flash">
|
Erreur
|
||||||
<div class="header">
|
|
||||||
Erreur
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
<%= flash.error[i].replace('Error: ', '') %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<%
|
<div class="body">
|
||||||
}
|
<%= page.failureFlash %>
|
||||||
}
|
|
||||||
if ( flash.info.length > 0 ) {
|
|
||||||
for ( let i = 0 ; i < flash.info.length ; i += 1 ) {
|
|
||||||
%>
|
|
||||||
<div class="flash info">
|
|
||||||
<div class="header">
|
|
||||||
Information
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
<%= flash.info[i] %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<%
|
<% } %>
|
||||||
}
|
<%
|
||||||
}
|
if (error && error.length > 0) {
|
||||||
if ( flash.success.length > 0 ) {
|
for( let i = 0 ; i < error.length ; i += 1 ) {
|
||||||
for ( let i = 0 ; i < flash.success.length ; i += 1 ) {
|
%>
|
||||||
%>
|
<div class="header">
|
||||||
<div class="flash success">
|
Erreur
|
||||||
<div class="header">
|
</div>
|
||||||
Succès
|
<div class="body">
|
||||||
</div>
|
<%= error %>
|
||||||
<div class="body">
|
</div>
|
||||||
<%= flash.success[i] %>
|
<%
|
||||||
</div>
|
}
|
||||||
</div>
|
}
|
||||||
<%
|
%>
|
||||||
}
|
</div>
|
||||||
}
|
<% } %>
|
||||||
%>
|
|
||||||
|
|
||||||
<%- include(viewname) %>
|
<%- include(viewname) %>
|
||||||
|
|
||||||
<footer class="footer layout-hero">
|
<footer class="footer layout-hero">
|
||||||
<p>
|
<p>
|
||||||
<strong title="Merci Brunus ! 😜">MusicTopus</strong> par <a href="https://www.darkou.link" target="_blank" rel="noopener noreferrer">Damien Broqua <i class="icon-link"></i></a>.
|
<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>.
|
||||||
Logo réalisé par Brunus avec <a href="https://inkscape.org/fr/" target="_blank" rel="noopener noreferrer">Inkscape <i class="icon-link"></i></a>.
|
|
||||||
<br />
|
|
||||||
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> et disponible sur <a href="https://git.darkou.fr/dbroqua/MusicTopus" target="_blank">git.darkou.fr <i class="icon-link"></i></a>.
|
|
||||||
<br />
|
|
||||||
Fait avec ❤️ à Bordeaux.
|
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>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,42 +1,23 @@
|
||||||
<main class="layout-maxed ajouter-un-album" id="ajouter-album">
|
<main class="layout-maxed ajouter-un-album" id="app">
|
||||||
<h1>Ajouter un album</h1>
|
<h1>Ajouter un album</h1>
|
||||||
<form @submit="search">
|
<div class="grid sm:grid-cols-2">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2">
|
<div>
|
||||||
<div>
|
<form @submit="search">
|
||||||
<label for="q">Nom de l'album ou code barre</label>
|
<label for="q">Nom de l'album ou code barre</label>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<input type="text" name="q" id="q" v-model="q" placeholder="ex : Iron Maiden - Powerslave" autofocus>
|
<input type="text" name="q" id="q" v-model="q" placeholder="ex : Hybrid Theory" autofocus>
|
||||||
<button class="button is-primary" :disabled="loading" aria-label="Chercher">
|
<button class="button is-primary" :disabled="loading" aria-label="Chercher">
|
||||||
<i class="icon-search" v-if="!loading"></i>
|
<i class="icon-search" v-if="!loading"></i>
|
||||||
<i class="icon-spin animate-spin" v-if="loading"></i>
|
<i class="icon-spin animate-spin" v-if="loading"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid sm:grid-cols-3 gap-5">
|
</form>
|
||||||
<div class="field">
|
|
||||||
<label for="format">Format</label>
|
|
||||||
<select id="format" v-model="format">
|
|
||||||
<option value="">Tous</option>
|
|
||||||
<option v-for="format in orderedItems(formats)" :value="format">{{format}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="year">Année</label>
|
|
||||||
<input type="number" name="year" v-model="year" id="year" placeholder="1984">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="country">Pays</label>
|
|
||||||
<input type="string" name="country" v-model="country" id="country" placeholder="France">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
|
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
|
||||||
<div v-if="!loading" v-for="item in items" class="item" :class="{'in-collection': item.inCollection}">
|
<div class="item" v-if="!loading" v-for="item in items">
|
||||||
<a @click="loadDetails(item.id)" class="title">{{ item.artists_sort }} {{ item.title }}</a>
|
<a @click="loadDetails(item.id)" class="title">{{ item.artists_sort }} {{ item.title }}</a>
|
||||||
<small v-if="item.inCollection"> (Dans ma collection)</small>
|
|
||||||
<div class="grid grid-cols-2 md:grid-cols-4">
|
<div class="grid grid-cols-2 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
<img :src="item.thumb" :alt="item.title" @click="loadDetails(item.id)"/>
|
<img :src="item.thumb" :alt="item.title" @click="loadDetails(item.id)"/>
|
||||||
|
@ -79,7 +60,7 @@
|
||||||
<button aria-label="Fermer" class="close" @click="toggleModal"></button>
|
<button aria-label="Fermer" class="close" @click="toggleModal"></button>
|
||||||
</header>
|
</header>
|
||||||
<section>
|
<section>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-16">
|
<div class="grid grid-cols-2 gap-16">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img :src="details.thumb %>" :alt="`Miniature pour l'album ${details.title}`" />
|
<img :src="details.thumb %>" :alt="`Miniature pour l'album ${details.title}`" />
|
||||||
|
@ -87,93 +68,75 @@
|
||||||
<img v-for="image in details.images" :src="image.uri150" :alt="`Miniature de type ${image.type}`" style="max-width: 60px;" />
|
<img v-for="image in details.images" :src="image.uri150" :alt="`Miniature de type ${image.type}`" style="max-width: 60px;" />
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
<ul class="is-unstyled">
|
<ol class="ml-4">
|
||||||
<li v-for="track in details.tracklist" :class="{'ml-4': track.type_ === 'track'}">
|
<li v-for="track in details.tracklist">{{ track.title }} ({{track.duration}})</li>
|
||||||
<strong v-if="track.type_ === 'heading'">
|
</ol>
|
||||||
{{track.title}}
|
|
||||||
</strong>
|
|
||||||
<template v-else>
|
|
||||||
{{ track.position }}
|
|
||||||
{{ track.title }} <span v-if="track.duration">({{track.duration}})</span>
|
|
||||||
<ul v-if="track.artists && track.artists.length > 0" class="sm-hidden">
|
|
||||||
<li v-for="extra in track.artists" class=" ml-4">
|
|
||||||
<small>{{extra.role}} : {{extra.name}}</small>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2">
|
<div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
<div class="grid grid-cols-2 gap-10">
|
||||||
<div class="grid grid-cols-1">
|
<div>
|
||||||
<strong>Genres</strong>
|
<strong>Genres</strong>
|
||||||
|
<br />
|
||||||
<template v-for="(genre, index) in details.genres">
|
<template v-for="(genre, index) in details.genres">
|
||||||
{{genre}}<template v-if="index < details.genres.length - 1">, </template>
|
{{genre}}<template v-if="index < details.genres.length - 1">, </template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1">
|
<div>
|
||||||
<strong>Styles</strong>
|
<strong>Styles</strong>
|
||||||
|
<br />
|
||||||
<span v-for="(style, index) in details.styles">
|
<span v-for="(style, index) in details.styles">
|
||||||
{{style}}<template v-if="index < details.styles.length - 1">, </template>
|
{{style}}<template v-if="index < details.styles.length - 1">, </template>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-10">
|
<div class="grid grid-cols-3 gap-10">
|
||||||
<div class="grid grid-cols-2 md:grid-cols-1">
|
<div>
|
||||||
<strong>Pays</strong>
|
<strong>Pays</strong>
|
||||||
|
<br />
|
||||||
<span>{{details.country}}</span>
|
<span>{{details.country}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-1">
|
<div>
|
||||||
<strong>Année</strong>
|
<strong>Année</strong>
|
||||||
|
<br />
|
||||||
<span>{{details.year}}</span>
|
<span>{{details.year}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-1">
|
<div>
|
||||||
<strong>Date de sortie</strong>
|
<strong>Date de sortie</strong>
|
||||||
|
<br />
|
||||||
<span>{{details.released}}</span>
|
<span>{{details.released}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="grid grid-cols-1 gap-10">
|
<div class="grid grid-cols-2 gap-10">
|
||||||
<div>
|
<div>
|
||||||
<strong>Format<template v-if="details?.formats?.length > 1">s</template></strong>
|
<strong>Format</strong>
|
||||||
<ul class="ml-4">
|
<br />
|
||||||
<li v-for="(format) in details.formats">
|
<span v-for="(format, index) in details.formats">
|
||||||
{{format.name}}
|
{{format.name}}<template v-if="index < details.formats.length - 1">, </template>
|
||||||
<template v-if="format.text">
|
</span>
|
||||||
- <i>{{format.text}}</i>
|
|
||||||
</template>
|
|
||||||
<template v-if="format.descriptions && format.descriptions.length > 0">
|
|
||||||
(<span v-for="(description, index) in format.descriptions">
|
|
||||||
{{description}}<template v-if="index < format.descriptions.length - 1">, </template>
|
|
||||||
</span>)
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
<div class="grid grid-cols-2 gap-10">
|
||||||
<div>
|
<div>
|
||||||
<strong>Code<template v-if="details?.identifiers?.length > 1">s</template> barre<template v-if="details?.identifiers?.length > 1">s</template></strong>
|
<strong>Codes barres</strong>
|
||||||
<ol class="ml-4">
|
<ol>
|
||||||
<li v-for="identifier in details.identifiers">
|
<li v-for="identifier in details.identifiers">
|
||||||
{{identifier.value}} ({{identifier.type}})
|
{{identifier.value}} ({{identifier.type}})
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Label<template v-if="details?.labels?.length > 1">s</template></strong>
|
<strong>Label</strong>
|
||||||
<ol class="ml-4">
|
<ol>
|
||||||
<li v-for="label in details.labels">
|
<li v-for="label in details.labels">
|
||||||
{{label.name}}
|
{{label.name}}
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<strong>Société<template v-if="details?.companies?.length > 1">s</template></strong>
|
<strong>Société</strong>
|
||||||
<ol class="ml-4">
|
<ol>
|
||||||
<li v-for="company in details.companies">
|
<li v-for="company in details.companie">
|
||||||
<strong>{{company.entity_type_name}}</strong>
|
|
||||||
{{company.name}}
|
{{company.name}}
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -183,15 +146,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer>
|
<footer>
|
||||||
<% if ( user.mastodon && user.mastodon.publish ) { %>
|
<button class="button is-primary" @click="add">Ajouter</button>
|
||||||
<div class="field">
|
|
||||||
<label for="share">Partager sur le fédiverse</label>
|
|
||||||
<span>
|
|
||||||
<input type="checkbox" id="share" name="share" v-model="share">
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
<button :class="['button is-primary', submitting ? 'is-disabled' : '']" @click="add">Ajouter</button>
|
|
||||||
<button class="button" @click="toggleModal">Annuler</button>
|
<button class="button" @click="toggleModal">Annuler</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
@ -199,5 +154,94 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const canPublish = <%- (user.mastodon && user.mastodon.publish) || false %>;
|
Vue.createApp({
|
||||||
</script>
|
data() {
|
||||||
|
return {
|
||||||
|
q: '',
|
||||||
|
loading: false,
|
||||||
|
items: [],
|
||||||
|
details: {},
|
||||||
|
modalIsVisible: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if ( this.loading ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
axios.get(`/api/v1/search?q=${this.q}`)
|
||||||
|
.then( response => {
|
||||||
|
const {
|
||||||
|
results,
|
||||||
|
} = response.data;
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
for (let i = 0 ; i < results.length ; i += 1 ) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
thumb,
|
||||||
|
year,
|
||||||
|
country,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
style,
|
||||||
|
} = results[i];
|
||||||
|
items.push({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
thumb,
|
||||||
|
year,
|
||||||
|
country,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
style,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items = items;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Aucun résultat trouvé :/");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleModal() {
|
||||||
|
this.modalIsVisible = !this.modalIsVisible;
|
||||||
|
},
|
||||||
|
loadDetails(discogsId) {
|
||||||
|
axios.get(`/api/v1/search/${discogsId}`)
|
||||||
|
.then( response => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
} = response;
|
||||||
|
|
||||||
|
this.details = data;
|
||||||
|
this.toggleModal();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Impossible de charger les détails de cet album");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
add() {
|
||||||
|
axios.post('/api/v1/albums', this.details)
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = '/ma-collection';
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Impossible d'ajouter ce album pour le moment…");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
<%
|
<main class="layout-maxed collection" id="app">
|
||||||
const pageType = page.username ? 'public' : 'private';
|
|
||||||
%>
|
|
||||||
|
|
||||||
<main class="layout-maxed collection" id="collection">
|
|
||||||
<h1>
|
<h1>
|
||||||
<% if ( pageType === 'private' ) {
|
Collection de <%= page.username %>
|
||||||
__append('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>');
|
|
||||||
} else {
|
|
||||||
__append(`Collection de ${page.username}`);
|
|
||||||
} %>
|
|
||||||
</h1>
|
</h1>
|
||||||
<% if ( pageType === 'private' ) { %>
|
|
||||||
<div>
|
|
||||||
<a :href="shareLink" v-if="isPublicCollection" target="_blank">
|
|
||||||
<i class="icon-share"></i> Voir ma collection partagée
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<%- include('../components/filters/index') %>
|
<div class="filters">
|
||||||
|
<div class="field">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 list hover">
|
<label for="artist">Artiste</label>
|
||||||
<div class="loader" v-if="loading">
|
<select id="artist" v-model="artist" @change="changeFilter">
|
||||||
<div class="animation"></div>
|
<option value="">Tous</option>
|
||||||
<div>
|
<%
|
||||||
Chargement des données en cours…
|
for (let i = 0; i < page.artists.length; i += 1 ) {
|
||||||
</div>
|
__append(`<option value="${page.artists[i]}">${page.artists[i]}</option>`);
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="item" v-if="!loading" v-for="item in items">
|
<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">
|
||||||
|
<div class="item" v-if="!loading" v-for="item in items">
|
||||||
<span class="title">
|
<span class="title">
|
||||||
<% if ( pageType === 'private' ) { %>
|
{{ item.artists_sort}} - {{ item.title }}
|
||||||
<a :href="'/ma-collection/' + item._id">{{ renderAlbumTitle(item) }}</a>
|
|
||||||
<i class="icon-trash" @click="showConfirmDelete(item._id)"></i>
|
|
||||||
<% } else { %>
|
|
||||||
{{ item.artists_sort}} - {{ item.title }}
|
|
||||||
<% } %>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-4">
|
<div class="grid grid-cols-2 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
<% if ( pageType === 'private' ) { %>
|
<img :src="item.thumb" :alt="item.title" />
|
||||||
<a :href="'/ma-collection/' + item._id"><img :src="item.thumb" :alt="item.title" /></a>
|
|
||||||
<% } else { %>
|
|
||||||
<img :src="item.thumb" :alt="item.title" />
|
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-3">
|
<div class="md:col-span-3">
|
||||||
<span><strong>Année :</strong> {{ item.year }}</span>
|
<span><strong>Année :</strong> {{ item.year }}</span>
|
||||||
|
@ -74,84 +79,106 @@
|
||||||
</div>
|
</div>
|
||||||
<nav class="pagination" role="navigation" aria-label="Pagination">
|
<nav class="pagination" role="navigation" aria-label="Pagination">
|
||||||
<ul class="pagination-list">
|
<ul class="pagination-list">
|
||||||
<li v-if="page > 1">
|
|
||||||
<a class="pagination-link" @click="goTo(1)" aria-label="Aller à la première page">«</a>
|
|
||||||
</li>
|
|
||||||
<template v-for="p in Array.from({length: totalPages}, (v, i) => (i+1))">
|
<template v-for="p in Array.from({length: totalPages}, (v, i) => (i+1))">
|
||||||
<template v-if="p < 2 || p > (totalPages - 1) || (Number(page) - 1) <= p && Number(page) + 1 >= p">
|
<template v-if="p < 2 || p > (totalPages - 1) || (page - 1) <= p && page + 1 >= p">
|
||||||
<li>
|
<li>
|
||||||
<a class="pagination-link" :class="{'is-current': p === Number(page)}" @click="goTo(p)" :aria-label="'Aller à la page '+p">{{ p }}</a>
|
<a class="pagination-link" :class="{'is-current': p === page}" @click="goTo(p)" aria-label="Aller à la page {{p}}">{{ p }}</a>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="(Number(page) - 3 === p && Number(page) - 2 > 1) || (Number(page) + 2 === p && Number(page) + 2 < totalPages - 1)">
|
<template v-if="(page - 3 === p && page - 2 > 1) || (page + 2 === p && page + 2 < totalPages - 1)">
|
||||||
<li>
|
<li>
|
||||||
<a class="pagination-link is-disabled">…</a>
|
<a class="pagination-link is-disabled">…</a>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<li v-if="page < totalPages">
|
|
||||||
<a class="pagination-link" @click="goTo(totalPages)" aria-label="Aller à la derière page">»</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<% if ( pageType === 'private' ) { %>
|
|
||||||
<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>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const vueType = "<%= pageType %>";
|
const {
|
||||||
const query = <%- JSON.stringify(query) %>;
|
protocol,
|
||||||
<% if ( pageType === 'private' ) { %>
|
host
|
||||||
const isPublicCollection = <%= user.isPublicCollection ? 'true' : 'false' %>;
|
} = window.location;
|
||||||
const userId = "<%= user._id %>";
|
|
||||||
<% } else { %>
|
Vue.createApp({
|
||||||
const userId = "<%= params.userId %>";
|
data() {
|
||||||
const isPublicCollection = false;
|
return {
|
||||||
<% } %>
|
loading: false,
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
limit: 16,
|
||||||
|
artist: '',
|
||||||
|
format: '',
|
||||||
|
sortOrder: 'artists_sort-asc',
|
||||||
|
sort: 'artists_sort',
|
||||||
|
order: 'asc',
|
||||||
|
userId: "<%= params.userId %>",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
let url = `/api/v1/albums?userId=${this.userId}&page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`;
|
||||||
|
if ( this.artist ) {
|
||||||
|
url += `&artists_sort=${this.artist}`;
|
||||||
|
}
|
||||||
|
if ( this.format ) {
|
||||||
|
url += `&format=${this.format}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.get(url)
|
||||||
|
.then( response => {
|
||||||
|
this.items = response.data.rows;
|
||||||
|
this.total = response.data.count;
|
||||||
|
this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Impossible de charger cette collection");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -238,7 +238,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 id="boites">Les boites</h2>
|
<h2 id="boites">Les boites</h2>
|
||||||
<div class="box mini">
|
<div class="box">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<h1>
|
<h1>
|
||||||
Connexion
|
Connexion
|
||||||
|
@ -274,22 +274,6 @@
|
||||||
Ceci est une erreur
|
Ceci est une erreur
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flash info">
|
|
||||||
<div class="header">
|
|
||||||
Information
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
Ceci est une information
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flash success">
|
|
||||||
<div class="header">
|
|
||||||
Succès
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
Ceci est un succès
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<pre>
|
<pre>
|
||||||
<div class="flash">
|
<div class="flash">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -355,7 +339,6 @@
|
||||||
<i class="icon-left-open">.icon-left-open</i>
|
<i class="icon-left-open">.icon-left-open</i>
|
||||||
<i class="icon-right-open">.icon-right-open</i>
|
<i class="icon-right-open">.icon-right-open</i>
|
||||||
<i class="icon-export">.icon-export</i>
|
<i class="icon-export">.icon-export</i>
|
||||||
<i class="icon-refresh">.icon-refresh</i>
|
|
||||||
<i class="icon-share">.icon-share</i>
|
<i class="icon-share">.icon-share</i>
|
||||||
<i class="icon-spin">.icon-spin</i>
|
<i class="icon-spin">.icon-spin</i>
|
||||||
<i class="icon-sun">.icon-sun</i>
|
<i class="icon-sun">.icon-sun</i>
|
||||||
|
@ -487,8 +470,19 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</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="navbar-item">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
<button type="button" class="button is-primary" id="switchAriaTheme" aria-label="Renforcer la visibilité de ce site" title="Renforcer la visibilité de ce site">
|
||||||
|
<i class="icon-eye"></i>
|
||||||
|
</button>
|
||||||
<a class="button is-danger" href="/se-deconnecter">
|
<a class="button is-danger" href="/se-deconnecter">
|
||||||
Déconnexion
|
Déconnexion
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="box mini">
|
<div class="box">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<h1>
|
<h1>
|
||||||
Connexion
|
Connexion
|
||||||
|
@ -12,11 +12,9 @@
|
||||||
<input type="password" name="password" id="password" placeholder="********">
|
<input type="password" name="password" id="password" placeholder="********">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if ( config.registrationOpen === true ) { %>
|
|
||||||
<div class="text-right mt-10">
|
<div class="text-right mt-10">
|
||||||
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
|
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<button type="submit" class="button is-primary">Connexion</button>
|
<button type="submit" class="button is-primary">Connexion</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
Pourquoi utiliser MusicTopus ?
|
Pourquoi utiliser MusicTopus ?
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-justify">
|
<p class="text-justify">
|
||||||
<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.
|
<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.
|
||||||
<br />
|
<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.
|
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>
|
</p>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="box mini">
|
<div class="box">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<h1>
|
<h1>
|
||||||
Inscription
|
Inscription
|
|
@ -1,19 +0,0 @@
|
||||||
<main class="layout-maxed">
|
|
||||||
<div class="header layout-hero"></div>
|
|
||||||
<h1>
|
|
||||||
Inscription
|
|
||||||
</h1>
|
|
||||||
<div class="container">
|
|
||||||
<div class="text">
|
|
||||||
<p class="text-justify">
|
|
||||||
Les inscriptions sur ce site sont fermées.
|
|
||||||
</p>
|
|
||||||
<p class="text-justify">
|
|
||||||
Vous avez cependant la possibilité d'héberger vous même une version de <a href="https://www.musictopus.fr" target="_blank">MusicTopus</a> en vous rendant directement sur le <a href="https://git.darkou.fr/dbroqua/MusicTopus" target="_blank">dépot du projet</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p class="text-center">
|
|
||||||
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
|
@ -1,162 +0,0 @@
|
||||||
<main class="layout-maxed collection" id="mon-compte">
|
|
||||||
<h1>
|
|
||||||
Mon compte
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<form method="POST" @submit.prevent="updateProfil">
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
|
||||||
<div class="box">
|
|
||||||
<h2>Mes données personnelles</h2>
|
|
||||||
<div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="email">Adresse e-mail</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
readonly
|
|
||||||
disabled
|
|
||||||
name="email"
|
|
||||||
id="email"
|
|
||||||
v-model="formData.email"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="username">Nom d'utilisateur</label>
|
|
||||||
<input
|
|
||||||
type="string"
|
|
||||||
readonly
|
|
||||||
disabled
|
|
||||||
name="username"
|
|
||||||
id="username"
|
|
||||||
v-model="formData.username"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="oldPassword">Mot de passe actuel</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="oldPassword"
|
|
||||||
id="oldPassword"
|
|
||||||
placeholder="Saisisssez votre mot de passe actuel"
|
|
||||||
v-model="formData.oldPassword"
|
|
||||||
/>
|
|
||||||
<div class="message error" v-if="errors.includes('emptyPassword')">
|
|
||||||
Pour changer votre mot de passe vous devez saisir votre mot de passe actuel
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="password">Nouveau mot de passe</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
id="password"
|
|
||||||
placeholder="Saisisssez votre nouveau mot de passe"
|
|
||||||
v-model="formData.password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="passwordConfirm">Nouveau mot de passe (confirmation)</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="passwordConfirm"
|
|
||||||
id="passwordConfirm"
|
|
||||||
placeholder="Confirmez votre nouveau mot de passe"
|
|
||||||
v-model="formData.passwordConfirm"
|
|
||||||
/>
|
|
||||||
<div class="message error" v-if="errors.includes('passwordsDiffer')">
|
|
||||||
La confirmation ne correspond pas avec votre nouveau mot de passe
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<h2>Mon activité</h2>
|
|
||||||
<div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="mastodon.publish">Publier sur le fédiverse lorsque j'ajoute un album</label>
|
|
||||||
<select id="format" v-model="formData.mastodon.publish">
|
|
||||||
<option value="true">Oui</option>
|
|
||||||
<option value="false">Non</option>
|
|
||||||
</select>
|
|
||||||
<!-- <input
|
|
||||||
type="checkbox"
|
|
||||||
name="mastodon.publish"
|
|
||||||
id="mastodon.publish"
|
|
||||||
v-model="mastodon.publish"
|
|
||||||
/> -->
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="mastodon.url">Url de l'API de votre instance</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="mastodon.url"
|
|
||||||
id="mastodon.url"
|
|
||||||
v-model="formData.mastodon.url"
|
|
||||||
placeholder="https://mastodon.social/api/v1/"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="mastodon.token">Jeton d'accès (droits nécessaires : write:media et write:statuses)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="mastodon.token"
|
|
||||||
id="mastodon.token"
|
|
||||||
v-model="formData.mastodon.token"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="mastodon.message">Message</label>
|
|
||||||
<textarea
|
|
||||||
name="mastodon.message"
|
|
||||||
id="mastodon.message"
|
|
||||||
v-model="formData.mastodon.message"
|
|
||||||
></textarea>
|
|
||||||
<small>
|
|
||||||
Variables possibles :
|
|
||||||
<ul>
|
|
||||||
<li>{artist}, exemple : Iron Maiden</li>
|
|
||||||
<li>{album}, exemple : Powerslave</li>
|
|
||||||
<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>
|
|
||||||
<button type="button" class="button is-secondary mt-10" :disabled="loading" @click="testMastodon">
|
|
||||||
<span>Tester</span>
|
|
||||||
</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>
|
|
||||||
<i class="icon-spin animate-spin" v-if="loading"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<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>
|
|
|
@ -1,15 +1,6 @@
|
||||||
<main class="layout-maxed ma-collection-details" id="ma-collection-details" v-cloak @keyup="changeImage">
|
<main class="layout-maxed ma-collection-details" id="app" v-cloak @keyup="changeImage">
|
||||||
|
|
||||||
<h1>
|
<h1>{{item.artists_sort}} - {{item.title}}</h1>
|
||||||
<template v-for="artist in item.artists">
|
|
||||||
<a :href="`/ma-collection?page=1&limit=16&sort=year&order=asc&artist=${artist.name}`">{{artist.name}}</a>
|
|
||||||
<template v-if="artist.join"> {{artist.join}} </template>
|
|
||||||
</template>
|
|
||||||
- {{item.title}}
|
|
||||||
<i class="icon-trash" title="Supprimer cette fiche" @click="showConfirmDelete()"></i>
|
|
||||||
<i class="icon-refresh" title="Mettre à jour les données de cette fiche" @click="updateItem()"></i>
|
|
||||||
<i class="icon-share" title="Partager cet album sur le fédiverse" @click="showModalShare = true" v-if="canShareItem"></i>
|
|
||||||
</h1>
|
|
||||||
<div class="grid sm:grid-cols-3 gap-16">
|
<div class="grid sm:grid-cols-3 gap-16">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img :src="item.thumb %>" :alt="`Miniature pour l'album ${item.title}`" />
|
<img :src="item.thumb %>" :alt="`Miniature pour l'album ${item.title}`" />
|
||||||
|
@ -21,80 +12,271 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<%- include('../../../components/album.ejs') %>
|
<div class="grid md:grid-cols-3 gap-16">
|
||||||
|
<div>
|
||||||
|
<template v-for="album in tracklist">
|
||||||
|
<strong v-if="album.title">{{album.title}}</strong>
|
||||||
|
<ol class="ml-4">
|
||||||
|
<li v-for="track in album.tracks">
|
||||||
|
{{ track.title }} <template v-if="track.duration">({{track.duration}})</template>
|
||||||
|
<ul v-if="track.extraartists && track.extraartists.length > 0" class="sm-hidden ml-4">
|
||||||
|
<li v-for="extra in track.extraartists">
|
||||||
|
<small>{{extra.role}} : {{extra.name}}</small>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<div class="grid grid-cols-2 gap-10">
|
||||||
|
<div>
|
||||||
|
<strong>Genres</strong>
|
||||||
|
<br />
|
||||||
|
<template v-for="(genre, index) in item.genres">
|
||||||
|
{{genre}}<template v-if="index < item.genres.length - 1">, </template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Styles</strong>
|
||||||
|
<br />
|
||||||
|
<span v-for="(style, index) in item.styles">
|
||||||
|
{{style}}<template v-if="index < item.styles.length - 1">, </template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="grid grid-cols-3 gap-10">
|
||||||
|
<div>
|
||||||
|
<strong>Pays</strong>
|
||||||
|
<br />
|
||||||
|
<span>{{item.country}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Année</strong>
|
||||||
|
<br />
|
||||||
|
<span>{{item.year}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Date de sortie</strong>
|
||||||
|
<br />
|
||||||
|
<span>{{item.released}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="grid gap-10">
|
||||||
|
<div>
|
||||||
|
<strong>Format</strong>
|
||||||
|
<ul class="ml-4">
|
||||||
|
<li v-for="(format) in item.formats">
|
||||||
|
{{format.name}}
|
||||||
|
<template v-if="format.descriptions && format.descriptions.length > 0">
|
||||||
|
(<span v-for="(description, index) in format.descriptions">
|
||||||
|
{{description}}<template v-if="index < format.descriptions.length - 1">, </template>
|
||||||
|
</span>)
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="grid grid-cols-2 gap-10">
|
||||||
|
<div>
|
||||||
|
<strong id="identifiers">Codes barres</strong>
|
||||||
|
<ol class="ml-4">
|
||||||
|
<li v-for="identifier in identifiers">
|
||||||
|
{{identifier.value}} ({{identifier.type}})
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<template v-if="item.identifiers.length > identifiersPreviewLength">
|
||||||
|
<button type="button" class="button is-link" v-if="identifiersMode === 'preview'" @click="showAllIdentifiers">
|
||||||
|
Voir la suite
|
||||||
|
</button>
|
||||||
|
<button type="button" class="button is-link" v-if="identifiersMode === 'all'" @click="showLessIdentifiers">
|
||||||
|
Voir moins
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Label</strong>
|
||||||
|
<br />
|
||||||
|
<template v-for="label in item.labels">
|
||||||
|
{{label.name}} {{label.catno}}
|
||||||
|
<br />
|
||||||
|
</template>
|
||||||
|
<hr />
|
||||||
|
<strong>Sociétés</strong>
|
||||||
|
<br />
|
||||||
|
<template v-for="company in item.companies">
|
||||||
|
<strong>{{company.entity_type_name}}</strong> : {{company.name}}
|
||||||
|
<br />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <hr />
|
||||||
|
<div class="grid gap-10">
|
||||||
|
<div>
|
||||||
|
<strong>Note</strong>
|
||||||
|
<br />
|
||||||
|
<span>{{item.notes}}</span>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
<hr />
|
||||||
|
<div class="grid gap-10">
|
||||||
|
<div>
|
||||||
|
<strong>Vidéos</strong>
|
||||||
|
<dl>
|
||||||
|
<template v-for="video in item.videos">
|
||||||
|
<dt>
|
||||||
|
<a :href="video.uri" target="_blank" rel="noopener noreferrer">{{video.title}}</a>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
{{video.description}}
|
||||||
|
</dd>
|
||||||
|
</template>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal" :class="{'is-visible': modalIsVisible}">
|
<div class="modal" :class="{'is-visible': modalIsVisible}">
|
||||||
<div class="modal-background"></div>
|
<div class="modal-background"></div>
|
||||||
<button type="button" aria-label="Fermer" class="close" @click="toggleModal"></button>
|
<button type="button" aria-label="Fermer" class="close" @click="toggleModal"></button>
|
||||||
<div class="carousel">
|
<button type="button" aria-label="Image précédente" class="navigation previous" @click="previous" v-if="index > 0">
|
||||||
<button type="button" aria-label="Image précédente" class="navigation previous" @click="previous">
|
<i class="icon-left-open"></i>
|
||||||
<i class="icon-left-open"></i>
|
</button>
|
||||||
</button>
|
<button type="button" aria-label="Image suivante" class="navigation next" @click="next" v-if="index + 1 < item.images.length">
|
||||||
<div class="text-center">
|
<i class="icon-right-open"></i>
|
||||||
<img :src="preview" />
|
</button>
|
||||||
</div>
|
|
||||||
<button type="button" aria-label="Image suivante" class="navigation next" @click="next">
|
|
||||||
<i class="icon-right-open"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal" :class="{'is-visible': showModalDelete}">
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header></header>
|
<img :src="preview" />
|
||||||
<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="toggleModalDelete">Annuler</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal" :class="{'is-visible': showModalShare}">
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-card">
|
|
||||||
<header>Partager un album sur le fédiverse</header>
|
|
||||||
<section>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
|
||||||
<div class="field">
|
|
||||||
<label for="shareMessage">Message</label>
|
|
||||||
<textarea
|
|
||||||
name="shareMessage"
|
|
||||||
id="shareMessage"
|
|
||||||
v-model="shareMessage"
|
|
||||||
rows="6"
|
|
||||||
></textarea>
|
|
||||||
Caractères utilisés : {{ shareMessageLength }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<small>
|
|
||||||
Variables possibles :
|
|
||||||
<ul>
|
|
||||||
<li>{artist}, exemple : Iron Maiden</li>
|
|
||||||
<li>{album}, exemple : Powerslave</li>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<footer>
|
|
||||||
<button :class="['button is-primary', shareSubmiting ? 'is-disabled' : '']" @click="shareAlbum">Partager</button>
|
|
||||||
<button class="button" @click="showModalShare=!showModalShare">Annuler</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const item = <%- JSON.stringify(page.item) %>;
|
Vue.createApp({
|
||||||
const canShareItem = <%= user.mastodon?.publish || false %>;
|
data() {
|
||||||
|
return {
|
||||||
|
item: <%- JSON.stringify(page.item) %>,
|
||||||
|
tracklist: [],
|
||||||
|
identifiers: [],
|
||||||
|
modalIsVisible: false,
|
||||||
|
identifiersMode: 'preview',
|
||||||
|
identifiersPreviewLength: 16,
|
||||||
|
preview: null,
|
||||||
|
index: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.setTrackList();
|
||||||
|
this.setIdentifiers();
|
||||||
|
|
||||||
|
window.addEventListener("keydown", this.changeImage);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
window.removeEventListener('keydown', this.changeImage);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setIdentifiers() {
|
||||||
|
this.identifiers = [];
|
||||||
|
|
||||||
|
let max = this.identifiersMode == 'preview' && this.item.identifiers.length > this.identifiersPreviewLength ? this.identifiersPreviewLength : this.item.identifiers.length;
|
||||||
|
|
||||||
|
for ( let i = 0 ; i < max ; i += 1 ) {
|
||||||
|
this.identifiers.push(this.item.identifiers[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setTrackList() {
|
||||||
|
let subTrack = {
|
||||||
|
type: null,
|
||||||
|
title: null,
|
||||||
|
tracks: [],
|
||||||
|
};
|
||||||
|
for (let i = 0 ; i < this.item.tracklist.length ; i += 1 ) {
|
||||||
|
const {
|
||||||
|
type_,
|
||||||
|
title,
|
||||||
|
position,
|
||||||
|
duration,
|
||||||
|
extraartists,
|
||||||
|
} = this.item.tracklist[i];
|
||||||
|
|
||||||
|
if ( type_ === 'heading' ) {
|
||||||
|
if ( subTrack.type ) {
|
||||||
|
this.tracklist.push(subTrack);
|
||||||
|
subTrack = {
|
||||||
|
type: null,
|
||||||
|
title: null,
|
||||||
|
tracks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
subTrack.type = type_;
|
||||||
|
subTrack.title = title;
|
||||||
|
} else {
|
||||||
|
subTrack.tracks.push({
|
||||||
|
title,
|
||||||
|
position,
|
||||||
|
duration,
|
||||||
|
extraartists
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tracklist.push(subTrack);
|
||||||
|
},
|
||||||
|
setImage() {
|
||||||
|
this.preview = this.item.images[this.index].uri;
|
||||||
|
},
|
||||||
|
showGallery(event) {
|
||||||
|
const item = event.target.tagName === 'IMG' ? event.target.parentElement : event.target;
|
||||||
|
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
} = item.dataset;
|
||||||
|
|
||||||
|
this.index = Number(index);
|
||||||
|
this.modalIsVisible = true;
|
||||||
|
|
||||||
|
this.setImage();
|
||||||
|
},
|
||||||
|
toggleModal() {
|
||||||
|
this.modalIsVisible = !this.modalIsVisible;
|
||||||
|
},
|
||||||
|
previous() {
|
||||||
|
this.index = this.index > 0 ? this.index - 1 : this.item.images.length -1;
|
||||||
|
this.setImage();
|
||||||
|
},
|
||||||
|
next() {
|
||||||
|
this.index = (this.index +1) === this.item.images.length ? 0 : this.index + 1;
|
||||||
|
this.setImage();
|
||||||
|
},
|
||||||
|
changeImage(event) {
|
||||||
|
const direction = event.code;
|
||||||
|
|
||||||
|
if ( this.modalIsVisible && ['ArrowRight', 'ArrowLeft', 'Escape'].indexOf(direction) !== -1 ) {
|
||||||
|
switch (direction) {
|
||||||
|
case 'ArrowRight':
|
||||||
|
return this.next();
|
||||||
|
case 'ArrowLeft':
|
||||||
|
return this.previous();
|
||||||
|
default:
|
||||||
|
this.modalIsVisible = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showAllIdentifiers() {
|
||||||
|
this.identifiersMode = 'all';
|
||||||
|
this.setIdentifiers();
|
||||||
|
},
|
||||||
|
showLessIdentifiers() {
|
||||||
|
this.identifiersMode = 'preview';
|
||||||
|
this.setIdentifiers();
|
||||||
|
|
||||||
|
document.querySelector('#identifiers').scrollIntoView({ behavior: 'smooth' });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).mount('#app');
|
||||||
</script>
|
</script>
|
|
@ -1,4 +1,4 @@
|
||||||
<main class="layout-maxed" id="exporter">
|
<main class="layout-maxed" id="app">
|
||||||
<h1>Exporter ma collection</h1>
|
<h1>Exporter ma collection</h1>
|
||||||
<p>
|
<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 :
|
Les formats CSV et Excel sont facilement lisiblent par un humain. Dans ces 2 formats vous trouverez seulement les informations principales de vos albums, à savoir :
|
||||||
|
@ -44,4 +44,25 @@
|
||||||
Exporter
|
Exporter
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
format: 'xml',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
exportCollection(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
window.open(`/api/v1/albums?exportFormat=${this.format}`, '_blank');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
|
@ -1,43 +0,0 @@
|
||||||
<main class="layout-maxed" id="importer">
|
|
||||||
<h1>Importer une collection</h1>
|
|
||||||
<p>
|
|
||||||
Il est actuellement possible d'importer une collection provenant de discogs.
|
|
||||||
<br />
|
|
||||||
Vous devez dans un premier temps vous rendre sur la page <a href="https://www.discogs.com/fr/users/export" target="_blank" rel="noopener noreferrer">Exporter</a> de discogs.
|
|
||||||
<br />
|
|
||||||
Une fois exporter vous recevrez un mail de Discogs avec un lien de téléchargement. Une fois le fichier .zip téléchargé vous devez en extraire le fichier .csv afin de l'importer dans MusicTopus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
D'autres formats d'imports seront ajoutés par la suite, comme l'import entre 2 instances MusicTopus.
|
|
||||||
</p>
|
|
||||||
<div class="flash info">
|
|
||||||
<div class="header">
|
|
||||||
Information
|
|
||||||
</div>
|
|
||||||
<div class="body">
|
|
||||||
Si un album est déjà présent en base celui-ci sera ignoré.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form @submit="importCollection">
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label for="file">Fichier .csv</label>
|
|
||||||
<input type="file" name="file" id="file" @change="handleFileUpload( $event )" accept=".csv">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<span>
|
|
||||||
Albums à impoter : <strong>{{content.length}}</strong>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="button is-primary my-16" :disabled="disabled">
|
|
||||||
<i v-if="['parse', 'submit'].includes(state)" class="icon-spin animate-spin"></i>
|
|
||||||
<span v-if="state === 'default'">Importer</span>
|
|
||||||
<span v-if="state === 'parse'">Analyse en cours...</span>
|
|
||||||
<span v-if="state === 'submit'">Importation en cours... ({{imported}}/{{content.length}})</span>
|
|
||||||
<span v-if="state === 'done'">Importatation terminée</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
|
@ -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>
|
|
@ -1,122 +0,0 @@
|
||||||
<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,39 +1,22 @@
|
||||||
<section class="box mini" id="contact">
|
<section class="box">
|
||||||
<h1>Nous contacter</h1>
|
<h1>Nous contacter</h1>
|
||||||
<form @submit="send" <% if (config.mailMethod === 'formspree' ) { %> id="contact" method="POST" action="https://formspree.io/f/<%= config.formspreeId %>" <% } %>>
|
<form action="https://formspree.io/f/<%= config.formspreeId %>" method="POST">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-16">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-16">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="email">Addresse e-mail*</label>
|
<label for="email">Addresse e-mail*</label>
|
||||||
<input type="email" name="email" id="email" v-model="email" required />
|
<input type="email" name="email" id="email" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="name">Prénom, nom</label>
|
<label for="name">Prénom, nom</label>
|
||||||
<input type="text" name="name" id="name" v-model="name" />
|
<input type="text" name="name" id="name" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="message">Message*</label>
|
<label for="message">Message*</label>
|
||||||
<textarea name="message" id="message" rows="6" required v-model="message" ></textarea>
|
<textarea name="message" id="message" rows="6" required ></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if (config.mailMethod !== 'formspree' ) { %>
|
<button type="submit" class="button is-primary">Envoyer</button>
|
||||||
<img src="/api/v1/contact" alt="Captcha" />
|
|
||||||
<div class="field">
|
|
||||||
<label for="captcha">Captcha</label>
|
|
||||||
<input type="text" name="captcha" id="captcha" v-model="captcha" required />
|
|
||||||
</div>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<button type="submit" class="button is-primary" :disabled="loading">
|
|
||||||
<% if (config.mailMethod !== 'formspree' ) { %>
|
|
||||||
<i class="icon-spin animate-spin" v-if="loading"></i>
|
|
||||||
<% } %>
|
|
||||||
Envoyer
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
|
||||||
const contactMethod = '<%= config.mailMethod %>';
|
|
||||||
</script>
|
|
Loading…
Reference in New Issue
Block a user