Compare commits

...

51 Commits

Author SHA1 Message Date
859924d6be feat: Modification du design des checkbox
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-06-20 14:33:14 +02:00
a26ec95983 feat: Modification du design du bouton de la certitude
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-06-20 01:01:55 +02:00
575cc28717 chore: lint
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-05-27 11:47:48 +02:00
e5665a24e7 feat: Ajout de la certitude d'un score 2025-05-27 11:44:52 +02:00
6e42e8c800 feat: Ajout de l'inclusion basale
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-27 11:34:28 +02:00
bb7d5f16e1 feat: Suppression du bouton de partage
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-04-11 11:10:22 +02:00
1e30a79f73 wip
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-04-30 01:20:04 +02:00
455545482f feat: Upgrade node on ci
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-30 00:03:01 +02:00
2de488f3bf feat: Ajout du mode offline
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-29 23:56:05 +02:00
370949cfe0 docs: Ajout de la documentation pour les icones
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-04-23 15:21:10 +02:00
6ff6c5229d feat: Ajout de l'environement pour le site fourche 2024-04-23 15:20:49 +02:00
61bc2541f7 feat: Ajout d'un lien vers une documentation
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-20 00:38:12 +02:00
1f67ee9810 fix: Footer position
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-03-14 16:13:58 +01:00
ccd83369fd feat: Ajout du déploiement automatique de DMH
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-14 15:57:35 +01:00
7de52a3b45 fix: import dependencies
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-03-06 14:58:57 +01:00
398d0f6957 feat: Ajout de la case à cocher pour l'environement test
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2024-03-06 14:43:32 +01:00
c0a0fda9c2 fix: Test deploiement
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-03-06 14:25:06 +01:00
41135bb2c7 fix: Mise à jour de la donnée en lancant l'évènement change
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-06 14:16:40 +01:00
2ea897e563 fix: link
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-02-21 16:31:15 +01:00
6ca8c1bdcf feat: Ajout du drapeau anglais
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 16:26:25 +01:00
7555056b5b fix: Le design de la page d'accueil 2024-02-21 15:49:05 +01:00
35e970a160 feat: Mise à jour
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 15:41:25 +01:00
13c863d09b feat: Update
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 15:26:44 +01:00
d7922166ba feat: Modification de l'échelle de résultat
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-02-21 14:36:28 +01:00
d2f5d7d840 feat: Ajout des traductions depuis l'administration
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is passing
2024-02-15 10:16:37 +01:00
2bcddaabcf feat: Ajout de traductions
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
2024-02-15 09:57:04 +01:00
79a0ab2a1a feat: Ajout de la traduction du titre dans la vue d'un scrore
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-15 09:33:57 +01:00
d6c1992e11 feat: Ajout de l'environnement test
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-05 15:05:15 +01:00
7a61fc4907 feat: filtre les scores en production
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-10-19 16:49:04 +02:00
2960b936b1 feat Ajout du build automatique de staging
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-10-19 15:55:56 +02:00
483a0f14ec feat: Modification du conteneur du text des checkbox
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-06-05 18:06:03 +02:00
7cce542804 feat: Inverse le design des checkbox
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-05 17:42:45 +02:00
f26a7b0c21 save (README.md): /README.md
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-09 12:14:01 +00:00
fd4bc23930 feat: Agrandissement de l'icone boulier
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 23:32:12 +02:00
ac6ff78934 feat: Modification du design
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-06 15:54:31 +02:00
f32f4a019d feat: Fix le centrage sur iphone
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-05 16:55:36 +02:00
c7c1e0c0ed feat: Modification design pour mac
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 16:40:29 +02:00
077c36d3be feat: Ajout des notifications
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-03 17:38:49 +02:00
4c3d796b17 feat: Filtre les boutons
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-03 17:38:21 +02:00
c85fdc8d27 feat: Suppression de html2canvas
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-03 17:20:25 +02:00
5b52cee3fe feat: Suppression d'une configuration inutile
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-03 17:17:43 +02:00
e4de9aa3e5 feat: Remove staging compilation
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-03 17:12:31 +02:00
f8958416fc feat: Changement de librairie
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-03 16:30:27 +02:00
da9fd594a4 fix: Popup design 2023-05-03 15:39:20 +02:00
4d9445d3c5 feat: Suppression de configuration inutile
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-02 23:35:09 +02:00
0d13783421 feat: Design ajout d'un logo pour le téléchargement
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-02 23:28:28 +02:00
ce5b3ec3ab feat: Center buttons 2023-05-02 23:19:22 +02:00
bd1e7c4030 feat: Ajout du design de la calculette
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-02 23:12:43 +02:00
3e0225f84e feat: Ajout du calcul
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
2023-05-02 21:42:36 +02:00
9d3e144748 feat: Mise à jour du design de l'application
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-02 16:46:54 +02:00
58a6da1d94 feat: Modification du design
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-04-26 00:46:24 +02:00
25 changed files with 7672 additions and 4001 deletions

View File

@ -34,6 +34,42 @@ kind: secret
name: PRODUCTION_AWS_SECRET_ACCESS_KEY
data: JX44CwSuvFmYhJCW+vxC4GmUKbO83VyYeImCvIgtkdeC1cRtNEgCA6gWoclT4vSBrk6AW6iQnh3MSpIhOERSn/prcWiTwbBn6YqdUaQ18B9/Dp3Qk1RtOkS2wKI=
---
# drone encrypt Weko/ceiba-scores $AWS_ACCESS_KEY_ID
kind: secret
name: TEST_AWS_ACCESS_KEY_ID
data: Bf6VZlmdGQGAOQtwtiOzRCqhnFaW310rw9LqMFVoaQBHiCbso23Zrt6G1N3J0nYGI/oLMhM3
---
# drone encrypt Weko/ceiba-scores $AWS_SECRET_ACCESS_KEY
kind: secret
name: TEST_AWS_SECRET_ACCESS_KEY
data: kIq0RuZPg2gSamnbei121OHNtupioEYyVay8CnNxNz+u0fEh0c1qnAKM506h+XbwMr+HDyQEUNDM09fqzM14xmRxz8EZtqg6hXVPUcfEIG9nXI9VtnCKMwoKTMQ=
---
# drone encrypt Weko/ceiba-scores $AWS_ACCESS_KEY_ID
kind: secret
name: DMH_AWS_ACCESS_KEY_ID
data: ZJi1IozPCoLgmwBnQy9OkOe7ByerC/b94aTNivtkAYh1K9A4WR+eXGNrbyXZci06HYjV8pBU
---
# drone encrypt Weko/ceiba-scores $AWS_SECRET_ACCESS_KEY
kind: secret
name: DMH_AWS_SECRET_ACCESS_KEY
data: sLiY4plTjS73Dzw0qHaAuSR4PiHGIhb6H2i1KyP2GpjvIDyaxbR2gjsjzn3E8e0gAtCiIM10nbvvAMvGmPoh1dLuYlmHj2YdOQA8wbjkA9jSqe9owbfxfrcEuDA=
---
# drone encrypt Weko/ceiba-scores $AWS_ACCESS_KEY_ID
kind: secret
name: FOURCHE_AWS_ACCESS_KEY_ID
data: GgpM5vSzE4N4ldcs+Db+sjG/siXmO+sYvw9DJYr0puAIv3jejlzwa3zJGcwGb2fmwCqyxied
---
# drone encrypt Weko/ceiba-scores $AWS_SECRET_ACCESS_KEY
kind: secret
name: FOURCHE_AWS_SECRET_ACCESS_KEY
data: rEE5ttbao6dla92YOw1vh1VE1sBWAjen+jfP/ZdUSDnFlkZKY56ms+V0jOReGulhwbVRxQijp1fTS43TW/vJbq3XSeTVUwDGTHLZFQ+yGObvr+4DKOVIRqp/rJE=
---
kind: pipeline
type: docker
@ -41,15 +77,17 @@ name: prod
steps:
- name: install npm
image: node:current-alpine
image: node:20.12.2-alpine
volumes:
- name: node_modules
path: /drone/src/node_modules
commands:
- npm i
- npm i --include dev
- npm run fetchData
- npm run lint
#- npm run lint
- npm run build
environment:
NODE_ENV: 'production'
- name: deploy on s3
image: minio/mc
@ -85,15 +123,17 @@ name: staging
steps:
- name: install npm
image: node:current-alpine
image: node:20.12.2-alpine
volumes:
- name: node_modules
path: /drone/src/node_modules
commands:
- npm i
- npm run fetchData
- npm run lint
#- npm run lint
- npm run build
environment:
NODE_ENV: 'staging'
- name: deploy on s3
image: minio/mc
@ -120,4 +160,142 @@ steps:
volumes:
- name: node_modules
host:
path: /tmp/drone/cache/weko/scores.ceiba-conseil.com/node_modules
path: /tmp/drone/cache/weko/staging.scores.ceiba-conseil.com/node_modules
---
kind: pipeline
type: docker
name: test
steps:
- name: install npm
image: node:20.12.2-alpine
volumes:
- name: node_modules
path: /drone/src/node_modules
commands:
- npm i
- npm run fetchData
#- npm run lint
- npm run build
environment:
NODE_ENV: 'test'
- name: deploy on s3
image: minio/mc
environment:
AWS_ACCESS_KEY_ID:
from_secret: TEST_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: TEST_AWS_SECRET_ACCESS_KEY
commands:
- mc alias set garage https://s3.garage.resilien.cloud $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY --api S3v4
- mc mirror --overwrite /drone/src/dist garage/test.scores.ceiba-conseil.com
- name: notify
image: plugins/matrix@sha256:f1affb31b0c86963c97c6f976fa0dcb3cc84272057fd8558d609d28b3064bd7f
settings:
homeserver: https://converser.eu
roomid: "QwOITmkKxRJJyCSDOZ:converser.eu"
userid: "resilien:converser.eu"
accesstoken:
from_secret: MATRIX_ACCESSTOKEN
when:
status: [ failure ]
volumes:
- name: node_modules
host:
path: /tmp/drone/cache/weko/test.scores.ceiba-conseil.com/node_modules
---
kind: pipeline
type: docker
name: DMH
steps:
- name: install npm
image: node:20.12.2-alpine
volumes:
- name: node_modules
path: /drone/src/node_modules
commands:
- npm i
- npm run fetchData
#- npm run lint
- apk add --no-cache jq
- echo $(cat src/data.json | jq -c '[.[4]]') > src/data.json
- npm run build
- name: deploy on s3
image: minio/mc
environment:
AWS_ACCESS_KEY_ID:
from_secret: DMH_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: DMH_AWS_SECRET_ACCESS_KEY
commands:
- mc alias set garage https://s3.garage.resilien.cloud $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY --api S3v4
- mc mirror --overwrite /drone/src/dist garage/dmh.ceiba-conseil.com
- name: notify
image: plugins/matrix@sha256:f1affb31b0c86963c97c6f976fa0dcb3cc84272057fd8558d609d28b3064bd7f
settings:
homeserver: https://converser.eu
roomid: "QwOITmkKxRJJyCSDOZ:converser.eu"
userid: "resilien:converser.eu"
accesstoken:
from_secret: MATRIX_ACCESSTOKEN
when:
status: [ failure ]
volumes:
- name: node_modules
host:
path: /tmp/drone/cache/weko/dmh.ceiba-conseil.com/node_modules
---
kind: pipeline
type: docker
name: FOURCHE
steps:
- name: install npm
image: node:20.12.2-alpine
volumes:
- name: node_modules
path: /drone/src/node_modules
commands:
- npm i
- npm run fetchData
#- npm run lint
- apk add --no-cache jq
- echo $(jq '[.[] | select(.id == 7 or .id == 11)]' src/data.json) > src/data.json
- npm run build
- name: deploy on s3
image: minio/mc
environment:
AWS_ACCESS_KEY_ID:
from_secret: FOURCHE_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: FOURCHE_AWS_SECRET_ACCESS_KEY
commands:
- mc alias set garage https://s3.garage.resilien.cloud $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY --api S3v4
- mc mirror --overwrite /drone/src/dist garage/fourche.ceiba-conseil.com
- name: notify
image: plugins/matrix@sha256:f1affb31b0c86963c97c6f976fa0dcb3cc84272057fd8558d609d28b3064bd7f
settings:
homeserver: https://converser.eu
roomid: "QwOITmkKxRJJyCSDOZ:converser.eu"
userid: "resilien:converser.eu"
accesstoken:
from_secret: MATRIX_ACCESSTOKEN
when:
status: [ failure ]
volumes:
- name: node_modules
host:
path: /tmp/drone/cache/weko/fourche.ceiba-conseil.com/node_modules

