Compare commits

..

2 Commits

54 changed files with 1786 additions and 997 deletions

View File

@ -1,13 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Brain Blast</title>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Blast</title>
</head> </head>
<body> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,8 +1,5 @@
{ {
"compilerOptions": { "compilerOptions": { "paths": { "@/*": ["./src/*"] }
"paths": {
"@/*": ["./src/*"]
}
}, },
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }

1257
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",
"express": "^5.0.0",
"mqtt": "^5.3.5", "mqtt": "^5.3.5",
"ping": "^0.4.4", "ping": "^0.4.4",
"roboto-fontface": "^0.10.0", "roboto-fontface": "^0.10.0",

View File

@ -1,11 +1,11 @@
<template> <template>
<v-app> <v-app>
<BrainBlastBar /> <BrainBlastBar />
<GameStatus v-if="$route.name === 'Game Control (Présentateur)'"></GameStatus> <GameStatus v-if="$route.name === 'Game Control (Présentateur)'">
<v-main> </GameStatus>
<RouterView /> <v-main>
</v-main> <RouterView />
<!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> --> </v-main> <!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
</v-app> </v-app>
</template> </template>
@ -17,11 +17,11 @@ import GameStatus from '@/components/GameStatus.vue'
</script> </script>
<style scoped> <style scoped>
.footer { .footer {
position: fixed; /* Fixe le footer en bas de la page */ position: fixed; /* Fixe le footer en bas de la page */
bottom: 0; /* Aligne le footer en bas de la page */ bottom: 0; /* Aligne le footer en bas de la page */
left: 0; /* Aligne le footer à gauche */ left: 0; /* Aligne le footer à gauche */
width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */ width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */
z-index: 1000; /* Assure que le footer est au-dessus des autres éléments */ z-index: 1000; /* Assure que le footer est au-dessus des autres éléments */
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<v-app-bar :elevation="5" height="50"> <v-app-bar :elevation="5" height="50">
<RouterMenu /> <RouterMenu />
<v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title> <v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title>
</v-app-bar> </v-app-bar>
</template> </template>

View File

@ -1,33 +1,18 @@
<template> <template>
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary" @click="toggleCardSize"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon>
Contrôle du jeu Contrôle du jeu
</v-card-title> </v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="5" class="mt-4"> <v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous"> <mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous">
<v-icon left size="60">mdi-skip-previous</v-icon> <v-icon left size="60">mdi-skip-previous</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="5" class="mt-4"> <v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="next"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="next"> <v-icon left size="60">mdi-skip-next</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="pause"> <v-icon left size="60">mdi-pause</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="play"> <v-icon left size="60">mdi-play</v-icon> </mqtt-button> </v-col> </v-row> </v-container>
<v-icon left size="60">mdi-skip-next</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="pause">
<v-icon left size="60">mdi-pause</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="play">
<v-icon left size="60">mdi-play</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card> </v-card>
</template> </template>
@ -39,16 +24,11 @@
const isCardReduced = ref(false); const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte // Méthode pour basculer l'état de la carte
function toggleCardSize() { function toggleCardSize() { isCardReduced.value = !isCardReduced.value;
isCardReduced.value = !isCardReduced.value;
} }
</script> </script>
<style> <style>
.card--reduced { .card--reduced { height: 56px; /* Réglez la hauteur réduite selon vos besoins */ width: 170px; overflow: hidden; transition: height 0.3s ease-in-out;
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
width: 170px;
overflow: hidden;
transition: height 0.3s ease-in-out;
} }
</style> </style>

View File

@ -1,97 +1,8 @@
<template> <template> <div class="label-pos"> <v-label class="labelTitle-style pb-4">Scores</v-label> </div>
<div class="label-pos"> <!-- Équipes Rouges et Bleues côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Rouge --> <v-col cols="6"> <!-- Colonnes de taille 6 pour chaque équipe --> <v-row no-gutters> <v-col class="scorediv-style-red"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ RedRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ RedTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
<v-label class="labelTitle-style pb-4">Scores</v-label> <!-- Équipe Bleue --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-blue"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ BlueRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ BlueTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
</div> <!-- Équipes Oranges et Vertes côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Orange --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-orange"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ OrangeRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ OrangeTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
<!-- Équipe Verte --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-green"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ GreenRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ GreenTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
<!-- Équipes Rouges et Bleues côte à côte -->
<v-row no-gutters class="scorebox-pos">
<!-- Équipe Rouge -->
<v-col cols="6"> <!-- Colonnes de taille 6 pour chaque équipe -->
<v-row no-gutters>
<v-col class="scorediv-style-red">
<div>
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
<div>
<v-label class="labelRoundScore-style">{{ RedRoundScore }}</v-label>
</div>
</div>
<v-divider color="background"/>
<div>
<v-label class="labelTotalScore-style pt-3">Total</v-label>
<div>
<v-label class="labelTotalScore-style pb-3">{{ RedTotalScore }}</v-label>
</div>
</div>
</v-col>
</v-row>
</v-col>
<!-- Équipe Bleue -->
<v-col cols="6">
<v-row no-gutters>
<v-col class="scorediv-style-blue">
<div>
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
<div>
<v-label class="labelRoundScore-style">{{ BlueRoundScore }}</v-label>
</div>
</div>
<v-divider color="background"/>
<div>
<v-label class="labelTotalScore-style pt-3">Total</v-label>
<div>
<v-label class="labelTotalScore-style pb-3">{{ BlueTotalScore }}</v-label>
</div>
</div>
</v-col>
</v-row>
</v-col>
</v-row>
<!-- Équipes Oranges et Vertes côte à côte -->
<v-row no-gutters class="scorebox-pos">
<!-- Équipe Orange -->
<v-col cols="6">
<v-row no-gutters>
<v-col class="scorediv-style-orange">
<div>
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
<div>
<v-label class="labelRoundScore-style">{{ OrangeRoundScore }}</v-label>
</div>
</div>
<v-divider color="background"/>
<div>
<v-label class="labelTotalScore-style pt-3">Total</v-label>
<div>
<v-label class="labelTotalScore-style pb-3">{{ OrangeTotalScore }}</v-label>
</div>
</div>
</v-col>
</v-row>
</v-col>
<!-- Équipe Verte -->
<v-col cols="6">
<v-row no-gutters>
<v-col class="scorediv-style-green">
<div>
<v-label class="labelRoundScore-style pt-3">Manche</v-label>
<div>
<v-label class="labelRoundScore-style">{{ GreenRoundScore }}</v-label>
</div>
</div>
<v-divider color="background"/>
<div>
<v-label class="labelTotalScore-style pt-3">Total</v-label>
<div>
<v-label class="labelTotalScore-style pb-3">{{ GreenTotalScore }}</v-label>
</div>
</div>
</v-col>
</v-row>
</v-col>
</v-row>
</template> </template>
<script setup> <script setup>
@ -111,51 +22,24 @@ const GreenRoundScore = ref(variables.GreenRoundScore);
</script> </script>
<style> <style>
.label-pos { .label-pos { padding-top: 15px; text-align: center;
padding-top: 15px;
text-align: center;
} }
.labelTitle-style { .labelTitle-style { font-size: 20px !important; font-weight: 500; color: #d42828 !important; opacity: 90% !important;
font-size: 20px !important;
font-weight: 500;
color: #d42828 !important;
opacity: 90% !important;
} }
.labelRoundScore-style { .labelRoundScore-style { opacity: 100% !important; font-size: 25px !important; font-weight: 500;
opacity: 100% !important;
font-size: 25px !important;
font-weight: 500;
} }
.labelTotalScore-style { .labelTotalScore-style { opacity: 100% !important; font-size: 15px !important; font-weight: 500;
opacity: 100% !important;
font-size: 15px !important;
font-weight: 500;
} }
.button-pos { .button-pos { padding-top: 10px; padding-bottom: 15px;
padding-top: 10px;
padding-bottom: 15px;
} }
.scorebox-pos { .scorebox-pos { text-align: center;
text-align: center;
} }
.scorediv-style-red { .scorediv-style-red { background-color: #d42828 !important; padding: 15px; border-top-left-radius: 10%;
background-color: #d42828 !important;
padding: 15px;
border-top-left-radius: 10%;
} }
.scorediv-style-orange { .scorediv-style-orange { background-color: #d48f28 !important; padding: 15px; border-bottom-left-radius: 10%;
background-color: #d48f28 !important;
padding: 15px;
border-bottom-left-radius: 10%;
} }
.scorediv-style-blue { .scorediv-style-blue { background-color: #2867d4 !important; padding: 15px; border-top-right-radius: 10%;
background-color: #2867d4 !important;
padding: 15px;
border-top-right-radius: 10%;
} }
.scorediv-style-green { .scorediv-style-green { background-color: #28d42e !important; padding: 15px; border-bottom-right-radius: 10%;
background-color: #28d42e !important;
padding: 15px;
border-bottom-right-radius: 10%;
} }
</style> </style>

View File

@ -1,48 +1,46 @@
<template> <template>
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary" @click="toggleCardSize"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon>
Solution Solution </v-card-title>
</v-card-title> <v-container class="text-center">
<v-container class="text-center"> <v-row justify="center">
<v-row justify="center"> <v-container class="text-center"> <!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
<v-container class="text-center"> <v-img width="450" src="@/assets/copilot-solution-FULL-HD.jpg" style="margin: 0 auto;">
<!-- Utilisation de styles CSS personnalisés pour centrer l'image --> </v-img>
<v-img width="450" src="@/assets/copilot-solution-FULL-HD.jpg" style="margin: 0 auto;"></v-img> </v-container>
</v-container> </v-row>
</v-row> </v-container>
</v-container> </v-card>
</v-card>
</template> </template>
<style> <style>
@media (min-width: 1024px) { @media (min-width: 1024px) {
.image-container { .image-container {
width: 300px; width: 300px; overflow: hidden; /* Pour masquer le dépassement de l'image */
overflow: hidden; /* Pour masquer le dépassement de l'image */ border: 1px solid #ccc; /* Bordure de l'image */
border: 1px solid #ccc; /* Bordure de l'image */ }
} .image-container img {
.image-container img { width: 100%; /* Pour remplir complètement le conteneur */
width: 100%; /* Pour remplir complètement le conteneur */ height: auto; /* Pour maintenir le ratio d'aspect de l'image */
height: auto; /* Pour maintenir le ratio d'aspect de l'image */ display: block; /* Pour éviter l'espace réservé pour les images */
display: block; /* Pour éviter l'espace réservé pour les images */ }
} }
} .card--reduced {
.card--reduced { height: 56px; /* Réglez la hauteur réduite selon vos besoins */
height: 56px; /* Réglez la hauteur réduite selon vos besoins */ width: 160px;
width: 160px; overflow: hidden;
overflow: hidden; transition: height 0.6s ease-in-out;
transition: height 0.6s ease-in-out; }
}
</style> </style>
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
// Variable pour contrôler l'état de la carte // Variable pour contrôler l'état de la carte
const isCardReduced = ref(false); const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte // Méthode pour basculer l'état de la carte
function toggleCardSize() { function toggleCardSize() {
isCardReduced.value = !isCardReduced.value; isCardReduced.value = !isCardReduced.value;
} }
</script> </script>

View File

@ -1,39 +1,39 @@
<template> <template>
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }"> <v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
<v-card-title class="card__title primary" @click="toggleCardSize"> <v-card-title class="card__title primary" @click="toggleCardSize">
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon> <v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon>
Soundboard Soundboard
</v-card-title> </v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded>
<v-icon size="60">mdi-check-circle-outline</v-icon> <v-icon size="60">mdi-check-circle-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded>
<v-icon size="60">mdi-close-circle-outline</v-icon> <v-icon size="60">mdi-close-circle-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mt-4"> <v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded>
<v-icon size="60">mdi-timer-outline</v-icon> <v-icon size="60">mdi-timer-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
</v-row> </v-row>
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mb-4"> <v-col cols="12" sm="6" md="4" class="mb-4">
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded>
<v-icon size="60">mdi-human-handsup</v-icon> <v-icon size="60">mdi-human-handsup</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
<v-col cols="12" sm="6" md="4" class="mb-4"> <v-col cols="12" sm="6" md="4" class="mb-4">
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded> <mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded>
<v-icon size="60">mdi-bell-outline</v-icon> <v-icon size="60">mdi-bell-outline</v-icon>
</mqtt-button> </mqtt-button>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card> </v-card>
</template> </template>
@ -46,16 +46,16 @@
const isCardReduced = ref(false); const isCardReduced = ref(false);
// Méthode pour basculer l'état de la carte // Méthode pour basculer l'état de la carte
function toggleCardSize() { function toggleCardSize() {
isCardReduced.value = !isCardReduced.value; isCardReduced.value = !isCardReduced.value;
} }
</script> </script>
<style scoped> <style scoped>
.card--reduced { .card--reduced {
height: 56px; /* Réglez la hauteur réduite selon vos besoins */ height: 56px; /* Réglez la hauteur réduite selon vos besoins */
width: 190px; width: 190px;
overflow: hidden; overflow: hidden;
transition: height 0.3s ease-in-out; transition: height 0.3s ease-in-out;
} }
</style> </style>

View File

@ -1,14 +1,13 @@
<template> <template>
<div class="container"> <div class="container">
<div class="timer"> <div class="timer">
<v-label color="primary" class="labelTime-style" >{{ formatTime }}</v-label> <v-label color="primary" class="labelTime-style" >{{ formatTime }}</v-label>
</div> </div>
<v-row no-gutters justify="space-around" >
<v-row no-gutters justify="space-around" > <v-btn class="buttons" color="primary" icon="mdi-play" @click="startTimer"></v-btn>
<v-btn class="buttons" color="primary" icon="mdi-play" @click="startTimer"></v-btn> <v-btn color="primary" icon="mdi-pause" @click="pauseTimer"></v-btn>
<v-btn color="primary" icon="mdi-pause" @click="pauseTimer"></v-btn> <v-btn color="primary" icon="mdi-restart" @click="resetTimer"></v-btn>
<v-btn color="primary" icon="mdi-restart" @click="resetTimer"></v-btn> </v-row>
</v-row>
</div> </div>
</template> </template>
@ -20,99 +19,82 @@
const currentTime = ref(null); const currentTime = ref(null);
const elapsedTime = ref(0); const elapsedTime = ref(0);
const formatTime = computed(() => { const formatTime = computed(() => {
let seconds = Math.floor(elapsedTime.value / 1000); let seconds = Math.floor(elapsedTime.value / 1000);
let minutes = Math.floor(seconds / 60); let minutes = Math.floor(seconds / 60);
let hours = Math.floor(minutes / 60); let hours = Math.floor(minutes / 60);
seconds = seconds % 60; minutes = minutes % 60;
seconds = seconds % 60; return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
minutes = minutes % 60; });
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; const pad = (number) => {
}); return (number < 10 ? "0" : "") + number;
const pad = (number) => {
return (number < 10 ? "0" : "") + number;
};
const startTimer = () => {
if (!timerActive.value) {
timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer();
}
};
const pauseTimer = () => {
if (timerActive.value) {
timerActive.value = false;
clearInterval(currentTime.value);
}
};
const resetTimer = () => {
elapsedTime.value = 0;
timerActive.value = false;
clearInterval(currentTime.value);
};
const updateTimer = () => {
currentTime.value = setInterval(() => {
elapsedTime.value = Date.now() - startTime.value;
}, 1000);
};
onBeforeUnmount(() => {
clearInterval(currentTime.value);
});
</script>
<script>
const startTimer = () => {
if (!timerActive.value) {
timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer();
}
}; };
const pauseTimer = () => { const startTimer = () => {
if (timerActive.value) { if (!timerActive.value) {
timerActive.value = false; timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer(); }
};
const pauseTimer = () => {
if (timerActive.value) {
timerActive.value = false;
clearInterval(currentTime.value); }
};
const resetTimer = () => {
elapsedTime.value = 0;
timerActive.value = false;
clearInterval(currentTime.value); clearInterval(currentTime.value);
}
}; };
const resetTimer = () => { const updateTimer = () => {
elapsedTime.value = 0; currentTime.value = setInterval(() => {elapsedTime.value = Date.now() - startTime.value; }, 1000);
timerActive.value = false;
clearInterval(currentTime.value);
}; };
export { startTimer, pauseTimer, resetTimer }; onBeforeUnmount(() => { clearInterval(currentTime.value);
});
</script>
<script>
const startTimer = () => {
if (!timerActive.value) {
timerActive.value = true;
startTime.value = Date.now() - elapsedTime.value;
updateTimer(); } };
const pauseTimer = () => {
if (timerActive.value) {
timerActive.value = false;
clearInterval(currentTime.value); } };
const resetTimer = () => {
elapsedTime.value = 0;
timerActive.value = false;
clearInterval(currentTime.value); };
export { startTimer, pauseTimer, resetTimer };
</script> </script>
<style> <style>
.container { .container {
text-align: center; text-align: center;
margin-top: auto; /* Place le container en bas de son parent */ margin-top: auto; /* Place le container en bas de son parent */
margin-bottom: 1px; /* Marge en bas pour un espacement */ margin-bottom: 1px; /* Marge en bas pour un espacement */
position: fixed; /* Le positionne de manière fixe */ position: fixed; /* Le positionne de manière fixe */
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
padding: 20px; padding: 20px;
} }
.timer { .timer {
margin-bottom: 15px; margin-bottom: 15px;
} }
.labelTime-style { .labelTime-style {
font-size: 30px !important; font-size: 30px !important;
font-weight: 500; font-weight: 500;
color: #d42828 !important; color: #d42828 !important;
opacity: 90% !important; opacity: 90% !important;
} }
.buttons{ .buttons{
background-color: rgb(255, 255, 255); background-color: rgb(255, 255, 255);
} }
</style> </style>

View File

@ -1,45 +1,41 @@
<template> <template>
<v-navigation-drawer width="250"> <!-- Augmenter la largeur pour deux équipes côte à côte --> <v-navigation-drawer width="250"> <!-- Augmenter la largeur pour deux équipes côte à côte -->
<div class="label-pos"> <div class="label-pos">
<v-label class="labelTitle-style">Buzzer connectés</v-label> <v-label class="labelTitle-style">Buzzer connectés</v-label>
</div> </div>
<v-row no-gutters justify="space-around" class="button-pos">
<v-row no-gutters justify="space-around" class="button-pos"> <v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon> <v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon>
<v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon> </v-row>
</v-row> <v-divider :thickness="2" class="border-opacity-100" color="primary"/>
<CardScore/>
<v-divider :thickness="2" class="border-opacity-100" color="primary"/> <CardTimer/>
</v-navigation-drawer>
<CardScore/>
<CardTimer/>
</v-navigation-drawer>
</template> </template>
<script setup> <script setup>
import CardTimer from '@/components/CardTimer.vue' import CardTimer from '@/components/CardTimer.vue'
import CardScore from '@/components/CardScore.vue' import CardScore from '@/components/CardScore.vue'
import { ref } from 'vue'; // Import des fonctions de Vue 3 import { ref } from 'vue'; // Import des fonctions de Vue 3
import variables from '@/variables.js'; import variables from '@/variables.js';
</script> </script>
<style> <style>
.label-pos { .label-pos {
padding-top: 15px; padding-top: 15px;
text-align: center; text-align: center;
} }
.labelTitle-style { .labelTitle-style {
font-size: 20px !important; font-size: 20px !important;
font-weight: 500; font-weight: 500;
color: #e91e1e !important; color: #e91e1e !important;
opacity: 90% !important; opacity: 90% !important;
} }
.button-pos { .button-pos {
padding-top: 10px; padding-top: 10px;
padding-bottom: 15px; padding-bottom: 15px;
} }
</style> </style>

View File

@ -1,63 +1,58 @@
<template> <template>
<v-container class="v-container-style"> <v-container class="v-container-style">
<v-card tile outlined width="500"> <v-card tile outlined width="500">
<v-card-title class="card__title primary centered-title"> <v-card-title class="card__title primary centered-title">
<v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon> <v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon>
Console MQTT Console MQTT
</v-card-title> </v-card-title>
<v-container class="text-center"> <v-container class="text-center">
<div v-for="(log, index) in messageLogs" :key="index"> <div v-for="(log, index) in messageLogs" :key="index">
<v-label class="v-label-timestamp">{{ log.timestamp }} </v-label> <v-label class="v-label-timestamp">{{ log.timestamp }} </v-label> -
- <v-label>{{ log.message }}</v-label>
<v-label>{{ log.message }}</v-label> </div>
</div> </v-container>
</v-container> </v-card>
</v-card> </v-container>
</v-container>
</template> </template>
<script> <script>
import { subscribeToTopic } from '@/services/mqttService' import { subscribeToTopic } from '@/services/mqttService'
export default { export default {
data() { data() { return {
return { messageLogs: [] // Initialiser un tableau pour stocker les messages MQTT avec horodatage
messageLogs: [] // Initialiser un tableau pour stocker les messages MQTT avec horodatage
}
},
created() {
subscribeToTopic('#', (topic, message) => {
// Obtenir l'horodatage actuel
const timestamp = new Date().toLocaleString('fr-FR', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// Ajouter le message avec l'horodatage à la liste des messages
this.messageLogs.push({ timestamp, message: `Topic: ${topic}, Message: ${message}` });
// Limiter la liste à 10 messages
if (this.messageLogs.length > 10) {
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
} }
}) // S'abonner à tous les topics MQTT },
} created() { subscribeToTopic('#', (topic, message) => { // Obtenir l'horodatage actuel
} const timestamp = new Date().toLocaleString('fr-FR', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// Ajouter le message avec l'horodatage à la liste des messages
this.messageLogs.push({ timestamp, message: `Topic: ${topic}, Message: ${message}` });
// Limiter la liste à 10 messages
if (this.messageLogs.length > 10) {
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
} }) // S'abonner à tous les topics MQTT
}
}
</script> </script>
<style> <style>
.v-container-style { .v-container-style {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.centered-title { .centered-title {
text-align: center; text-align: center;
} }
.v-label-timestamp{ .v-label-timestamp{
opacity: 100%; opacity: 100%;
font-weight: 700; font-weight: 700;
color: #d42828; color: #d42828;
} }
</style> </style>

View File

@ -1,17 +1,16 @@
<template> <template>
<v-container class="v-container-style"> <v-container class="v-container-style">
<v-card tile outlined width="500"> <v-card tile outlined width="500">
<v-card-title class="card__title primary centered-title"> <v-card-title class="card__title primary centered-title">
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon> <v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
Publier un message Publier un message
</v-card-title> </v-card-title>
<div class="input-style"> <div class="input-style">
<v-select label="Topic" v-model="selectedTopic" :items="topics" prepend-icon="mdi-target"></v-select> <v-select label="Topic" v-model="selectedTopic" :items="topics" prepend-icon="mdi-target"></v-select>
<v-text-field label="Message" v-model="message" prepend-icon="mdi-text-box"></v-text-field> <v-text-field label="Message" v-model="message" prepend-icon="mdi-text-box"></v-text-field>
</div> </div> <v-btn class="v-btn-style" height="50" @click="publishMessage">Publier</v-btn>
<v-btn class="v-btn-style" height="50" @click="publishMessage">Publier</v-btn> </v-card>
</v-card> </v-container>
</v-container>
</template> </template>
@ -19,49 +18,35 @@
import { publishMessage } from '@/services/mqttService' import { publishMessage } from '@/services/mqttService'
export default { export default {
data() { data() { return { message: '', // Initialiser la variable message
return { selectedTopic: 'topic1',
message: '', // Initialiser la variable message topics: [ '/display/control', '/sound/playsound', 'topic3', 'topic4', 'topic5', 'topic6', 'topic7', 'topic8', 'topic9', 'topic10' ] // Liste des topics
selectedTopic: 'topic1', }
topics: [ },
'/display/control', methods: {
'/sound/playsound', publishMessage() {
'topic3', publishMessage(this.selectedTopic, this.message) }
'topic4',
'topic5',
'topic6',
'topic7',
'topic8',
'topic9',
'topic10'
] // Liste des topics
}
},
methods: {
publishMessage() {
publishMessage(this.selectedTopic, this.message)
}
} }
} }
</script> </script>
<style> <style>
.v-container-style { .v-container-style {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.input-style{ .input-style{
margin: 20px; margin: 20px;
} }
.v-btn-style{ .v-btn-style{
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
background-color: #d42828; /* Changez la couleur en fonction de votre thème */ background-color: #d42828; /* Changez la couleur en fonction de votre thème */
border-top-right-radius: 0%; border-top-right-radius: 0%;
border-top-left-radius: 0%; border-top-left-radius: 0%;
} }
.centered-title { .centered-title {
text-align: center; text-align: center;
} }
</style> </style>

View File

@ -1,22 +1,22 @@
<template> <template>
<v-btn @click="_publishMessage" v-bind="$attrs"> <v-btn @click="_publishMessage" v-bind="$attrs">
<slot/> <slot/>
</v-btn> </v-btn>
</template> </template>
<script setup> <script setup>
import { publishMessage } from '@/services/mqttService' import { publishMessage } from '@/services/mqttService'
import { ref, defineProps } from 'vue' import { ref, defineProps } from 'vue'
const props = defineProps({ const props = defineProps({
topic: String, topic: String,
message: null message: null
}) })
const disabled = ref(false) const disabled = ref(false)
const _publishMessage = () => { const _publishMessage = () => {
publishMessage(props.topic, JSON.stringify(props.message)) publishMessage(props.topic, JSON.stringify(props.message))
disabled.value = true disabled.value = true
} }
</script> </script>

View File

@ -1,8 +1,8 @@
<template> <template>
<v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon> <v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon>
<v-menu v-model="menu" class="menu-below-bar"> <v-menu v-model="menu" class="menu-below-bar">
<v-list> <v-list>
<v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item> <v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
</template> </template>

View File

@ -3,32 +3,30 @@ import HomeView from '../views/HomeView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [ {
{ path: '/',
path: '/', name: 'Accueil',
name: 'Accueil', component: HomeView
component: HomeView },
}, {
{ path: '/game/control',
path: '/game/control', name: 'Game Control (Présentateur)',
name: 'Game Control (Présentateur)', component: () => import('@/views/GameControl.vue')
component: () => import('@/views/GameControl.vue') },
}, {
{ path: '/game/display',
path: '/game/display', name: 'Game Display (Projection)',
name: 'Game Display (Projection)', component: () => import('@/views/GameDisplay.vue')
component: () => import('@/views/GameDisplay.vue') },
}, {
{ path: '/mqtt-debugger',
path: '/mqtt-debugger', name: 'Debugger MQTT',
name: 'Debugger MQTT', component: () => import('@/views/MQTTDebugView.vue')
component: () => import('@/views/MQTTDebugView.vue') },
}, {
{ path: '/settings',
path: '/settings', name: 'Paramètres',
name: 'Paramètres', component: () => import('@/views/SettingsView.vue') }
component: () => import('@/views/SettingsView.vue')
}
] ]
}) })

View File

@ -13,46 +13,45 @@ import { createVuetify } from 'vuetify'
const CustomThemeDark = { const CustomThemeDark = {
dark: true, dark: true,
colors: { colors: {
background: '#121212', background: '#121212',
primary: '#d42828', primary: '#d42828',
secondary: '#F44336', secondary: '#F44336',
accent: '#FFC107', accent: '#FFC107',
error: '#e91e1e', error: '#e91e1e',
warning: '#FFC107', warning: '#FFC107',
info: '#607D8B', info: '#607D8B',
success: '#e91e1e', success: '#e91e1e',
BuzzerBlue: '#2867d4', BuzzerBlue: '#2867d4',
BuzzerOrange: '#d48f28', BuzzerOrange: '#d48f28',
BuzzerRed: '#d42828', BuzzerRed: '#d42828',
BuzzerGreen: '#28d42e', BuzzerGreen: '#28d42e',
} }
} }
const CustomThemeLight = { const CustomThemeLight = {
dark: false, dark: false,
colors: { colors: {
background: '#ffffff', background: '#ffffff',
primary: '#d42828', primary: '#d42828',
secondary: '#F44336', secondary: '#F44336',
accent: '#FFC107', accent: '#FFC107',
error: '#e91e1e', error: '#e91e1e',
warning: '#FFC107', warning: '#FFC107',
info: '#607D8B', info: '#607D8B',
success: '#4CAF50', success: '#4CAF50',
BuzzerBlue: '#2867d4', BuzzerBlue: '#2867d4',
BuzzerOrange: '#d48f28', BuzzerOrange: '#d48f28',
BuzzerRed: '#d42828', BuzzerRed: '#d42828',
BuzzerGreen: '#28d42e', BuzzerGreen: '#28d42e',
} }
} }
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
export default createVuetify({ export default createVuetify({
theme: { theme: {
defaultTheme: 'CustomThemeDark', defaultTheme: 'CustomThemeDark',
themes: { themes: {
CustomThemeDark, CustomThemeDark,
CustomThemeLight, CustomThemeLight, },
},
}, },
}) })

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -0,0 +1,27 @@
name: "Histoire & Géographie"
questions:
1:
Q: "Quelle bataille célèbre s'est déroulée en 1815, marquant la défaite de Napoléon Bonaparte ?"
T: "Elle a eu lieu en Belgique"
R: "La bataille de Waterloo."
P: "Q-1.jpeg"
2:
Q: "Quelle est la capitale de l'Australie ?"
T: "Le nom de cette ville commence par la lettre 'C'"
R: "Canberra."
P: "Q-2.jpeg"
3:
Q: "En quelle année la Seconde Guerre mondiale a-t-elle pris fin ?"
T: "C'est au milieu des années 40."
R: "En 1945."
P: "Q-3.jpeg"
4:
Q: "Quel fleuve traverse la ville du Caire en Égypte ?"
T: "C'est l'un des plus longs fleuves du monde"
R: "Le Nil."
P: "Q-4.jpeg"
5:
Q: "Quel pays a été divisé par un mur de 1961 à 1989 ?"
T: "Sa chute a marqué la fin de la guerre froide."
R: "L'Allemagne (le mur de Berlin)."
P: "Q-5.jpeg"

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

View File

View File

View File

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -0,0 +1,27 @@
name: "Jeux vidéos"
questions:
1:
Q: "Quel personnage de jeu vidéo est un plombier moustachu qui saute sur des ennemis pour sauver une princesse ?"
T: "Cest le personnage le plus célèbre de Nintendo, son nom commence par 'M'."
R: "Mario."
P: "Q-1.jpeg"
2:
Q: "Quel jeu vidéo multijoueur de football avec des voitures est très populaire ?"
T: "Il s'agit d'un mélange de sport et de voitures rapides."
R: "Rocket League."
P: "Q-2.jpeg"
3:
Q: "Quel jeu vidéo mobile consiste à faire exploser des bonbons en alignant trois pièces identiques ?"
T: "Son nom fait référence aux bonbons."
R: "Candy Crush Saga."
P: "Q-3.jpeg"
4:
Q: "Quel est le nom du célèbre personnage bleu de SEGA qui court à une vitesse incroyable ?"
T: "Son nom commence par la lettre 'S' et c'est un hérisson."
R: "Sonic"
P: "Q-4.jpeg"
5:
Q: "Quel jeu permet de construire et explorer un monde fait de blocs, tout en survivant face à des monstres ?"
T: "Le monde est entièrement fait de blocs carrés."
R: "Minecraft."
P: "Q-5.jpeg"

View File

@ -6,43 +6,43 @@ import ping from 'ping';
Vue.use(Vuex); Vue.use(Vuex);
const store = new Vuex.Store({ const store = new Vuex.Store({
state: { state: {
buzzerRedState: '', buzzerRedState: '',
buzzerBlueState: '', buzzerBlueState: '',
buzzerOrangeState: '', buzzerOrangeState: '',
buzzerGreenState: '' buzzerGreenState: ''
}, },
mutations: { mutations: {
setBuzzerRedState(state, newState) { setBuzzerRedState(state, newState) {
state.buzzerRedState = newState; state.buzzerRedState = newState;
}, },
setBuzzerBlueState(state, newState) { setBuzzerBlueState(state, newState) {
state.buzzerBlueState = newState; state.buzzerBlueState = newState;
}, },
setBuzzerOrangeState(state, newState) { setBuzzerOrangeState(state, newState) {
state.buzzerOrangeState = newState; state.buzzerOrangeState = newState;
}, },
setBuzzerGreenState(state, newState) { setBuzzerGreenState(state, newState) {
state.buzzerGreenState = newState; state.buzzerGreenState = newState;
} }
}, },
actions: { actions: {
updateBuzzerStates({ commit }, { index, newState }) { updateBuzzerStates({ commit }, { index, newState }) {
switch (index) { switch (index) {
case 0: case 0:
commit('setBuzzerRedState', newState); commit('setBuzzerRedState', newState);
break; break;
case 1: case 1:
commit('setBuzzerBlueState', newState); commit('setBuzzerBlueState', newState);
break; break;
case 2: case 2:
commit('setBuzzerOrangeState', newState); commit('setBuzzerOrangeState', newState);
break; break;
case 3: case 3:
commit('setBuzzerGreenState', newState); commit('setBuzzerGreenState', newState);
break; break;
default: default:
console.error('Index de buzzer invalide:', index); console.error('Index de buzzer invalide:', index);
} }
} }
} }
@ -51,13 +51,11 @@ const store = new Vuex.Store({
const hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4']; const hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'];
async function pingHosts() { async function pingHosts() {
hosts.forEach(async (host, index) => { hosts.forEach(async (host, index) => { try {
try { const res = await ping.promise.probe(host);
const res = await ping.promise.probe(host); store.dispatch('updateBuzzerStates', {index, newState: res.alive ? 'ON' : 'OFF' }); }
store.dispatch('updateBuzzerStates', { index, newState: res.alive ? 'ON' : 'OFF' }); catch (error) {
} catch (error) { console.error(`Erreur lors du ping de ${host}:`, error.message); }
console.error(`Erreur lors du ping de ${host}:`, error.message);
}
}); });
} }

View File

@ -13,7 +13,6 @@ export function publishMessage(topic, message) {
// Fonction pour s'abonner à un topic MQTT et écouter les messages entrants // Fonction pour s'abonner à un topic MQTT et écouter les messages entrants
export function subscribeToTopic(topic, callback) { export function subscribeToTopic(topic, callback) {
client.subscribe(topic) client.subscribe(topic)
client.on('message', (receivedTopic, message) => { client.on('message', (receivedTopic, message) => { callback(receivedTopic.toString(), message.toString())
callback(receivedTopic.toString(), message.toString())
}) })
} }

View File

@ -0,0 +1,43 @@
import express from 'express';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const port = 3000;
// Obtenir le chemin du répertoire parent
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Middleware pour gérer les requêtes depuis le frontend
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
next();
});
// Le dossier assets est situé un niveau au-dessus du dossier services
const assetsDir = path.join(__dirname, '..', 'quizz/geography-history');
console.log(assetsDir)
// Middleware pour servir les fichiers statiques
app.use('/images', express.static(assetsDir));
// API pour lister les fichiers d'image
app.get('/images-list', async (req, res) => {
try {
const files = await fs.readdir(assetsDir);
// Filtrer pour ne renvoyer que les fichiers d'image (par ex : .jpg, .png)
const images = files.filter(file => /\.(jpg|jpeg|png|gif)$/.test(file));
res.json(images);
} catch (err) {
res.status(500).send('Erreur lors de la lecture du dossier');
}
});
app.listen(port, () => {
console.log(`Serveur démarré sur http://localhost:${port}`);
});

View File

@ -23,10 +23,5 @@ export default {
}; };
// Variables localStorage // Variables localStorage
export const localStorageVars = { export const localStorageVars = { // Exemple de variable localStorage RedScorelocal: localStorage.getItem('RedScore') || '', BlueScorelocal: localStorage.getItem('BlueScore') || '', OrangeScorelocal: localStorage.getItem('OrangeScore') || '', GreenScorelocal: localStorage.getItem('GreenScore') || '',
// Exemple de variable localStorage
RedScorelocal: localStorage.getItem('RedScore') || '',
BlueScorelocal: localStorage.getItem('BlueScore') || '',
OrangeScorelocal: localStorage.getItem('OrangeScore') || '',
GreenScorelocal: localStorage.getItem('GreenScore') || '',
}; };

View File

@ -1,23 +1,22 @@
<template> <template>
<v-container> <v-container>
<v-row no-gutters> <v-row no-gutters>
<v-col class="align-start"> <v-col class="align-start">
<card-control /> <card-control />
</v-col> </v-col>
<v-col class="pl-3"> <v-col class="pl-3">
<card-soundboard /> <card-soundboard />
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
<v-row no-gutters class="pr-4 pl-4">
<v-row no-gutters class="pr-4 pl-4"> <v-row no-gutters>
<v-row no-gutters> <v-col class="align-start">
<v-col class="align-start"> <CardButtonScore />
<CardButtonScore /> </v-col>
</v-col> <v-col class="pl-3">
<v-col class="pl-3"> <card-solution />
<card-solution /> </v-col>
</v-col>
</v-row> </v-row>
</v-row> </v-row>
</template> </template>
@ -33,49 +32,45 @@ import CardButtonScore from '@/components/CardButtonScore.vue'
<style> <style>
@media (min-width: 1024px) { @media (min-width: 1024px) {
.card__title.primary { .card__title.primary {
background-color: #d42828; /* Changez la couleur en fonction de votre thème */ background-color: #d42828; /* Changez la couleur en fonction de votre thème */
} }
.card__title.feedback { .card__title.feedback {
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */ background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
} }
.btn{ .btn{
border-radius:20px!important; border-radius:20px!important;
} }
.btn.red { .btn.red {
background-color: #d42828; /* Changez la couleur en fonction de votre thème */ background-color: #d42828; /* Changez la couleur en fonction de votre thème */
} }
.btn.blue { .btn.blue {
background-color: #2867d4; /* Changez la couleur en fonction de votre thème */ background-color: #2867d4; /* Changez la couleur en fonction de votre thème */
} }
.btn.orange { .btn.orange {
background-color: #d48f28; /* Changez la couleur en fonction de votre thème */ background-color: #d48f28; /* Changez la couleur en fonction de votre thème */
} }
.btn.green { .btn.green {
background-color: #28d42e; /* Changez la couleur en fonction de votre thème */ background-color: #28d42e; /* Changez la couleur en fonction de votre thème */
} }
.scorediv-style-red { .scorediv-style-red {
background-color: #d42828 !important; background-color: #d42828 !important;
padding: 15px; padding: 15px;
border-top-left-radius: 10%; border-top-left-radius: 10%;
} }
.scorediv-style-orange { .scorediv-style-orange {
background-color: #d48f28 !important; background-color: #d48f28 !important;
padding: 15px; padding: 15px;
border-bottom-left-radius: 10%; border-bottom-left-radius: 10%;
} }
.scorediv-style-blue { .scorediv-style-blue {
background-color: #2867d4 !important; background-color: #2867d4 !important;
padding: 15px; padding: 15px;
border-top-right-radius: 10%; border-top-right-radius: 10%;
} }
.scorediv-style-green { .scorediv-style-green {
background-color: #28d42e !important; background-color: #28d42e !important;
padding: 15px; padding: 15px;
border-bottom-right-radius: 10%; border-bottom-right-radius: 10%;
} }
} }

View File

@ -1,3 +1,53 @@
<template> <template>
<v-container>
</template> <v-row>
<v-col
v-for="image in images"
:key="image"
cols="12"
sm="6"
md="4"
>
<v-card>
<v-img
:src="`http://localhost:3000/images/${image}`"
:alt="image"
class="image-thumbnail"
/>
<v-card-title>{{ image }}</v-card-title>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
// Déclare une variable réactive pour stocker les images
const images = ref([]);
// Fonction pour récupérer les images depuis l'API
const fetchImages = async () => {
try {
const response = await fetch('http://localhost:3000/images-list');
const data = await response.json();
images.value = data; // Met à jour les images
} catch (error) {
console.error('Erreur lors de la récupération des images :', error);
}
};
// Appelle la fonction fetchImages lorsque le composant est monté
onMounted(() => {
fetchImages();
});
</script>
<style scoped>
.image-thumbnail {
max-width: 100%;
height: auto;
}
</style>

View File

@ -1,10 +1,6 @@
<template> <template>
<div> <div> <!-- Zone pour publier sur MQTT --> <PublishMQTTComponent />
<!-- Zone pour publier sur MQTT --> <!-- Zone pour afficher la console MQTT --> <MQTTConsoleComponent />
<PublishMQTTComponent />
<!-- Zone pour afficher la console MQTT -->
<MQTTConsoleComponent />
</div> </div>
</template> </template>
@ -13,24 +9,18 @@ import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT
export default { export default {
components: { components: { PublishMQTTComponent, // Composant pour publier sur MQTT MQTTConsoleComponent // Composant pour la console MQTT
PublishMQTTComponent, // Composant pour publier sur MQTT
MQTTConsoleComponent // Composant pour la console MQTT
} }
} }
</script> </script>
<style> <style>
@media (min-width: 1024px) { @media (min-width: 1024px) {
.card__title.primary { .card__title.primary { background-color: #d42828; /* Changez la couleur en fonction de votre thème */
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
} }
.card__title.feedback { .card__title.feedback { background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
} }
.btn{ .btn{ border-radius:30px!important; background-color: #d42828; /* Changez la couleur en fonction de votre thème */
border-radius:30px!important;
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
} }
} }
</style> </style>

View File

@ -1,39 +1,32 @@
<template> <template>
<v-label class="title-style-1">Paramètres</v-label> <v-label class="title-style-1">Paramètres</v-label>
<v-divider :thickness="2" class="border-opacity-100" color="primary"/> <v-divider :thickness="2" class="border-opacity-100" color="primary"/>
<v-label class="title-style-2">Son</v-label> <v-label class="title-style-2">Son</v-label>
<div class="mutltiple-per-line"> <div class="mutltiple-per-line">
<v-switch hide-details label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/> <v-switch hide-details label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/>
<div> <div>
<v-slider hide-details class="v-slider-style ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/> <v-slider hide-details class="v-slider-style ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/>
</div> </div>
</div> </div>
<v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/> <v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/>
<v-divider />
<v-divider /> <v-label class="title-style-2">Affichage</v-label>
<v-label class="title-style-2">Affichage</v-label> <div>
<v-switch hide-details label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15 pb-3" color="primary"/>
<div> </div>
<v-switch hide-details label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15 pb-3" color="primary"/> <v-divider />
</div> <v-label class="title-style-2">MQTT</v-label>
<div class="mutltiple-per-line">
<v-divider /> <v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon>
<v-label class="title-style-2">MQTT</v-label> <v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label>
<v-btn class="ml-10 mb-5" color="primary" @click="goToDebugRoute">Debugger</v-btn>
<div class="mutltiple-per-line"> </div>
<v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon> <v-divider />
<v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label> <v-label class="title-style-2">Jeu</v-label>
<v-btn class="ml-10 mb-5" color="primary" @click="goToDebugRoute">Debugger</v-btn> <div class="mutltiple-per-line">
<v-switch hide-details label='Jouer le son de succès lorsque des points sont ajoutés' v-model="SuccessPlay" class="ml-15" color="primary"/>
</div> </div>
<v-divider /> <v-switch hide-details label='Jouer le son de erreur lorsque des points sont enlevés' v-model="ErrorPlay" class="ml-15" color="primary"/>
<v-label class="title-style-2">Jeu</v-label>
<div class="mutltiple-per-line">
<v-switch hide-details label='Jouer le son de succès lorsque des points sont ajoutés' v-model="SuccessPlay" class="ml-15" color="primary"/>
</div>
<v-switch hide-details label='Jouer le son de erreur lorsque des points sont enlevés' v-model="ErrorPlay" class="ml-15" color="primary"/>
</template> </template>
@ -51,92 +44,89 @@
const ErrorPlay = ref(false); const ErrorPlay = ref(false);
const router = useRouter(); const router = useRouter();
const goToDebugRoute = () => { const goToDebugRoute = () => {
router.push({ name: 'Debugger MQTT' }); // Redirige vers la route nommée 'debugger' router.push({
}; name: 'Debugger MQTT'
}); // Redirige vers la route nommée 'debugger'
};
onMounted(() => { onMounted(() => {
if (localStorage.getItem('EmbeddedSound')) { if (localStorage.getItem('EmbeddedSound')) {
EmbeddedSound.value = localStorage.getItem('EmbeddedSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence EmbeddedSound.value = localStorage.getItem('EmbeddedSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
} }
if (localStorage.getItem('MQTTSound')) { if (localStorage.getItem('MQTTSound')) {
MQTTSound.value = localStorage.getItem('MQTTSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence MQTTSound.value = localStorage.getItem('MQTTSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
} }
if (localStorage.getItem('EmbeddedSoundVolume')) { if (localStorage.getItem('EmbeddedSoundVolume')) {
EmbeddedSoundVolume.value = localStorage.getItem('EmbeddedSoundVolume'); // Si l'état de la case à cocher est stocké, le mettre dans la référence EmbeddedSoundVolume.value = localStorage.getItem('EmbeddedSoundVolume'); // Si l'état de la case à cocher est stocké, le mettre dans la référence
} }
if (localStorage.getItem('SattelitesDisplay')) { if (localStorage.getItem('SattelitesDisplay')) {
SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch
} }
if (localStorage.getItem('SuccessPlay')) { if (localStorage.getItem('SuccessPlay')) {
SuccessPlay.value = localStorage.getItem('SuccessPlay') === 'true'; // Added a default value for this switch SuccessPlay.value = localStorage.getItem('SuccessPlay') === 'true'; // Added a default value for this switch
} }
if (localStorage.getItem('ErrorPlay')) { if (localStorage.getItem('ErrorPlay')) {
ErrorPlay.value = localStorage.getItem('ErrorPlay') === 'true'; // Added a default value for this switch ErrorPlay.value = localStorage.getItem('ErrorPlay') === 'true'; // Added a default value for this switch
} }
}); });
watch(EmbeddedSound, (EmbeddedSoundNewValue) => { watch(EmbeddedSound, (EmbeddedSoundNewValue) => {
if (EmbeddedSoundNewValue !== null) { if (EmbeddedSoundNewValue !== null) {
localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change. localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
} }
}); });
watch(EmbeddedSoundVolume, (EmbeddedSoundVolumeNewValue) => {
watch(EmbeddedSoundVolume, (EmbeddedSoundVolumeNewValue) => { if (EmbeddedSoundVolumeNewValue !== null) {
if (EmbeddedSoundVolumeNewValue !== null) { localStorage.setItem('EmbeddedSoundVolume', EmbeddedSoundVolumeNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
localStorage.setItem('EmbeddedSoundVolume', EmbeddedSoundVolumeNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change. }
} });
}); watch(MQTTSound, (MQTTSoundNewValue) => {
if (MQTTSoundNewValue !== null) {
watch(MQTTSound, (MQTTSoundNewValue) => { localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
}
if (MQTTSoundNewValue !== null) { });
localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change. watch(SattelitesDisplay, (SattelitesDisplaynewValue) => {
} if (SattelitesDisplaynewValue !== null) {
}); localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch
watch(SattelitesDisplay, (SattelitesDisplaynewValue) => { }
if (SattelitesDisplaynewValue !== null) { });
localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch watch(SuccessPlay, (SuccessPlaynewValue) => {
} if (SuccessPlaynewValue !== null) {
}); localStorage.setItem('SuccessPlay', SuccessPlaynewValue); // Added a default value for this switch
}
watch(SuccessPlay, (SuccessPlaynewValue) => { });
if (SuccessPlaynewValue !== null) { watch(ErrorPlay, (ErrorPlaynewValue) => {
localStorage.setItem('SuccessPlay', SuccessPlaynewValue); // Added a default value for this switch if (ErrorPlaynewValue !== null) {
} localStorage.setItem('ErrorPlay', ErrorPlaynewValue); // Added a default value for this switch
}); }
});
watch(ErrorPlay, (ErrorPlaynewValue) => {
if (ErrorPlaynewValue !== null) {
localStorage.setItem('ErrorPlay', ErrorPlaynewValue); // Added a default value for this switch
}
});
</script> </script>
<style> <style>
.title-style-1{ .title-style-1{
margin-top: 20px; margin-top: 20px;
margin-bottom: 16px; margin-bottom: 16px;
margin-left: 20px; margin-left: 20px;
font-size: 30px; font-size: 30px;
opacity: 100%; opacity: 100%;
font-weight: 500; font-weight: 500;
} }
.title-style-2{ .title-style-2{
margin-top: 20px; margin-top: 20px;
margin-bottom: 10px; margin-bottom: 10px;
margin-left: 40px; margin-left: 40px;
font-size: 25px; font-size: 25px;
opacity: 100%; opacity: 100%;
font-weight: 500; font-weight: 500;
} }
.mutltiple-per-line{ .mutltiple-per-line{
display: flex; display: flex;
align-items: center; align-items: center;
} }
.v-slider-style{ .v-slider-style{
width: 250px; width: 250px;
margin-left: 16px; margin-left: 16px;
padding-top: px; padding-top: px;
} }
</style> </style>

View File

@ -9,24 +9,9 @@ import { defineConfig } from 'vite'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [ vue({ template: { transformAssetUrls } }), Vuetify(), ViteFonts({ google: { families: [{ name: 'Roboto', styles: 'wght@100;300;400;500;700;900', }] } }),
vue({
template: { transformAssetUrls }
}),
Vuetify(),
ViteFonts({
google: {
families: [{
name: 'Roboto',
styles: 'wght@100;300;400;500;700;900',
}]
}
}),
], ],
define: { 'process.env': {} }, define: { 'process.env': {} },
resolve: { resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
} }
}) })