Move brainblast vue app to ui repository

This commit is contained in:
2024-03-16 21:17:43 +00:00
parent ac2fb16703
commit 4c42f15dc6
34 changed files with 8 additions and 0 deletions

35
ui/README.md Normal file
View File

@ -0,0 +1,35 @@
# brain-blast
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```

13
ui/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="fr">
<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>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

8
ui/jsconfig.json Normal file
View File

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

3098
ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
ui/package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "brain-blast",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"@mdi/font": "^7.4.47",
"mqtt": "^5.3.5",
"roboto-fontface": "^0.10.0",
"vue": "^3.4.19",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^8.0.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.3",
"unplugin-fonts": "^1.1.1",
"vite": "^5.1.4",
"vite-plugin-vuetify": "^2.0.1"
}
}

BIN
ui/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

27
ui/src/App.vue Normal file
View File

@ -0,0 +1,27 @@
<template>
<v-app>
<BrainBlastBar />
<GameStatus />
<v-main>
<RouterView />
</v-main>
<v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer>
</v-app>
</template>
<script setup>
import BrainBlastBar from '@/components/BrainBlastBar.vue'
import GameStatus from '@/components/GameStatus.vue'
</script>
<style scoped>
.footer {
position: fixed; /* Fixe le footer en bas de la page */
bottom: 0; /* Aligne le footer en bas de la page */
left: 0; /* Aligne le footer à gauche */
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 */
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

1
ui/src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1,27 @@
<template>
<v-app-bar :elevation="5">
<RouterMenu />
<v-app-bar-title>Brain Blast</v-app-bar-title>
<template v-slot:append>
<v-btn icon @click="toggleTheme">
<v-icon>{{ darkTheme ? 'mdi-white-balance-sunny' : 'mdi-moon-waning-crescent' }}</v-icon>
</v-btn>
</template>
</v-app-bar>
</template>
<script setup>
import { ref } from 'vue'
import { useTheme } from 'vuetify'
import RouterMenu from '@/components/RouterMenu.vue'
const theme = useTheme()
const darkTheme = ref(true)
function toggleTheme() {
darkTheme.value = !darkTheme.value
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<v-card tile outlined class="card">
<v-card-title class="card__title primary">
<v-icon left class="white--text pr-5 pl-2" size="50">mdi-camera-control</v-icon>
Contrôles
</v-card-title>
<v-container class="text-center">
<v-row justify="center">
<v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="240" height="130" class="btn xs12 sm6 md3" topic="/display/control" message="previous">
<v-icon left size="100">mdi-skip-previous</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mt-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3" topic="/display/control" message="next">
<v-icon left size="100">mdi-skip-next</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3" topic="/display/control" message="pause">
<v-icon left size="100">mdi-pause</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="5" class="mb-4">
<mqtt-button width="240" height="130" class="btn card xs12 sm6 md3 " topic="/display/control" message="play">
<v-icon left size="100">mdi-play</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script setup>
import MqttButton from './MqttButton.vue';
</script>

View File

@ -0,0 +1,30 @@
<template>
<v-card tile outlined class="card">
<v-card-title class="card__title feedback">
<v-icon left class="white--text pr-5 pl-2" size="50">mdi-play-network-outline</v-icon>
Solution
</v-card-title>
<v-container class="text-center">
<v-row justify="center">
<v-container class="text-center">
<!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
<v-img width="450" src="https://c4.wallpaperflare.com/wallpaper/908/893/291/funny-middle-finger-black-background-wallpaper-preview.jpg" style="margin: 0 auto;"></v-img>
</v-container>
</v-row>
</v-container>
</v-card>
</template>
<style>
@media (min-width: 1024px) {
.image-container {
width: 300px;
overflow: hidden; /* Pour masquer le dépassement de l'image */
border: 1px solid #ccc; /* Bordure de l'image */
}
.image-container img {
width: 100%; /* Pour remplir complètement le conteneur */
height: auto; /* Pour maintenir le ratio d'aspect de l'image */
display: block; /* Pour éviter l'espace réservé pour les images */
}
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<v-card tile outlined class="card" color="">
<v-card-title class="card__title primary">
<v-icon left class="white--text pr-5 pl-2" size="50">mdi-music-box-multiple</v-icon>
Soundboard
</v-card-title>
<v-container class="text-center">
<v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="130" topic="/sound/playsound" message="good-response" rounded>
<v-icon size="100">mdi-check-circle-outline</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="130" topic="/sound/playsound" message="bad-response" rounded>
<v-icon size="100">mdi-close-circle-outline</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="4" class="mt-4">
<mqtt-button class="btn" width="200" height="130" topic="/sound/playsound" message="timer" rounded>
<v-icon size="100">mdi-timer-outline</v-icon>
</mqtt-button>
</v-col>
</v-row>
<v-row justify="center">
<v-col cols="12" sm="6" md="4" class="mb-4">
<mqtt-button class="btn" width="220" height="130" topic="/sound/playsound" message="applause" rounded>
<v-icon size="100">mdi-human-handsup</v-icon>
</mqtt-button>
</v-col>
<v-col cols="12" sm="6" md="s" class="mb-4">
<mqtt-button class="btn" width="220" height="130" topic="/sound/playsound" message="bell" rounded>
<v-icon size="100">mdi-bell-outline</v-icon>
</mqtt-button>
</v-col>
</v-row>
</v-container>
</v-card>
</template>
<script setup>
import MqttButton from './MqttButton.vue';
</script>

View File

@ -0,0 +1,9 @@
<template>
<v-navigation-drawer>
<v-list-item title="Brain Blast" subtitle="The cultural quizzzz"></v-list-item>
<v-divider></v-divider>
<v-list-item link title="List Item 1"></v-list-item>
<v-list-item link title="List Item 2"></v-list-item>
<v-list-item link title="List Item 3"></v-list-item>
</v-navigation-drawer>
</template>

View File

@ -0,0 +1,23 @@
<template>
<div>
<h1>Console MQTT</h1>
<div v-for="(message, index) in messages" :key="index">{{ message }}</div>
</div>
</template>
<script>
import { subscribeToTopic } from '@/services/mqttService'
export default {
data() {
return {
messages: [] // Initialiser un tableau pour stocker les messages MQTT
}
},
created() {
subscribeToTopic('#', (topic, message) => {
this.messages.push(`Topic: ${topic}, Message: ${message}`) // Ajouter le message à la liste des messages
}) // S'abonner à tous les topics MQTT
}
}
</script>

View File

@ -0,0 +1,40 @@
<template>
<div>
<h1>Publier sur MQTT</h1>
<select v-model="selectedTopic">
<option v-for="topic in topics" :key="topic">{{ topic }}</option>
</select>
<input type="text" v-model="message" placeholder="Saisissez votre message" />
<button @click="publishMessage">Publier sur MQTT</button>
</div>
</template>
<script>
import { publishMessage } from '@/services/mqttService'
export default {
data() {
return {
message: '', // Initialiser la variable message
selectedTopic: 'topic1',
topics: [
'topic1',
'topic2',
'topic3',
'topic4',
'topic5',
'topic6',
'topic7',
'topic8',
'topic9',
'topic10'
] // Liste des topics
}
},
methods: {
publishMessage() {
publishMessage(this.selectedTopic, this.message)
}
}
}
</script>

View File

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

View File

@ -0,0 +1,24 @@
<template>
<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-list>
<v-list-item v-for="route in routes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
</v-list>
</v-menu>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const routes = router.options.routes
let menu = ref(false)
</script>
<style scoped>
.menu-below-bar {
margin-top: 48px; /* La hauteur de la barre d'application */
}
</style>

8
ui/src/config.js Normal file
View File

@ -0,0 +1,8 @@
// Fichier vide, regarde config.js.example pour personaliser ce fichier.
// Note de dev : Normalement ce fichier ne devrait plus avoir de
// modifications
// config.js
export default {
mqttBrokerUrl: 'ws://localhost:9001'
};

4
ui/src/config.js.example Normal file
View File

@ -0,0 +1,4 @@
// config.js
export default {
mqttBrokerUrl: 'ws://localhost:9001'
};

9
ui/src/main.js Normal file
View File

@ -0,0 +1,9 @@
import { registerPlugins } from '@/plugins'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
registerPlugins(app)
app.mount('#app')

3
ui/src/plugins/README.md Normal file
View File

@ -0,0 +1,3 @@
# Plugins
Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.

14
ui/src/plugins/index.js Normal file
View File

@ -0,0 +1,14 @@
/**
* plugins/index.js
*
* Automatically included in `./src/main.js`
*/
// Plugins
import vuetify from './vuetify'
import router from './router'
export function registerPlugins (app) {
app.use(vuetify)
app.use(router)
}

38
ui/src/plugins/router.js Normal file
View File

@ -0,0 +1,38 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'Home',
component: HomeView
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/views/AboutView.vue')
},
{
path: '/game/control',
name: 'Game Control (Présentateur)',
component: () => import('@/views/GameControl.vue')
},
{
path: '/game/display',
name: 'Game Display (Projection)',
component: () => import('@/views/GameDisplay.vue')
},
{
path: '/mqtt-debugger',
name: 'Debugger MQTT',
component: () => import('@/views/MQTTDebugView.vue')
}
]
})
export default router

40
ui/src/plugins/vuetify.js Normal file
View File

@ -0,0 +1,40 @@
/**
* plugins/vuetify.js
*
* Framework documentation: https://vuetifyjs.com`
*/
// Styles
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
// Composables
import { createVuetify } from 'vuetify'
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
export default createVuetify({
theme: {
themes: {
light: {
background: '#212121',
primary: '#cc0000',
controls: '#cc0000',
soundboard: '#9A2779',
secondary: '#b0bec5',
feedback: '#2E7D32',
accent: '#8c9eff',
error: '#b71c1c',
},
dark: {
background: '#121212',
primary: '#2979FF',
controls: '#AB47B',
secondary: '#90a4ae',
feedback: '#2E7D32',
accent: '#8c9eff',
error: '#b71c1c',
},
},
defaultTheme: 'dark',
}
})