View File

@ -10,5 +10,6 @@ module.exports = {
],
env: {
"vue/setup-compiler-macros": true,
node: true,
},
};

5
.gitignore vendored
View File

@ -28,5 +28,8 @@ coverage
*.sw?
# app
src/data.json
src/*.json
public/answers
public/homepage.webp
public/logo.png
public/favicon/*.png

View File

@ -2,34 +2,41 @@
Vous trouverez ici le code source de l'application de Scores de [Ceiba Conseil](https://www.ceiba-conseil.com/).
## Recommended IDE Setup
## Information
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin).
- Le projet est développé par _[Weko](https://weko.io)_ et hébergé par _[RésiLien](https://resilien.fr)_
- L'application utilise [un script](./scripts/fetchData.js) permettant d'importer des contenus depuis [une interface d'administration](https://admin.ceiba-conseil.com)
- [Un outil automatique](https://ci.resilien.fr/Weko/ceiba-scores) permet une compilation automatique du projet lorsqu'un changement est effectué coté administration
- [Les emojis](https://twemoji-cheatsheet.vercel.app/) sont libres de droit proposé par _Twitter_
- _RésiLien_ reçoit des notifications automatiques lorsqu'un problème arrive lors de la compilation sur le canal _Matrix_ suivant https://matrix.to/#/#resilien-monitoring:converser.eu
## Customize configuration
## Développement
See [Vite Configuration Reference](https://vitejs.dev/config/).
L'application a été développée avec le framework Javascript _[Vue.js](https://vuejs.org/)_ et l'outil _[Vite](https://vitejs.dev/)_.
Il utilise _[NPM](https://www.npmjs.com/)_ pour la gestion des dépendances Javascript.
## Project Setup
### Recommandation pour votre environnement de développement
```sh
npm install
```
_[VSCode](https://code.visualstudio.com/)_ + _[Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)_ (et désactiver _Vetur_) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin).
### Compile and Hot-Reload for Development
Une configuration spécifique à _Vite_ peut être mise en place, voir [la référence](https://vitejs.dev/config/).
```sh
npm run dev
```
### Commandes
### Compile and Minify for Production
- Installation des dépendances : `npm install`
- Import des données de l'administration : `npm run fetchData`
- Compilation avec du _Hot-Reload_ (Rechargement à chaud) pour le développement : `npm run dev`
- Compilation et minification pour la production : `npm run build`
- Les règles de mise en forme sont assurées par _[ESLint](https://eslint.org/)_ : `npm run lint`
```sh
npm run build
```
## Logiciels utilisés
### Lint with [ESLint](https://eslint.org/)
_RésiLien_ met en place des outils open source eprouvés dans le temps et dont les communautés sont assez grandes pour permettre une perenité de ses projets.
```sh
npm run lint
```
Voici les différentes briques utilisé autour du projet :
- _[Garage](https://garagehq.deuxfleurs.fr/)_ pour l'hébergement développé par l'association [Deuxfleurs](https://deuxfleurs.fr/)
- _[Directus](https://directus.io/)_ pour l'administration
- _[Drone](https://drone.io)_ pour la compilation automatique du projet
- _[Matrix](https://fr.wikipedia.org/wiki/Matrix_(protocole))_ est un protocole ouvert pour de la communication en temps réel
Icon : https://www.svgrepo.com/collection/solar-linear-icons/

View File

@ -2,9 +2,29 @@
<html lang="fr">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/favicon/ms-icon-144x144.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scores Ceiba</title>
<meta name="description" content="L'application Scores de Ceiba">
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
</head>
<body>
<div id="app"></div>

9489
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,22 +10,24 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"@splidejs/vue-splide": "^0.5.18",
"pinia": "^2.0.11",
"@splidejs/vue-splide": "^0.6.12",
"html-to-image": "^1.11.11",
"pinia": "^2.1.7",
"pinia-plugin-persist": "^1.0.0",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
"sharp": "^0.30.3",
"vue": "^3.2.31",
"vue-router": "^4.0.12"
"sass": "^1.75.0",
"sass-loader": "^14.2.1",
"sharp": "^0.33.3",
"vite-plugin-pwa": "^0.19.8",
"vue": "^3.4.26",
"vue-router": "^4.3.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@vitejs/plugin-vue": "^2.2.2",
"@vue/eslint-config-prettier": "^7.0.0",
"@rushstack/eslint-patch": "^1.10.2",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^9.0.0",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^8.2.0",
"prettier": "^2.5.1",
"vite": "^2.8.4"
"prettier": "^3.2.5",
"vite": "^5.2.10"
}
}

1
public/abacus.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#CCD6DD" d="M3 16v-2h30v2zm0 7v-2h30v2zm0 7v-2h30v2zM3 9V7h30v2z"/><path fill="#D5AB88" d="M35 33V1c0-.552-.448-1-1-1h-1c-.552 0-1 .448-1 1H4c0-.552-.448-1-1-1H2c-.552 0-1 .448-1 1v32h34zM4 4h28v29H4V4z"/><path fill="#3B94D9" d="M19 5.5c-.829 0-1.5.671-1.5 1.5 0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5c0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5c0-.829-.671-1.5-1.5-1.5S8.5 6.171 8.5 7c0-.829-.671-1.5-1.5-1.5S5.5 6.171 5.5 7v2c0 .829.671 1.5 1.5 1.5S8.5 9.829 8.5 9c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5.828 0 1.5-.671 1.5-1.5V7c0-.829-.672-1.5-1.5-1.5z"/><path fill="#BE1931" d="M19 12.5c-.829 0-1.5.671-1.5 1.5 0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5c0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5c0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5c0-.829-.671-1.5-1.5-1.5s-1.5.671-1.5 1.5v2c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5s1.5-.671 1.5-1.5c0 .829.671 1.5 1.5 1.5.828 0 1.5-.671 1.5-1.5v-2c0-.829-.672-1.5-1.5-1.5z"/><path fill="#5C913B" d="M19 19.5c-.829 0-1.5.672-1.5 1.5 0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5v2c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5.828 0 1.5-.672 1.5-1.5v-2c0-.828-.672-1.5-1.5-1.5z"/><path fill="#FFAC33" d="M19 26.5c-.829 0-1.5.672-1.5 1.5 0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5c0-.828-.671-1.5-1.5-1.5s-1.5.672-1.5 1.5v2c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5s1.5-.672 1.5-1.5c0 .828.671 1.5 1.5 1.5.828 0 1.5-.672 1.5-1.5v-2c0-.828-.672-1.5-1.5-1.5z"/><path fill="#3B94D9" d="M29 10.5c-.828 0-1.5-.671-1.5-1.5V7c0-.829.672-1.5 1.5-1.5s1.5.671 1.5 1.5v2c0 .829-.672 1.5-1.5 1.5z"/><path fill="#BE1931" d="M29 17.5c-.828 0-1.5-.671-1.5-1.5v-2c0-.829.672-1.5 1.5-1.5s1.5.671 1.5 1.5v2c0 .829-.672 1.5-1.5 1.5z"/><path fill="#5C913B" d="M29 24.5c-.828 0-1.5-.672-1.5-1.5v-2c0-.828.672-1.5 1.5-1.5s1.5.672 1.5 1.5v2c0 .828-.672 1.5-1.5 1.5z"/><path fill="#FFAC33" d="M29 31.5c-.828 0-1.5-.672-1.5-1.5v-2c0-.828.672-1.5 1.5-1.5s1.5.672 1.5 1.5v2c0 .828-.672 1.5-1.5 1.5z"/><path fill="#BF6952" d="M35 33H1c-.552 0-1 .448-1 1v1c0 .552.448 1 1 1h34c.552 0 1-.448 1-1v-1c0-.552-.448-1-1-1z"/><script xmlns=""/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/favicon/ms-icon-70x70.png"/><square150x150logo src="/favicon/ms-icon-150x150.png"/><square310x310logo src="/favicon/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

View File

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/favicon\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/favicon\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/favicon\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/favicon\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/favicon\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/favicon\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

3
public/spin.svg Normal file
View File

@ -0,0 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<g><path d="M758.7,873c10.4,17.7,4.6,41.2-13.6,51.1c-18,10.4-40.7,4.3-50.9-13.7c-10.9-17.7-4.5-40.7,13.1-51.1C725.7,848.9,748.3,855.4,758.7,873L758.7,873z M537.9,952.8L537.9,952.8c0,20.4-16.9,37.5-38,37.5c-20.4,0-37.5-16.6-37.5-37.5v-26.2c0-20.9,17.1-37.5,37.5-37.5c20.8,0,38,16.6,38,37.5V952.8L537.9,952.8z M306.6,910.5L306.6,910.5c-10.7,18.5-33.7,24.4-51.4,13.9c-18.2-10.2-24.7-33.2-13.9-51.4l27.8-48.7c10.7-18,33.7-24.1,51.9-13.9c17.7,10.5,24.1,33.7,13.4,51.4L306.6,910.5L306.6,910.5z M127.3,759L127.3,759c-18.2,10.2-41.5,4-51.4-13.9c-10.7-17.7-4.6-41,13.3-51.4l75-43.4c17.9-9.9,41.2-4.3,51.3,13.9c10.2,17.7,4.1,41-13.9,51.4L127.3,759L127.3,759z M47.4,537.6L47.4,537.6c-20.8,0-37.4-16.9-37.4-37.5c0-20.9,16.6-37.7,37.4-37.7h116.8c20.8,0,37.5,16.9,37.5,37.5c0,20.9-16.6,37.7-37.5,37.7H47.4L47.4,537.6z M89.2,306.1L89.2,306.1c-18.2-10.2-24-33.2-13.3-51.1c9.9-18.2,33.2-24.4,51.4-13.9l126.8,73.6c18,10.2,23.9,33.2,14,50.8c-10.7,18.2-33.7,24.1-51.4,13.9L89.2,306.1L89.2,306.1z M241.3,126.7L241.3,126.7l88.3,153.4c10.8,17.9,33.7,24.4,51.7,13.7c17.9-10.2,23.8-33.5,13.3-51.4L306,89.2c-10.2-17.7-33.2-24.1-50.8-13.7C236.9,86,231.1,109.1,241.3,126.7L241.3,126.7z M462.4,47.5L462.4,47.5c0-20.3,17.1-37.7,37.5-37.7c20.8,0,38,16.9,38,37.7v176.7c0,20.9-16.9,37.7-38,38c-20.4,0-37.5-16.6-37.5-38V47.5L462.4,47.5z M693.6,89.2L693.6,89.2c10.7-18.2,33.5-24.1,51.5-13.9c18.2,10.2,24.6,33.2,13.8,51.4l-88.6,153.4c-9.8,17.9-33.4,24.4-51.3,13.9c-18-10.5-23.9-33.7-13.7-51.6L693.6,89.2L693.6,89.2z M873.5,241L873.5,241l-153.6,88.6c-17.9,10.2-24.1,33.2-14,51.4c10.7,17.7,33.8,23.5,51.4,13.7l153.7-88.6c17.6-10.2,24-33.2,13.4-51.1C914.3,236.7,891.2,230.3,873.5,241L873.5,241z M952.5,462.4L952.5,462.4c21.1,0,37.8,16.9,37.5,37.7c0,20.6-16.4,37.5-37.5,37.5h-177c-20.3,0-37.4-16.9-37.4-37.7c0-20.6,17.1-37.5,37.4-37.5H952.5L952.5,462.4z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

91
public/test.html Normal file
View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<style>
body {
padding: 0;
margin: 0;
}
svg:not(:root) {
display: block;
}
.playable-code {
background-color: #f4f7f8;
border: none;
border-left: 6px solid #558abb;
border-width: medium medium medium 6px;
color: #4d4e53;
height: 100px;
width: 90%;
padding: 10px 10px 0;
}
.playable-canvas {
border: 1px solid #4d4e53;
border-radius: 2px;
}
.playable-buttons {
text-align: right;
width: 90%;
padding: 5px 10px 5px 26px;
}
</style>
<title>Navigator: share() method - sharing_files - code sample</title>
</head>
<body>
<div>
<label for="files">Select images to share:</label>
<input id="files" type="file" accept="image/*" multiple />
</div>
<button id="share" type="button">Share your images!</button>
<output id="output"></output>
<script>
const input = document.getElementById("files");
const output = document.getElementById("output");
document.getElementById("share").addEventListener("click", async () => {
const files = input.files;
if (files.length === 0) {
output.textContent = "No files selected.";
return;
}
// feature detecting navigator.canShare() also implies
// the same for the navigator.share()
if (!navigator.canShare) {
output.textContent = `Your browser doesn't support the Web Share API.`;
return;
}
if (navigator.canShare({ files })) {
try {
await navigator.share({
files,
title: "Images",
text: "Beautiful images",
});
output.textContent = "Shared!";
} catch (error) {
output.textContent = `Error: ${error.message}`;
}
} else {
output.textContent = `Your system doesn't support sharing these files.`;
}
});
</script>
</body>
</html>

View File

@ -30,7 +30,33 @@ async function fetchAsset(uuid) {
return fetch(url);
}
async function fetchData() {
async function fetchCertitudesData() {
const fields = ["*", "translations.*"];
const url = `/items/certitudes?${fields
.map((item) => `fields[]=${item}`)
.join("&")}`;
let certitudes = (await fetchJSONApi(url)).data;
await fs.writeFile(
"./src/certitudes.json",
JSON.stringify(certitudes),
"utf8",
);
}
async function fetchCertitudesResultsData() {
const fields = ["*", "translations.*"];
const url = `/items/certitudes_results?${fields
.map((item) => `fields[]=${item}`)
.join("&")}`;
let certitudes = (await fetchJSONApi(url)).data;
await fs.writeFile(
"./src/certitudesResults.json",
JSON.stringify(certitudes),
"utf8",
);
}
async function fetchScoresData() {
const fields = [
"*",
"translations.*",
@ -47,28 +73,39 @@ async function fetchData() {
const url = `/items/scores?${fields
.map((item) => `fields[]=${item}`)
.join("&")}`;
const data = (await fetchJSONApi(url)).data;
await fs.writeFile("./src/data.json", JSON.stringify(data), "utf8");
let scores = (await fetchJSONApi(url)).data;
if (process.env.NODE_ENV == "production") {
console.log("Suppresion des scores avec le mode brouillon en production");
scores = scores.filter((score) => !score.draft);
} else if (process.env.NODE_ENV == "test") {
console.log("Suppresion des scores sans l'environnement test");
scores = scores.filter((score) => score.env_test);
}
await fs.writeFile("./src/data.json", JSON.stringify(scores), "utf8");
const folder = "public/answers";
if (!existsSync(folder)) mkdirSync(folder);
for (const score of data) {
for (const score of scores) {
const uuid_score = score.image;
if (uuid_score) {
console.log(`Score image : ${folder}/${uuid_score}`);
const response = await fetchAsset(uuid_score);
try {
const thumbnail = sharp().resize({ height: 200 }).webp();
await streamPipeline(
response.body,
thumbnail,
createWriteStream(`${folder}/${uuid_score}.webp`),
);
} catch (err) {
console.log(err);
}
}
for (const question of score.questions) {
for (const answer of question.questions_id.answers) {
const uuid = answer.answers_id.image;
if (uuid) {
console.log(`${folder}/${uuid}`);
const response = await fetchAsset(uuid);
try {
const thumbnail = sharp().resize({ height: 200 }).webp();
await streamPipeline(
response.body,
thumbnail,
createWriteStream(`${folder}/${uuid}.webp`)
);
} catch (err) {
console.log(err);
}
await downloadImage(uuid, `${folder}/${uuid}.webp`, { height: 200 });
}
}
}
@ -77,4 +114,233 @@ async function fetchData() {
// await sharp('src/assets/arbre.png').resize({ width: 440, height: 690 }).webp().toFile('public/arbre.webp')
}
async function transformImage(response, path, options, format = "webp") {
try {
let thumbnail;
console.log(path);
if (format == "webp") {
thumbnail = sharp().resize(options).webp();
} else {
thumbnail = sharp().resize(options).toFormat("png");
}
const stream = createWriteStream(path);
await streamPipeline(response.body, thumbnail, stream);
await stream;
} catch (err) {
console.log(err);
}
}
async function downloadImage(uuid, path, options, format = "webp") {
const response = await fetchAsset(uuid);
await transformImage(response, path, options, format);
}
async function fetchHomepageData() {
const fields = ["*"];
const url = `/items/homepage?${fields
.map((item) => `fields[]=${item}`)
.join("&")}`;
const homepage = (await fetchJSONApi(url)).data;
await downloadImage(homepage.image, `public/homepage.webp`, {
width: 1280,
height: 720,
fit: sharp.fit.cover,
});
const favicons = [
{
path: `public/favicon/android-icon-192x192.png`,
options: {
width: 192,
height: 192,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-57x57.png`,
options: {
width: 57,
height: 57,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-60x60.png`,
options: {
width: 60,
height: 60,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-72x72.png`,
options: {
width: 72,
height: 72,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-76x76.png`,
options: {
width: 76,
height: 76,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-114x114.png`,
options: {
width: 114,
height: 114,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-120x120.png`,
options: {
width: 120,
height: 120,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-144x144.png`,
options: {
width: 144,
height: 144,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-152x152.png`,
options: {
width: 152,
height: 152,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/apple-icon-180x180.png`,
options: {
width: 180,
height: 180,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/favicon-16x16.png`,
options: {
width: 16,
height: 16,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/favicon-32x32.png`,
options: {
width: 32,
height: 32,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/favicon-96x96.png`,
options: {
width: 96,
height: 96,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/favicon-256x256.png`,
options: {
width: 256,
height: 256,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/ms-icon-70x70.png`,
options: {
width: 70,
height: 70,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/ms-icon-144x144.png`,
options: {
width: 144,
height: 144,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/ms-icon-150x150.png`,
options: {
width: 150,
height: 150,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
{
path: `public/favicon/ms-icon-310x310.png`,
options: {
width: 310,
height: 310,
fit: sharp.fit.contain,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
format: "png",
},
];
for (const favicon of favicons) {
downloadImage(homepage.logo, favicon.path, favicon.options, favicon.format);
}
}
async function fetchData() {
await fetchHomepageData();
await fetchCertitudesData();
await fetchCertitudesResultsData();
await fetchScoresData();
}
fetchData();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 KiB

View File

@ -6,31 +6,52 @@
--color-black: #000000;
--color-green: rgb(118, 148, 67);
--header-size-small: 64px;
--header-size-small: 48px;
--header-size-big: 128px;
--header-size: var(--header-size-small);
--footer-size: 48px;
--color-red: #c82606;
--color-orange: #df6a0f;
--color-light-green: #70bf41;
--color-dark-green: #00882b;
}
/* semantic color variables for this project */
:root, body.theme-light {
--color-header-background: var(--color-green);
--color-header-text: var(--color-white);
--color-background: var(--color-white);
--color-text: var(--color-black);
--color-highlight: var(--color-green);
--header-size: var(--header-size-small);
--color-highlight-background: var(--color-green);
--color-highlight-text: var(--color-white);
--color-highlight-text-invert: var(--color-black);
}
body.theme-dark {
--color-header-background: var(--color-green);
--color-header-text: var(--color-black);
--color-background: var(--color-black);
--color-text: var(--color-white);
--color-highlight-background: var(--color-green);
--color-highlight-text: var(--color-black);
--color-highlight-text-invert: var(--color-white);
}
@media (prefers-color-scheme: dark) {
:root {
--color-header-background: var(--color-green);
--color-header-text: var(--color-black);
--color-background: var(--color-black);
--color-text: var(--color-white);
--color-highlight-background: var(--color-green);
--color-highlight-text: var(--color-black);
--color-highlight-text-invert: var(--color-white);
}
}
@media (min-width: 1024px) {
@media (min-height: 1000px) {
:root {
--header-size: var(--header-size-big);
}
@ -51,6 +72,10 @@ html, body, #app {
padding: 0;
}
#app {
min-width: 320px;
}
body {
color: var(--color-text);
background: var(--color-background);
@ -62,3 +87,53 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body.print {
--color-highlight-background: var(--color-white);
--color-highlight-text: var(--color-black);
--color-highlight-text-invert: var(--color-white);
--color-header-background: var(--color-white);
--color-header-text: var(--color-black);
}
.print .no-print {
display: none
}
.btn {
width: 3rem;
height: 3rem;
padding: .5rem;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
border-radius: 100%;
position: absolute;
border: none;
box-shadow: 0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);
cursor: pointer;
background: white;
}
.btn svg {
width: 100%;
height: 100%;
}
header svg.color-text [stroke] {
stroke: var(--color-header-text);
}
header svg.color-text [fill]:not([fill=none]) {
fill: var(--color-header-text)
}
strong {
font-weight: bold;
}
.bg-red { background: var(--color-red); }
.bg-orange { background: var(--color-orange); }
.bg-light-green { background: var(--color-light-green); }
.bg-dark-green { background: var(--color-dark-green); }

