feat: Ajout de la certitude d'un score
This commit is contained in:
parent
6e42e8c800
commit
e5665a24e7
|
@ -28,7 +28,7 @@ coverage
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
# app
|
# app
|
||||||
src/data.json
|
src/*.json
|
||||||
public/answers
|
public/answers
|
||||||
public/homepage.webp
|
public/homepage.webp
|
||||||
public/logo.png
|
public/logo.png
|
||||||
|
|
|
@ -30,6 +30,32 @@ async function fetchAsset(uuid) {
|
||||||
return fetch(url);
|
return fetch(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
async function fetchScoresData() {
|
||||||
const fields = [
|
const fields = [
|
||||||
"*",
|
"*",
|
||||||
|
@ -312,6 +338,8 @@ async function fetchHomepageData() {
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
await fetchHomepageData();
|
await fetchHomepageData();
|
||||||
|
await fetchCertitudesData();
|
||||||
|
await fetchCertitudesResultsData();
|
||||||
await fetchScoresData();
|
await fetchScoresData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,3 +123,7 @@ header svg.color-text [stroke] {
|
||||||
header svg.color-text [fill]:not([fill=none]) {
|
header svg.color-text [fill]:not([fill=none]) {
|
||||||
fill: var(--color-header-text)
|
fill: var(--color-header-text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
<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
|
||||||
|
|
||||||
|
&:checked + label > div::before
|
||||||
|
background-color: var(--color-green)
|
||||||
|
|
||||||
|
&: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>
|
|
@ -298,9 +298,9 @@ legend
|
||||||
height: calc(1rem - 6px)
|
height: calc(1rem - 6px)
|
||||||
&:checked + label > div
|
&:checked + label > div
|
||||||
text-shadow: -0.06ex 0 0 currentColor, 0.06ex 0 0 currentColor
|
text-shadow: -0.06ex 0 0 currentColor, 0.06ex 0 0 currentColor
|
||||||
&:not(:checked) + label > div::before
|
&:checked + label > div::before
|
||||||
background-color: var(--color-green)
|
background-color: var(--color-green)
|
||||||
&:not(:checked) + label > div::after
|
&:checked + label > div::after
|
||||||
background-color: white
|
background-color: white
|
||||||
|
|
||||||
.main
|
.main
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import data from "@/data.json";
|
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 { useStore } from "@/stores";
|
||||||
|
|
||||||
import { Splide, SplideSlide } from "@splidejs/vue-splide";
|
import { Splide, SplideSlide } from "@splidejs/vue-splide";
|
||||||
import Question from "./Question.vue";
|
import Question from "./Question.vue";
|
||||||
|
import Certitude from "./Certitude.vue";
|
||||||
import "@splidejs/splide/dist/css/splide.min.css";
|
import "@splidejs/splide/dist/css/splide.min.css";
|
||||||
import ScoreHeader from "./ScoreHeader.vue";
|
import ScoreHeader from "./ScoreHeader.vue";
|
||||||
import { toPng } from "html-to-image";
|
import { toPng } from "html-to-image";
|
||||||
|
@ -115,7 +118,7 @@ function answerSelected(question, answerWeight) {
|
||||||
function nextQuestion() {
|
function nextQuestion() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
slides.value.go(">");
|
slides.value.go(">");
|
||||||
console.log(slides);
|
// console.log(slides);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,27 +144,56 @@ const saveAs = (blob, fileName) => {
|
||||||
elem.remove();
|
elem.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
const sharing = ref(false);
|
// const sharing = ref(false);
|
||||||
async function share() {
|
// async function share() {
|
||||||
sharing.value = true;
|
// sharing.value = true;
|
||||||
const filter = (node) => {
|
// const filter = (node) => {
|
||||||
const exclusionClasses = ["btn"];
|
// const exclusionClasses = ["btn"];
|
||||||
return !exclusionClasses.some((classname) =>
|
// return !exclusionClasses.some((classname) =>
|
||||||
node.classList?.contains(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() {
|
||||||
|
return certitudesResults
|
||||||
|
.find(
|
||||||
|
(result) =>
|
||||||
|
result.weight_min <= weightAllCertitudes.value &&
|
||||||
|
result.weight_max >= weightAllCertitudes.value,
|
||||||
|
)
|
||||||
|
?.translations.find(
|
||||||
|
(translation) => translation.languages_code == language,
|
||||||
);
|
);
|
||||||
};
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -187,6 +219,15 @@ async function share() {
|
||||||
@nextQuestion="nextQuestion"
|
@nextQuestion="nextQuestion"
|
||||||
/>
|
/>
|
||||||
</SplideSlide>
|
</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">
|
<SplideSlide class="latest">
|
||||||
<template v-if="displayScoreResult && result">
|
<template v-if="displayScoreResult && result">
|
||||||
<div>
|
<div>
|
||||||
|
@ -216,6 +257,20 @@ async function share() {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="displayCertitude" class="certitude_result">
|
||||||
|
<button @click="displayCertitudeQuestions">
|
||||||
|
<span>Niveau de certitude :
|
||||||
|
{{ certitudeResult?.niveau }}<br />
|
||||||
|
<span v-html="certitudeResult?.description"></span>
|
||||||
|
</span>
|
||||||
|
<span class="cross">✖️</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="certitude_result">
|
||||||
|
<button @click="displayCertitudeQuestions">
|
||||||
|
➕ Ajout un niveau de certitude
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<!--button class="btn download" @click="() => share()" v-if="!sharing">
|
<!--button class="btn download" @click="() => share()" v-if="!sharing">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||||
<path
|
<path
|
||||||
|
@ -236,9 +291,9 @@ async function share() {
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button-->
|
</button-->
|
||||||
<button class="btn spin" v-if="sharing">
|
<!-- <button class="btn spin" v-if="sharing">
|
||||||
<img src="/spin.svg" />
|
<img src="/spin.svg" />
|
||||||
</button>
|
</button> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
@ -271,6 +326,17 @@ async function share() {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass" scoped>
|
<style lang="sass" scoped>
|
||||||
|
.certitude_result
|
||||||
|
text-align: center
|
||||||
|
button
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
flex-direction: row
|
||||||
|
align-items: center
|
||||||
|
margin: 1rem auto 0
|
||||||
|
padding: .3rem
|
||||||
|
.cross
|
||||||
|
margin-left: .5rem
|
||||||
.spin
|
.spin
|
||||||
bottom: 1.5rem
|
bottom: 1.5rem
|
||||||
right: 1.5rem
|
right: 1.5rem
|
||||||
|
|
Loading…
Reference in New Issue