Browse Source

WIP - VueJS components

websockets-2025
Edward Ribeiro 2 weeks ago
parent
commit
c7d1d09741
  1. 26
      frontend/src/__apps/painel/main.js
  2. 71
      frontend/src/components/PainelHeader.vue
  3. 36
      frontend/src/components/PainelParlamentares.vue
  4. 117
      frontend/src/components/StopWatch.vue
  5. 9
      sapl/painel/consumers.py
  6. 42
      sapl/templates/painel/painel_v2.html

26
frontend/src/__apps/painel/main.js

@ -2,6 +2,14 @@ import './scss/painel.scss'
// main.js (Vue 2)
import Vue from 'vue'
import StopWatch from '../../components/StopWatch.vue'
import PainelHeader from '../../components/PainelHeader.vue'
import PainelParlamentares from '../../components/PainelParlamentares.vue'
// register components
Vue.component('cronometro', StopWatch)
Vue.component('painel-header', PainelHeader)
Vue.component('painel-parlamentares', PainelParlamentares)
new Vue({
el: '#painel',
@ -45,12 +53,18 @@ new Vue({
const msg = JSON.parse(evt.data)
// SETUP STATE
console.log(`FROM VUEJS: ${evt.data}`) // DEBUG
this.state.parlamentares = msg.presentes
//TODO: group in a single sessao object
this.state.sessao_plenaria = msg.sessao.sessao_plenaria
this.state.sessao_plenaria_data = msg.sessao.sessao_plenaria_data
this.state.sessao_plenaria_hora_inicio = msg.sessao.sessao_plenaria_hora_inicio
this.state.brasao = msg.sessao.brasao
const parlamentaresInstance = this.$refs.parlamentares
parlamentaresInstance.parlamentares = msg.parlamentares
//TODO: group in a single SessaoPlenaria object
const headerInstance = this.$refs.painelHeader;
//TODO: setup as child's props?
headerInstance.sessao_plenaria = msg.sessao.sessao_plenaria
headerInstance.sessao_plenaria_data = msg.sessao.sessao_plenaria_data
headerInstance.sessao_plenaria_hora_inicio = msg.sessao.sessao_plenaria_hora_inicio
headerInstance.brasao = msg.sessao.brasao
this.state.oradores = msg.oradores
this.state.materia_legislativa_ementa = msg.mensagem_legislativa_ementa

71
frontend/src/components/PainelHeader.vue

@ -0,0 +1,71 @@
<template>
<div>
<div class="d-flex justify-content-center">
<h1 id="sessao_plenaria" class="title text-title">{{ sessao_plenaria }} </h1>
</div>
<div class="row ">
<div class="col text-center">
<span id="sessao_plenaria_data" class="text-value"> Data Início: {{ sessao_plenaria_data }} </span>
</div>
<div class="col text-center">
<span id="sessao_plenaria_hora_inicio" class="text-value"> Hora Início: {{ sessao_plenaria_hora_inicio }} </span>
</div>
</div>
<div class="row justify-content-center">
<div class="col-1">
<img v-bind:src="brasao" id="logo-painel" class="logo-painel" alt=""/>
</div>
</div>
<div class="row justify-content-center">
<h2 class="text-danger"><span id="message">{{ message }}</span></h2>
</div>
<div class="row">
<div class="col text-center"><span class="text-value data-hora" id="date">{{ data_atual }}</span></div>
<div class="col text-center"><span class="text-value data-hora" id="relogio">{{ relogio }}</span></div>
</div>
</div>
</template>
<script>
export default {
name: 'PainelHeader',
props: {
},
data() {
return {
sessao_plenaria: "Sessao Plenaria Teste",
sessao_plenaria_data: "22/10/2025",
sessao_plenaria_hora_inicio: "13:30",
brasao: "",
message: "painel fechado",
data_atual: "",
relogio: "",
currentDateTimeId: null, // stores the id returned by setInterval()
}
},
methods: {
startCurrentDateTime() {
this.data_atual = moment().utcOffset(-3).format("DD/MM/YY"); // RECOVER UTC OFFSET!!!!
this.currentDateTimeId = setInterval(() => {
this.relogio = moment.utc().utcOffset(-3).format("HH:mm:ss");
}, 500);
},
beforeDestroy() {
// Clear the interval before the component is destroyed
if (this.currentDateTimeId) {
clearInterval(this.currentDateTimeId);
console.log('currentDateTimeId Interval cleared.');
}
},
},
mounted() {
console.log('PainelHeader component mounted');
this.startCurrentDateTime();
}
}
</script>

36
frontend/src/components/PainelParlamentares.vue

@ -0,0 +1,36 @@
<template>
<div class="col-md-4">
<div class="text-center painel">
<h2 class="text-subtitle">Parlamentares</h2>
<span id="parlamentares" class="text-value text-center"></span>
<table id="parlamentares_list">
<tr v-for="p in parlamentares" :key="p.parlamentar_id" class="text-value text-center">
<td style="padding-right:20px; color:yellow" > {{ p.nome_parlamentar }}</td>
<td style="padding-right:20px; color:yellow"> {{ p.filiacao }}</td>
<td style="padding-right:20px; color:yellow"></td>
</tr>
</table>
</div>
</div>
</template>
<script>
export default {
name: 'PainelParlamentares',
data() {
return {
parlamentares: [],
};
},
mounted() {
console.log('PainelParlamentares mounted');
},
beforeDestroy() {
},
};
</script>
<style scoped>
/* Optional styling */
</style>

117
frontend/src/components/StopWatch.vue

@ -0,0 +1,117 @@
<template>
<div class="stopwatch-container">
<div class="stopwatch-card">
<h1>Stopwatch Timer</h1>
<div class="time-input">
<label>Set Time (seconds)</label>
<input
type="number"
v-model.number="initialTime"
:disabled="isRunning"
min="0"
/>
</div>
<div class="time-display" :class="{ 'time-up': time === 0 }">
{{ formatTime(time) }}
</div>
<div class="controls">
<button @click="handleStartStop" :class="isRunning ? 'pause-btn' : 'start-btn'">
{{ isRunning ? 'Pause' : 'Start' }}
</button>
<button @click="handleReset" class="reset-btn">
Reset
</button>
</div>
<div v-if="time === 0" class="alert">
<p>Time's up!</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Stopwatch',
data() {
return {
time: 300,
isRunning: false,
initialTime: 300,
intervalId: null
}
},
methods: {
handleStartStop() {
this.isRunning = !this.isRunning;
if (this.isRunning) {
this.intervalId = setInterval(() => {
if (this.time > 0) {
this.time--;
} else {
this.isRunning = false;
clearInterval(this.intervalId);
this.playSound();
}
}, 1000);
} else {
clearInterval(this.intervalId);
}
},
handleReset() {
this.isRunning = false;
clearInterval(this.intervalId);
this.time = this.initialTime;
},
playSound() {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
},
formatTime(seconds) {
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hrs > 0) {
return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
},
watch: {
initialTime(newVal) {
if (!this.isRunning) {
this.time = newVal;
}
}
},
beforeDestroy() {
clearInterval(this.intervalId);
}
}
</script>
<style scoped>
/* Add your own styles here */
</style>

9
sapl/painel/consumers.py

@ -34,7 +34,7 @@ def get_dados_painel(pk: int) -> dict:
etapa_sessao='expediente').values_list('parlamentar_id',
'nome_parlamentar',
'filiacao', )
presentes = [dict(zip(['parlamentar_id', 'nome_parlamentar', 'filiacao'], p)) for p in presentes]
parlamentares = [dict(zip(['parlamentar_id', 'nome_parlamentar', 'filiacao'], p)) for p in presentes]
oradores = SessaoOradorView.objects.filter(sessao_plenaria_id=pk,
etapa_sessao='expediente').values_list('ordem_pronunciamento',
@ -42,7 +42,9 @@ def get_dados_painel(pk: int) -> dict:
)
oradores = [dict(zip(['ordem_pronunciamento', 'nome_parlamentar'], o)) for o in oradores]
votos = SessaoMateriaVotacaoView.objects.get(sessao_plenaria_id=pk, etapa_sessao='expediente', materia_id=31919)
votos = SessaoMateriaVotacaoView.objects.get(sessao_plenaria_id=pk,
etapa_sessao='expediente',
materia_id=31919)
# TODO: recover stopwatch state from DB/Cache
stopwatch = {
@ -55,7 +57,6 @@ def get_dados_painel(pk: int) -> dict:
dados_sessao = {
"type": "data",
"data": {}, # legacy
"sessao": {
"status_painel": sessao.painel_aberto,
"brasao": brasao,
@ -68,7 +69,7 @@ def get_dados_painel(pk: int) -> dict:
"tema_solene": sessao.tema_solene,
"status_painel": False, # TODO: recover from DB **and** move status to other place.
},
"presentes": presentes,
"parlamentares": parlamentares,
"oradores": oradores,
"votacao": votos.total_votos, # TODO unify into single json
"votos_parlamentar": votos.votos_parlamentares, # TODO: unify into single JSON

42
sapl/templates/painel/painel_v2.html

@ -46,49 +46,12 @@
<audio type="hidden" id="alarm" src="{% webpack_static 'audio/ring.mp3' %}"></audio>
<div class="d-flex justify-content-center">
<h1 id="sessao_plenaria" class="title text-title">[[ state.sessao_plenaria ]] </h1>
</div>
<div class="row ">
<div class="col text-center">
<span id="sessao_plenaria_data" class="text-value">[[ state.sessao_plenaria_data ]] </span>
</div>
<div class="col text-center">
<span id="sessao_plenaria_hora_inicio" class="text-value"> [[ state.sessao_plenaria_data ]] </span>
</div>
</div>
<div class="row justify-content-center">
<div class="col-1">
<img src="" id="logo-painel" class="logo-painel" alt=""/>
</div>
</div>
<painel-header ref="painelHeader"></painel-header>
<div class="row justify-content-center">
<div class="row justify-content-center">
<h2 class="text-danger"><span id="message"></span></h2>
</div>
<div class="row">
<div class="col text-center"><span class="text-value data-hora" id="date"></span></div>
<div class="col text-center"><span class="text-value data-hora" id="relogio"></span></div>
</div>
<div class="d-flex justify-content-start">
<div class="col-md-4">
<div class="text-center painel">
<h2 class="text-subtitle">Parlamentares</h2>
<span id="parlamentares" class="text-value text-center"></span>
<table id="parlamentares_list">
<tr v-for="p in state.parlamentares" :key="p.parlamentar_id" class="text-value text-center">
<td style="padding-right:20px; color:yellow" > [[ p.nome_parlamentar ]]</td>
<td style="padding-right:20px; color:yellow"> [[ p.filiacao ]]</td>
<td style="padding-right:20px; color:yellow"></td>
</tr>
</table>
</div>
</div>
<painel-parlamentares ref="parlamentares"></painel-parlamentares>
<div class="d-flex col-md-8 painels">
<div class="col-md-6 text-center painel" id="aparecer_oradores">
<h2 class="text-subtitle">Oradores</h2>
@ -110,6 +73,7 @@
</button>
</div>
<div class="text-value" id="box_cronometros">
<!-- <cronometro></cronometro>-->
Discurso: <span id="cronometro_discurso"></span><br>
Aparte: <span id="cronometro_aparte"></span><br>
Questão de Ordem: <span id="cronometro_ordem"></span><br>

Loading…
Cancel
Save