View File

@ -0,0 +1,191 @@
<script setup>
import { ref, computed, watchEffect } from "vue";
import { useStore } from "@/stores"; // adapte le chemin si besoin
const store = useStore();
const props = defineProps({
certitude: {
type: Object,
required: true,
},
});
const emits = defineEmits(["answerSelected", "nextQuestion"]);
const selectedWeight = ref(null);
// Utilise la langue du store
const language = computed(() => store.language || "fr-FR");
// Recherche la bonne traduction selon la langue courante
const translation = computed(() => {
return (
props.certitude.translations.find(
(t) => t.languages_code === language.value,
) ||
// Fallback en français
props.certitude.translations.find((t) => t.languages_code === "fr-FR") ||
// Fallback générique
props.certitude.translations[0]
);
});
// Regroupe les réponses avec leurs poids
const answers = computed(() => {
return [1, 2, 3].map((i) => ({
id: i,
title: translation.value[`answer${i}`],
weight: props.certitude[`weight${i}`],
}));
});
function selectAnswer(answer) {
selectedWeight.value = answer.weight;
emits("answerSelected", props.certitude, selectedWeight.value);
}
watchEffect(() => {
if (answers.value.length && selectedWeight.value === null) {
selectAnswer(answers.value[0]);
}
});
</script>
<template>
<div class="main">
<div class="center">
<legend>{{ translation.title }}</legend>
<div class="description" v-html="translation.description"></div>
<ul class="choices">
<li class="choice" v-for="answer in answers" :key="answer.id">
<input
type="radio"
:id="`certitude_${certitude.id}_answer_${answer.id}`"
:name="`certitude_${certitude.id}`"
:value="answer.weight"
v-model="selectedWeight"
@change="selectAnswer(answer)"
@click="$emit('nextQuestion')"
/>
<label :for="`certitude_${certitude.id}_answer_${answer.id}`">
<div>{{ answer.title }}</div>
</label>
</li>
</ul>
</div>
</div>
<div class="btns">
<button class="btn next" @click="$emit('nextQuestion')">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 40 40"
width="40"
height="40"
>
<path
d="m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z"
></path>
</svg>
</button>
</div>
</template>
<style scoped lang="sass">
legend
text-align: center
font-size: 1.4rem
line-height: 2rem
font-weight: bold
width: 100%
.description
text-align: center
padding: 1rem
font-size: 1rem
.choices
list-style-type: none
text-align: left
display: inline-block
padding-left: 0
label
cursor: pointer
display: block
input[type=radio]
display: none
& + label > div
position: relative
padding: .2rem .2rem .2rem 2rem
& + label > div::before,
& + label > div::after
display: block
position: absolute
box-sizing: border-box
content:''
border-radius: 1rem
& + label > div::before
top: .5rem
left: 0
background-color: var(--color-green)
width: 1rem
height: 1rem
& + label > div::after
top: calc(3px + .5rem)
left: 3px
width: calc(1rem - 6px)
height: calc(1rem - 6px)
&:checked + label > div
text-shadow: -0.06ex 0 0 currentColor, 0.06ex 0 0 currentColor
&:not(:checked) + label > div::before
background-color: var(--color-green)
&:not(:checked) + label > div::after
background-color: white
.main
height: 100%
max-width: 100%
display: flex
flex-direction: column
justify-content: space-around
align-items: center
.center
width: 100%
text-align: center
.btns
width: 400px
max-width: 100%
min-width: 280px
position: relative
margin: 0 auto
.next
background: var(--color-highlight-background)
bottom: 1rem
right: 1rem
width: 3rem
height: 3rem
opacity: .8
&:hover
opacity: 1
svg
width: 80%
height: 80%
transform: rotate(90deg)
fill: var(--color-highlight-text)
</style>

