feat: Ajout de la certitude d'un score
This commit is contained in:
parent
6e42e8c800
commit
e5665a24e7
|
@ -28,7 +28,7 @@ coverage
|
|||
*.sw?
|
||||
|
||||
# app
|
||||
src/data.json
|
||||
src/*.json
|
||||
public/answers
|
||||
public/homepage.webp
|
||||
public/logo.png
|
||||
|
|
|
@ -30,6 +30,32 @@ async function fetchAsset(uuid) {
|
|||
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() {
|
||||
const fields = [
|
||||
"*",
|
||||
|
@ -312,6 +338,8 @@ async function fetchHomepageData() {
|
|||
|
||||
async function fetchData() {
|
||||
await fetchHomepageData();
|
||||
await fetchCertitudesData();
|
||||
await fetchCertitudesResultsData();
|
||||
await fetchScoresData();
|
||||
}
|
||||
|
||||
|
|
|
@ -123,3 +123,7 @@ header svg.color-text [stroke] {
|
|||
header svg.color-text [fill]:not([fill=none]) {
|
||||
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)
|
||||
&:checked + label > div
|
||||
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)
|
||||
&:not(:checked) + label > div::after
|
||||
&:checked + label > div::after
|
||||
background-color: white
|
||||
|
||||
.main
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<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";
|
||||
|
@ -115,7 +118,7 @@ function answerSelected(question, answerWeight) {
|
|||
function nextQuestion() {
|
||||
setTimeout(() => {
|
||||
slides.value.go(">");
|
||||
console.log(slides);
|
||||
// console.log(slides);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
@ -141,27 +144,56 @@ const saveAs = (blob, fileName) => {
|
|||
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 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() {
|
||||
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>
|
||||
|
||||
<template>
|
||||
|
@ -187,6 +219,15 @@ async function share() {
|
|||
@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">
|
||||
<div>
|
||||
|
@ -216,6 +257,20 @@ async function share() {
|
|||
</li>
|
||||
</ul>
|
||||
</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">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path
|
||||
|
@ -236,9 +291,9 @@ async function share() {
|
|||
/>
|
||||
</svg>
|
||||
</button-->
|
||||
<button class="btn spin" v-if="sharing">
|
||||
<!-- <button class="btn spin" v-if="sharing">
|
||||
<img src="/spin.svg" />
|
||||
</button>
|
||||
</button> -->
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -271,6 +326,17 @@ async function share() {
|
|||
</template>
|
||||
|
||||
<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
|
||||
bottom: 1.5rem
|
||||
right: 1.5rem
|
||||
|
|
Loading…
Reference in New Issue