View File

@ -0,0 +1,19 @@
import mqtt from 'mqtt'
import config from '@/config.js'
const mqttBrokerUrl = config.mqttBrokerUrl
// Créer une instance de client MQTT
const client = mqtt.connect(mqttBrokerUrl)
// Fonction pour publier un message sur un topic MQTT
export function publishMessage(topic, message) {
client.publish(topic, message)
}
// Fonction pour s'abonner à un topic MQTT et écouter les messages entrants
export function subscribeToTopic(topic, callback) {
client.subscribe(topic)
client.on('message', (receivedTopic, message) => {
callback(receivedTopic.toString(), message.toString())
})
}

View File

@ -0,0 +1,24 @@
<template>
<div class="about">
<h1>This is an about page</h1>
<hr/>
<p>
Exemple d'une page
</p><p>
Ou il y a un scroll
</p><p>
Pour mettre en valeur leu footer fixe en bas de page
</p><p>
Cette page n'a aucun interet et pourra etre supprimée
</p>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<v-container>
<v-row no-gutters>
<v-col class="align-start">
<card-control />
</v-col>
<v-col class="pl-3">
<card-soundboard />
</v-col>
</v-row>
</v-container>
<v-container>
<v-row no-gutters>
<v-col>
<card-solution />
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import CardSolution from '@/components/CardSolution.vue'
import CardControl from '@/components/CardControl.vue'
import CardSoundboard from '@/components/CardSoundboard.vue';
</script>
<style>
@media (min-width: 1024px) {
.card__title.primary {
background-color: #2979FF; /* Changez la couleur en fonction de votre thème */
}
.card__title.feedback {
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
}
.btn{
border-radius:30px!important;
background-color: #2979FF; /* Changez la couleur en fonction de votre thème */
}
}
</style>

View File

11
ui/src/views/HomeView.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<v-img width="1645" src="../assets/BrainBlast-For-HomeView-Alpha.png"></v-img>
</template>
<script>
export default {
name: 'VotreComposant', // Assurez-vous de donner un nom à votre composant
// Votre logique JavaScript ici, par exemple des méthodes, des données, etc.
};
</script>

View File

@ -0,0 +1,21 @@
<template>
<div>
<!-- Zone pour publier sur MQTT -->
<PublishMQTTComponent />
<!-- Zone pour afficher la console MQTT -->
<MQTTConsoleComponent />
</div>
</template>
<script>
import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer le composant pour publier sur MQTT
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT
export default {
components: {
PublishMQTTComponent, // Composant pour publier sur MQTT
MQTTConsoleComponent // Composant pour la console MQTT
}
}
</script>

32
ui/vite.config.js Normal file
View File

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