View File

@ -17,7 +17,7 @@ const answerWeight = ref(props.question.weight);
function selectAnswer(answer) {
const answerIndex = props.question.answers.findIndex(
(a) => a.id === answer.id
(a) => a.id === answer.id,
);
slides.value.splide.go(answerIndex);
}
@ -26,58 +26,282 @@ function slideMove(splide, newIndex) {
answerWeight.value = props.question.answers[newIndex].weight;
emits("answerSelected", props.question, answerWeight);
}
const displayPopup = ref(false);
function showHidePopup() {
displayPopup.value = !displayPopup.value;
}
function validatePopup() {
const calcule = Math.round((h.value * 100) / d.value);
const regexp = /[^0-9]*([0-9]{2})/g;
const answersLength = props.question.answers.length;
const answerIndex = props.question.answers.find((answer, index) => {
const values = Array.from(answer.title.matchAll(regexp), (m) => m[1]);
if (values.length > 0) {
const min = values.length == 1 ? 0 : values[0];
const max = values.length == 1 ? values[0] : values[1];
if (calcule >= min && calcule <= max) {
return true;
}
}
if (answersLength == index + 1) {
return true;
}
});
const answer = document.querySelector(
"input[id=" +
`question_${props.question.id}_answer_${answerIndex.id}` +
"]",
);
answer.checked = true;
answer.dispatchEvent(new Event("change"));
showHidePopup();
}
const h = ref();
const d = ref();
</script>
<template>
<div class="main">
<div class="top">
<div class="center">
<legend>{{ question.title }}</legend>
<div class="images">
<template v-if="question.answers">
<Splide
@splide:move="slideMove"
class="answers"
:id="`question_${question.id}`"
ref="slides"
>
<SplideSlide v-for="answer in question.answers" :key="answer.id">
<img height="200" :src="`/answers/${answer.image}.webp`" />
</SplideSlide>
</Splide>
</template>
</div>
<template v-if="question.answers">
<div class="choice" v-for="answer in question.answers" :key="answer.id">
<label>
<ul class="choices">
<li
class="choice"
v-for="answer in question.answers"
:key="answer.id"
>
<input
type="radio"
:data-answerId="answer.id"
:name="`question_${question.id}`"
:id="`question_${question.id}_answer_${answer.id}`"
:value="answer.weight"
v-model="answerWeight"
@change="selectAnswer(answer)"
@click="nextQuestion"
/>
{{ answer.title }}
</label>
</div>
</template>
</div>
<div class="bottom">
<template v-if="question.answers">
<Splide
@splide:move="slideMove"
class="answers"
:id="`question_${question.id}`"
ref="slides"
>
<SplideSlide
v-for="answer in question.answers"
:key="answer.id"
@click="$emit('nextQuestion')"
>
<img height="200" :src="`/answers/${answer.image}.webp`" />
</SplideSlide>
</Splide>
<label :for="`question_${question.id}_answer_${answer.id}`">
<div>
{{ answer.title }}
</div>
</label>
</li>
</ul>
</template>
</div>
</div>
<div class="btns">
<template v-if="question.function == 'rapport_hd'">
<button class="btn calculette" @click="showHidePopup">
<img src="/abacus.svg" alt="" />
</button>
</template>
<button class="btn next" @click="$emit('nextQuestion')">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 40 40"
width="40"
height="40"
>
<path
d="m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z"
></path>
</svg>
</button>
</div>
<template v-if="displayPopup">
<div class="popup">
<button class="btn close" @click="showHidePopup">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
<path
d="M21.529 18.006l8.238-8.238c.977-.976.977-2.559 0-3.535-.977-.977-2.559-.977-3.535 0l-8.238 8.238-8.238-8.238c-.976-.977-2.56-.977-3.535 0-.977.976-.977 2.559 0 3.535l8.238 8.238-8.258 8.258c-.977.977-.977 2.559 0 3.535.488.488 1.128.732 1.768.732s1.28-.244 1.768-.732l8.258-8.259 8.238 8.238c.488.488 1.128.732 1.768.732s1.279-.244 1.768-.732c.977-.977.977-2.559 0-3.535l-8.24-8.237z"
fill="#000"
/>
</svg>
</button>
<label for="H">H (m)</label>
<input v-model="h" type="number" name="H" />
<label for="D">D (cm)</label>
<input v-model="d" type="number" name="D" />
<p v-if="d && d != 0"> {{ Math.round((h * 100) / d) }}</p>
<p v-if="!d || d == 0" class="no-visible"></p>
<button class="btn equal" @click="validatePopup">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
<path
fill="#31373D"
d="M34.459 1.375c-1.391-.902-3.248-.506-4.149.884L13.5 28.17l-8.198-7.58c-1.217-1.125-3.114-1.051-4.239.166-1.125 1.216-1.051 3.115.166 4.239l10.764 9.952s.309.266.452.359c.504.328 1.07.484 1.63.484.982 0 1.945-.482 2.52-1.368L35.343 5.524c.902-1.39.506-3.248-.884-4.149z"
/>
</svg>
</button>
</div>
</template>
</template>
<style lang="sass">
.btns
width: 400px
max-width: 100%
min-width: 280px
position: relative
margin: 0 auto
.popup
position: absolute
background: var(--color-highlight-background)
color: var(--color-highlight-text)
top: 0
left: 0
right: 0
bottom: 0
z-index: 100
display: flex
flex-direction: column
padding: 1rem
justify-content: center
align-items: center
p
padding: 1rem
margin: 1rem
border: 1px solid var(--color-highlight-text)
font-size: 2rem
&.no-visible
border: 1px solid transparent
svg path
fill: var(--color-highlight-background)
.close
top: 1rem
right: 1rem
.equal
position: relative
input
padding: .4rem
text-align: center
font-size: 1rem
width: 5rem
.calculette
background: var(--color-highlight-background)
bottom: 1rem
left: 1rem
width: 4rem
height: 4rem
img
width: 80%
height: 80%
.next
background: var(--color-highlight-background)
bottom: 1rem
right: 1rem
width: 3rem
height: 3rem
opacity: .8
&:hover
opacity: 1
svg
width: 80%
height: 80%
transform: rotate(90deg)
fill: var(--color-highlight-text)
.splide__arrow
background: transparent
border: 3px solid var(--color-green)
width: 3rem
height: 3rem
opacity: 1
svg
fill: var(--color-green)
height: 1.6rem
width: 1.6rem
.splide__pagination
bottom: -1.5em
display: none
.splide__pagination__page
width: .7rem
height: .7rem
background: var(--color-green)
&.is-active
background: var(--color-green)
</style>
<style lang="sass" scoped>
input[type=radio]
margin-right: .5rem
legend
text-align: center
font-size: 1.4rem
line-height: 2rem
font-weight: bold
width: 100%
.choices
list-style-type: none
text-align: left
display: inline-block
padding-left: 0
label
cursor: pointer
display: block
input[type=radio]
display: none
& + label > div
position: relative
padding: .2rem .2rem .2rem 2rem
& + label > div::before,
& + label > div::after
display: block
position: absolute
box-sizing: border-box
content:''
border-radius: 1rem
& + label > div::before
top: .5rem
left: 0
background-color: var(--color-green)
width: 1rem
height: 1rem
& + label > div::after
top: calc(3px + .5rem)
left: 3px
width: calc(1rem - 6px)
height: calc(1rem - 6px)
&:checked + label > div
text-shadow: -0.06ex 0 0 currentColor, 0.06ex 0 0 currentColor
&:not(:checked) + label > div::before
background-color: var(--color-green)
&:not(:checked) + label > div::after
background-color: white
.main
height: 100%
@ -87,11 +311,22 @@ input[type=radio]
justify-content: space-around
align-items: center
.bottom
.center
width: 100%
text-align: center
.images
margin: 3rem auto
width: 400px
max-width: 100%
min-width: 280px
@media (max-height: 600px)
margin: 1rem auto
h2
font-size: 2rem
.answers
text-align: center
@ -99,6 +334,6 @@ input[type=radio]
.main
flex-direction: row
.bottom
.images
max-width: 50%
</style>

View File

@ -1,14 +1,18 @@
<script setup>
import data from "@/data.json";
import certitudes from "@/certitudes.json";
import certitudesResults from "@/certitudesResults.json";
import { ref, computed } from "vue";
import { ref, computed, watch } from "vue";
import { useStore } from "@/stores";
import { Splide, SplideSlide } from "@splidejs/vue-splide";
import Question from "./Question.vue";
import Certitude from "./Certitude.vue";
import "@splidejs/splide/dist/css/splide.min.css";
import ScoreHeader from "./ScoreHeader.vue";
// import { toPng } from "html-to-image";
const props = defineProps({
id: {
@ -28,7 +32,7 @@ function formatAnswers(answers) {
.map((answer) => answer.answers_id)
.map((answer) => {
const translation = answer.translations.filter(
(item) => item.languages_code == language
(item) => item.languages_code == language,
);
return {
id: answer.id,
@ -44,20 +48,25 @@ function formatScore(score) {
.map((question) => question.questions_id)
.map((question) => {
const translation = question.translations.filter(
(item) => item.languages_code == language
(item) => item.languages_code == language,
);
const answers = formatAnswers(question.answers);
return {
id: question.id,
weight: answers[0].weight,
title: translation.length > 0 ? translation[0].title : question.title,
function: question.function,
answers: answers,
splide: ref(),
};
});
}
const title = score ? score.title : "";
function getTranslation(translations, key) {
return translations.find((translation) => translation[key] == store.language);
}
const trad = getTranslation(score.translations, "languages_id");
const title = score ? trad.title : "";
const link = score ? trad.link : "";
const questions = ref(formatScore(score));
const scoreSum = computed(() => {
@ -66,14 +75,14 @@ const scoreSum = computed(() => {
.reduce((value, currentValue) => value + currentValue, 0);
});
const displayScoreResult = computed(
() => !questions.value.filter((q) => q.weight == null).length
() => !questions.value.filter((q) => q.weight == null).length,
);
function getResultsFromScore(score) {
return score.results
.map((result) => result.results_id)
.map((result) => {
const translation = result.translations.filter(
(item) => item.languages_code == language
(item) => item.languages_code == language,
);
return {
id: result.id,
@ -83,8 +92,10 @@ function getResultsFromScore(score) {
effets: translation.length > 0 ? translation[0].effets : "",
facteur: translation.length > 0 ? translation[0].facteur : "",
pde: translation.length > 0 ? translation[0].pde : "",
title: translation.length > 0 ? translation[0].title : "",
};
});
})
.reverse();
}
const results = ref(getResultsFromScore(score));
const result = computed(() =>
@ -92,12 +103,12 @@ const result = computed(() =>
? results.value
.filter((r) => !r.min || r.min <= scoreSum.value)
.filter((r) => !r.max || r.max >= scoreSum.value)[0]
: null
: null,
);
function goQuestionSlide(question) {
console.log(slides.value);
slides.value.go(
questions.value.findIndex((element) => element.id === question.id)
questions.value.findIndex((element) => element.id === question.id),
);
}
function answerSelected(question, answerWeight) {
@ -105,12 +116,88 @@ function answerSelected(question, answerWeight) {
}
function nextQuestion() {
setTimeout(() => slides.value.go(">"), 100);
setTimeout(() => {
slides.value.go(">");
// console.log(slides);
}, 100);
}
// const saveAs = (blob, fileName) => {
// var elem = window.document.createElement("a");
// elem.href = blob;
// elem.download = fileName;
// elem.style = "display:none;";
// (document.body || document.documentElement).appendChild(elem);
// if (typeof elem.click === "function") {
// elem.click();
// } else {
// elem.target = "_blank";
// elem.dispatchEvent(
// new MouseEvent("click", {
// view: window,
// bubbles: true,
// cancelable: true,
// }),
// );
// }
// URL.revokeObjectURL(elem.href);
// elem.remove();
// };
// const sharing = ref(false);
// async function share() {
// sharing.value = true;
// const filter = (node) => {
// const exclusionClasses = ["btn"];
// return !exclusionClasses.some((classname) =>
// node.classList?.contains(classname),
// );
// };
// const body = document.querySelector("body");
// body.classList.add("print");
// const dataUrl = await toPng(body, { filter: filter });
// body.classList.remove("print");
// const fileName = new Date()
// .toISOString()
// .replace(/T/, "_")
// .replace(/\..+/, "")
// .replaceAll(":", "-");
// saveAs(dataUrl, `Ceiba-score-${fileName}.png`);
// sharing.value = false;
// }
const displayCertitude = ref(false);
const weightCertitudes = ref(new Array(certitudes.length).fill(0));
const weightAllCertitudes = ref(0);
const certitudeResult = ref(selectCertitudeResult());
function selectCertitudeResult() {
const certitude = certitudesResults.find(
(result) =>
result.weight_min <= weightAllCertitudes.value &&
result.weight_max >= weightAllCertitudes.value,
);
const certitudeTrad = certitude?.translations.find(
(translation) => translation.languages_code == language,
);
return { ...certitudeTrad, color: certitude.color };
}
function displayCertitudeQuestions() {
displayCertitude.value = !displayCertitude.value;
}
function answerSelectedCertitude(question, answerWeight) {
weightCertitudes.value[question.sort - 1] = answerWeight;
weightAllCertitudes.value = weightCertitudes.value.reduce(
(accumulator, curr) => accumulator + curr,
0,
);
}
watch(weightAllCertitudes, () => {
certitudeResult.value = selectCertitudeResult();
});
</script>
<template>
<ScoreHeader v-if="title" :title="title" />
<ScoreHeader v-if="title" :title="title" :link="link" />
<Splide
ref="slides"
class="questions"
@ -132,23 +219,76 @@ function nextQuestion() {
@nextQuestion="nextQuestion"
/>
</SplideSlide>
<template v-if="displayCertitude">
<SplideSlide v-for="certitude in certitudes" :key="certitude.id">
<Certitude
:certitude="certitude"
@answerSelected="answerSelectedCertitude"
@nextQuestion="nextQuestion"
/>
</SplideSlide>
</template>
<SplideSlide class="latest">
<template v-if="displayScoreResult && result">
<ul>
<li>score : {{ scoreSum }}</li>
<li>pde : {{ result.pde }}</li>
<li>pde_qtra : {{ result.pde_qtra }}</li>
<li>effets : {{ result.effets }}</li>
<li>facteur : {{ result.facteur }}</li>
</ul>
<div class="gradient">
<div
v-for="(item, index) in [...Array(7).keys()]"
:class="{ active: result && result.pde_qtra === index + 1 }"
:key="item"
>
{{ index + 1 }}
<div>
<h2 class="center">{{ trad.result_title }}</h2>
<h2 class="center">{{ result.title }}</h2>
<div :class="'gradient size-' + results.length">
<div
v-for="item in results"
:class="{ active: result && result.pde_qtra === item.pde_qtra }"
:key="item"
:data-title="trad.result_sigle"
>
{{ item.title }}
</div>
</div>
<div class="details">
<p>{{ trad.result_params }} :</p>
<br />
<ul>
<li v-for="question in questions" :key="question.id">
{{ question.title }} :
{{
question.answers.find(
(answer) => answer.weight === question.weight,
).title
}}
</li>
</ul>
</div>
<div class="certitude_result">
Niveau de certitude
<button
@click="displayCertitudeQuestions"
:class="'bg-' + certitudeResult.color"
>
<span>{{ certitudeResult?.niveau }} </span>
</button>
</div>
<!--button class="btn download" @click="() => share()" v-if="!sharing">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
<path
fill="#D99E82"
d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4v-9c0-2.209.791-3 3-3h30c2.209 0 3 .791 3 3v9z"
/>
<path
fill="#662113"
d="M25 20c0 3.866-3.134 7-7 7s-7-3.134-7-7h14z"
/>
<path
fill="#C1694F"
d="M4 36h28c2.209 0 4-1.791 4-4H0c0 2.209 1.791 4 4 4z"
/>
<path
fill="#77B255"
d="M26.716 8h-4.783V2c0-1.105-.896-2-2-2h-4.001c-1.104 0-1.999.896-1.999 2v6H9.148c-1.223 0-1.516.623-.651 1.489l7.863 7.863c.865.865 2.28.865 3.146 0l7.863-7.863C28.232 8.623 27.94 8 26.716 8z"
/>
</svg>
</button-->
<!-- <button class="btn spin" v-if="sharing">
<img src="/spin.svg" />
</button> -->
</div>
</template>
<template v-else>
@ -181,6 +321,61 @@ function nextQuestion() {
</template>
<style lang="sass" scoped>
.certitude_result
padding-top: 1rem
font-size: 1.1rem
text-align: center
color: var(--color-highlight-text)
& *
font-weight: 800
button
display: flex
width: 100%
color: var(--color-highlight-text)
justify-content: center
flex-direction: row
align-items: center
margin: .2rem auto 0
border-radius: 0
border: none
font-weight: 900 !important
font-size: 1.1rem
padding: .3rem
.spin
bottom: 1.5rem
right: 1.5rem
img
position: absolute
top: 50%
left: 50%
width: 30px
height: 30px
margin: -15px 0 0 -15px
-webkit-animation: spin 2s linear infinite
-moz-animation: spin 2s linear infinite
animation: spin 2s linear infinite
@-moz-keyframes spin
100%
-moz-transform: rotate(360deg)
@-webkit-keyframes spin
100%
-webkit-transform: rotate(360deg)
@keyframes spin
100%
-webkit-transform: rotate(360deg)
transform: rotate(360deg)
.center
text-align: center
.download
bottom: 1.5rem
right: 1.5rem
opacity: .7
.noscore
display: flex
justify-content: center
@ -205,7 +400,6 @@ function nextQuestion() {
.splide__slide
background: transparent
position: relative
padding: 1rem
@ -222,42 +416,96 @@ label
padding: .3rem
.latest
background-color: var(--color-green)
color: var(--color-black)
background-color: var(--color-highlight-background)
color: var(--color-highlight-text)
display: flex
align-items: center
justify-content: center
h2
font-size: 1.5rem
font-weight: bold
line-height: 2.4rem
& + h2
line-height: 3rem
font-size: 2.6rem
@media (max-height: 600px)
font-size: 1.5rem
& + h2
font-size: 2rem
ul
text-align: left
padding-left: 1.5rem
list-style-type: disc
.details
text-align: left !important
font-size: 1.1rem
line-height: normal
background: var(--color-highlight-background)
border: 1px solid var(--color-highlight-text)
padding: 1rem
background: rgba(0,0,0,0.2)
li
line-height: 1.5rem
.gradient
width: calc(100% - 2rem)
padding: 0 1rem
height: 3rem
height: 2.5rem
background-image: linear-gradient(to right, red, red, rgb(255, 255, 0), rgb(255, 255, 0), green, green)
display: flex
margin: 2.5rem 0
margin: 1.5rem auto
align-items: center
border-radius: 3px
position: absolute
bottom: 0
max-width: 30rem
text-shadow: 1px 1px 4px var(--color-highlight-text-invert),-1px -1px 4px var(--color-highlight-text-invert), -1px 1px 4px var(--color-highlight-text-invert), 1px -1px 4px var(--color-highlight-text-invert)
color: var(--color-highlight-text)
&.size-3 div
width: calc(100%/3)
&.size-4 div
width: calc(100%/4)
&.size-5 div
width: calc(100%/5)
&.size-6 div
width: calc(100%/6)
&.size-7 div
width: calc(100%/7)
div
width: calc(100%/7)
text-align: center
align-self: center
font-weight: bold
.active
border: 1px solid black
border-radius: 3px
height: 200%
font-weight: bold
display: flex
align-items: center
justify-content: center
position: relative
&::before
content: "PdE\AQTRA"
&:before
content: ""
border: 1px solid var(--color-highlight-text)
border-radius: 3px
top: -1rem
bottom: -1rem
position: absolute
font-size: .5rem
top: 0
width: 100%
font-weight: bold
display: flex
align-items: center
justify-content: center
&::after
content: ""
position: absolute
font-size: 1rem
top: -2rem
bottom: -1.5rem
left: 0
right: 0
line-height: .7rem
text-shadow: none
.splide__arrows
position: inherit

View File

@ -4,62 +4,151 @@ defineProps({
type: String,
required: true,
},
link: {
type: String,
},
});
</script>
<template>
<header>
<router-link :to="{ name: 'home' }">
<p></p>
<svg
class="color-text"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 22L2 22"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M2 11L10.1259 4.49931C11.2216 3.62279 12.7784 3.62279 13.8741 4.49931L22 11"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M15.5 5.5V3.5C15.5 3.22386 15.7239 3 16 3H18.5C18.7761 3 19 3.22386 19 3.5V8.5"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M4 22V9.5"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M20 22V9.5"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M15 22V17C15 15.5858 15 14.8787 14.5607 14.4393C14.1213 14 13.4142 14 12 14C10.5858 14 9.87868 14 9.43934 14.4393C9 14.8787 9 15.5858 9 17V22"
stroke="white"
stroke-width="1.5"
/>
<path
d="M14 9.5C14 10.6046 13.1046 11.5 12 11.5C10.8954 11.5 10 10.6046 10 9.5C10 8.39543 10.8954 7.5 12 7.5C13.1046 7.5 14 8.39543 14 9.5Z"
stroke="white"
stroke-width="1.5"
/>
</svg>
</router-link>
<h1>{{ title }}</h1>
<a v-if="link" :href="link">
<svg
class="color-text"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17V11"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
/>
<circle
cx="1"
cy="1"
r="1"
transform="matrix(1 0 0 -1 11 9)"
fill="white"
/>
<path
stroke="white"
stroke-width="1.5"
d="M2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C22 4.92893 22 7.28595 22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12Z"
/>
</svg>
</a>
</header>
</template>
<style scoped>
header {
height: var(--header-size);
position: fixed;
background: var(--color-green);
width: 100%;
z-index: 1;
top: 0;
border-bottom: 1px solid var(--color-text);
display: flex;
align-items: center;
align-content: center;
}
<style lang="sass" scoped>
header
height: var(--header-size)
position: fixed
background: var(--color-header-background)
color: var(--color-header-text)
width: 100%
z-index: 1
top: 0
border-bottom: 1px solid var(--color-header-text)
display: flex
align-items: center
align-content: center
a {
height: 100%;
width: var(--header-size);
min-width: var(--header-size);
margin: 0;
display: flex;
align-items: center;
text-align: center;
align-content: center;
border-right: 1px solid var(--color-text);
text-decoration: none;
}
a:first-child
border-right: 1px solid var(--color-header-text)
a:last-child
border-left: 1px solid var(--color-header-text)
p {
margin: 0 auto;
font-size: 2rem;
color: var(--color-text);
}
a:hover svg, a:focus svg, a:active svg
transform: scale(1.1)
h1 {
margin: 0 1rem;
line-height: 0.9;
flex: 1;
font-size: 1.2rem;
font-weight: bold;
}
a
height: 100%
width: var(--header-size)
min-width: var(--header-size)
margin: 0
display: flex
align-items: center
text-align: center
align-content: center
text-decoration: none
@media (min-width: 1024px) {
p {
font-size: 4rem;
}
}
p
margin: 0 auto
font-size: 2rem
color: var(--color-header-text)
h1
margin: 0 1rem
line-height: 0.9
flex: 1
font-size: 1.2rem
font-weight: bold
text-align: center
svg
width: 2rem
margin: 0 auto
@media (min-height: 1000px)
p
font-size: 4rem
h1
font-size: 2rem
svg
width: 4rem
</style>

View File

@ -8,7 +8,7 @@ const translationKey = "languages_id";
const scores = data.filter((score) => {
return (
!!score.translations.find(
(translation) => translation[translationKey] == store.language
(translation) => translation[translationKey] == store.language,
) &&
score.results.length &&
score.questions.length
@ -27,51 +27,257 @@ function getTranslation(translations, key) {
@click="store.switchTheme"
title="Thème de votre système"
>
🌓
<svg
class="color-text"
version="1.1"
id="Icons"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 32 32"
xml:space="preserve"
>
<line
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
x1="16"
y1="3"
x2="16"
y2="29"
/>
<path
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
d="M16,23c-3.87,0-7-3.13-7-7s3.13-7,7-7"
/>
<line
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
x1="6.81"
y1="6.81"
x2="8.93"
y2="8.93"
/>
<line
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
x1="3"
y1="16"
x2="6"
y2="16"
/>
<line
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
x1="6.81"
y1="25.19"
x2="8.93"
y2="23.07"
/>
<path
fill="none"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
d="M16,12.55C17.2,10.43,19.48,9,22.09,9c0.16,0,0.31,0.01,0.47,0.02c-1.67,0.88-2.8,2.63-2.8,4.64
c0,2.9,2.35,5.25,5.25,5.25c1.6,0,3.03-0.72,3.99-1.85C28.48,20.43,25.59,23,22.09,23c-2.61,0-4.89-1.43-6.09-3.55"
/>
</svg>
</li>
<li
v-if="store.theme == 'dark'"
@click="store.switchTheme"
title="Thème sombre"
>
🌑
<svg
class="color-text"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="white"
d="M21.0672 11.8568L20.4253 11.469L21.0672 11.8568ZM12.1432 2.93276L11.7553 2.29085V2.29085L12.1432 2.93276ZM21.25 12C21.25 17.1086 17.1086 21.25 12 21.25V22.75C17.9371 22.75 22.75 17.9371 22.75 12H21.25ZM12 21.25C6.89137 21.25 2.75 17.1086 2.75 12H1.25C1.25 17.9371 6.06294 22.75 12 22.75V21.25ZM2.75 12C2.75 6.89137 6.89137 2.75 12 2.75V1.25C6.06294 1.25 1.25 6.06294 1.25 12H2.75ZM15.5 14.25C12.3244 14.25 9.75 11.6756 9.75 8.5H8.25C8.25 12.5041 11.4959 15.75 15.5 15.75V14.25ZM20.4253 11.469C19.4172 13.1373 17.5882 14.25 15.5 14.25V15.75C18.1349 15.75 20.4407 14.3439 21.7092 12.2447L20.4253 11.469ZM9.75 8.5C9.75 6.41182 10.8627 4.5828 12.531 3.57467L11.7553 2.29085C9.65609 3.5593 8.25 5.86509 8.25 8.5H9.75ZM12 2.75C11.9115 2.75 11.8077 2.71008 11.7324 2.63168C11.6686 2.56527 11.6538 2.50244 11.6503 2.47703C11.6461 2.44587 11.6482 2.35557 11.7553 2.29085L12.531 3.57467C13.0342 3.27065 13.196 2.71398 13.1368 2.27627C13.0754 1.82126 12.7166 1.25 12 1.25V2.75ZM21.7092 12.2447C21.6444 12.3518 21.5541 12.3539 21.523 12.3497C21.4976 12.3462 21.4347 12.3314 21.3683 12.2676C21.2899 12.1923 21.25 12.0885 21.25 12H22.75C22.75 11.2834 22.1787 10.9246 21.7237 10.8632C21.286 10.804 20.7293 10.9658 20.4253 11.469L21.7092 12.2447Z"
/>
</svg>
</li>
<li
v-if="store.theme == 'light'"
@click="store.switchTheme"
title="Thème clair"
>
🌞
<svg
class="color-text"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="12" r="5" stroke="#1C274C" stroke-width="1.5" />
<path
d="M12 2V4"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M12 20V22"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M4 12L2 12"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M22 12L20 12"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M19.7778 4.22266L17.5558 6.25424"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M4.22217 4.22266L6.44418 6.25424"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M6.44434 17.5557L4.22211 19.7779"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M19.7778 19.7773L17.5558 17.5551"
stroke="#1C274C"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg>
</li>
</ul>
<h1>Scores Ceiba</h1>
<ul>
<li v-if="store.language == 'fr-FR'" @click="store.switchLanguage">🇫🇷</li>
<li v-if="store.language == 'en-US'" @click="store.switchLanguage">🇺🇸</li>
<li v-if="store.language == 'fr-FR'" @click="store.switchLanguage">
<svg
xmlns="http://www.w3.org/2000/svg"
id="flag-icons-fr"
viewBox="0 0 640 480"
>
<path fill="#fff" d="M0 0h640v480H0z" />
<path fill="#000091" d="M0 0h213.3v480H0z" />
<path fill="#e1000f" d="M426.7 0H640v480H426.7z" />
</svg>
</li>
<li v-if="store.language == 'en-US'" @click="store.switchLanguage">
<svg
xmlns="http://www.w3.org/2000/svg"
id="flag-icons-gb"
viewBox="0 0 640 480"
>
<path fill="#012169" d="M0 0h640v480H0z" />
<path
fill="#FFF"
d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"
/>
<path
fill="#C8102E"
d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"
/>
<path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z" />
<path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z" />
</svg>
</li>
</ul>
</header>
<main>
<img src="/homepage.webp" class="homeImage" />
<div class="container" v-if="scores">
<ul>
<li v-for="score in scores" :key="score.id" :id="`score_${score.id}`">
<router-link :to="{ name: 'score', params: { id: score.id } }">{{
getTranslation(score.translations, "languages_id").title
}}</router-link>
</li>
<template v-for="(score, index) in scores" :key="score.id">
<li class="white" v-if="index % 2 === 0">
<router-link :to="{ name: 'score', params: { id: score.id } }">
<img height="200" :src="`/answers/${score.image}.webp`" />
</router-link>
</li>
<li>
<router-link
:to="{ name: 'score', params: { id: score.id } }"
class="link"
>{{
getTranslation(score.translations, "languages_id").title
}}</router-link
>
</li>
<li class="white" v-if="index % 2 !== 0">
<router-link :to="{ name: 'score', params: { id: score.id } }">
<img height="200" :src="`/answers/${score.image}.webp`" />
</router-link>
</li>
</template>
</ul>
</div>
</main>
<footer>
<p v-if="store.language == 'fr-FR'">
Scores & Appli par <a href="https://ceiba-conseil.com">Ceiba</a>
</p>
<p v-if="store.language == 'en-US'">
Scores & App by <a href="https://ceiba-conseil.com">Ceiba</a>
</p>
</footer>
</template>
<style lang="sass" scoped>
footer
background: var(--color-header-background)
text-align: center
padding: 1rem
color: var(--color-header-text)
height: var(--footer-size)
display: flex
justify-content: space-around
align-items: center
a
color: var(--color-header-text)
header
height: var(--header-size)
overflow: hidden
display: flex
justify-content: space-around
align-items: center
border-bottom: 2px solid var(--color-text)
background: var(--color-green)
border-bottom: 2px solid var(--color-header-text)
color: var(--color-header-text)
background: var(--color-header-background)
h1
text-align: center
@ -83,6 +289,8 @@ header
list-style: none
text-align: center
font-size: 1.5rem
width: 1.5rem
height: 2rem
a
text-decoration: none
@ -90,71 +298,62 @@ header
.hidden
display: none
svg
height: 32px
width: 32px
main
width: 100%
height: calc(100% - var(--header-size))
padding: 1rem
min-height: calc(100% - var(--header-size) - var(--footer-size))
background-color: var(--color-background)
overflow-x: hidden
overflow-y: auto
text-align: center
display: flex
flex-direction: column
justify-content: space-evenly
.homeImage
height: 30vh
width: 100%
max-width: 600px
object-fit: contain
margin: 1rem auto 0
.container
display: flex
justify-content: center
align-items: center
overflow: hidden
background-color: var(--color-highlight)
.container
position: relative
width: 100%
max-width: 440px
max-height: 100%
padding-top: calc(100% / (724 / 1136))
@media only screen and (min-width: 472px)
padding-top: 0
width: 440px
height: calc(440px * 1136 / 724)
padding: 1rem
ul
margin: 0
padding: 0
list-style: none
position: absolute
top: 0
left: 0
bottom: 0
right: 0
background-image: url(/arbre.webp)
background-position: center
background-repeat: no-repeat
background-size: contain
border-radius: 10px
display: flex
flex-wrap: wrap
justify-content: center
max-width: 404px
border: 1px solid var(--color-green)
li
display: none
height: 202px
width: 202px
max-width: 50%
border: 1px solid var(--color-green)
background: rgba(0, 0, 0, .5)
border-radius: 10px
position: absolute
border: 2px solid var(--color-text)
img
max-width: 100%
object-fit: contain
&#score_1
display: flex
top: 40%
bottom: 35%
left: 20%
right: 20%
.white
background: white
&#score_6
display: flex
top: 72%
bottom: 5%
left: 6%
right: 6%
a
color: var(--color-text)
a.link
font-size: 2rem
line-height: 2rem
color: var(--color-green)
display: block
width: 100%
height: 100%
@ -162,4 +361,6 @@ main
align-items: center
justify-content: center
text-align: center
padding: 1rem
text-decoration: none
</style>

View File

@ -1,10 +1,16 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import piniaPersist from "pinia-plugin-persist";
import { registerSW } from "virtual:pwa-register";
import App from "@/App.vue";
import router from "@/router";
if ("serviceWorker" in navigator) {
// && !/localhost/.test(window.location)) {
registerSW();
}
const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPersist);

View File

@ -1,11 +1,55 @@
import { fileURLToPath, URL } from "url";
import { VitePWA } from "vite-plugin-pwa";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
// publicDir: 'public',
// includeAssets: ['public/answers/*.web'],
plugins: [
vue(),
VitePWA({
name: "My First Progressive Web app",
short_name: "First PWA",
theme_color: "#eb5252",
background_color: "#000000",
registerType: "autoUpdate",
injectRegister: "auto",
// add this to cache all the imports
workbox: {
globPatterns: ["**/*"],
},
// add this to cache all the
// static assets in the public folder
includeAssets: ["**/*"],
useCredentials: true,
manifest: {
theme_color: "#eb5252",
orientation: "portrait",
display: "fullscreen",
scope: "/",
icons: [
{
src: "/images/pwa-icon-256.png",
sizes: "192x192",
type: "image/png",
purpose: "any maskable",
},
{
src: "/images/pwa-icon-512.png",
sizes: "512x512",
type: "image/png",
purpose: "any maskable",
},
],
},
devOptions: {
enabled: true,
},
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),