mirror of https://github.com/interlegis/sapl.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
3.3 KiB
88 lines
3.3 KiB
// frontend/src/__apps/painel/store/painelStore.js
|
|
//
|
|
// Store Pinia do painel. Substitui o store Vuex inline: um único ponto de
|
|
// entrada (`applyData`), alimentado pelo payload normalizado a partir do
|
|
// full-snapshot broadcast do backend (sapl/painel/views.py::
|
|
// build_dados_painel()) — não há mais eventos incrementais
|
|
// (`vote.update`/`stopwatch.update`); qualquer mudança de estado é
|
|
// republicada como um novo snapshot completo.
|
|
|
|
import { defineStore } from 'pinia'
|
|
import { DEFAULT_STATE, normalizePainelData } from '../ws/normalizePainel'
|
|
|
|
function deepClone (x) {
|
|
return JSON.parse(JSON.stringify(x))
|
|
}
|
|
|
|
export const usePainelStore = defineStore('painel', {
|
|
state: () => deepClone(DEFAULT_STATE),
|
|
|
|
getters: {
|
|
canRender: (s) => !!(s.sessao_aberta && s.painel_aberto),
|
|
|
|
// Getters usados pelo app `votacao` (apuração ao vivo a partir dos votos).
|
|
totalVotos: (s) => {
|
|
const totals = [
|
|
{ tipo: 'Sim', total: 0 },
|
|
{ tipo: 'Não', total: 0 },
|
|
{ tipo: 'Abstenção', total: 0 },
|
|
{ tipo: 'Não Votou', total: 0 }
|
|
]
|
|
s.parlamentares.forEach((p) => {
|
|
if (p.voto) {
|
|
const entry = totals.find((t) => t.tipo === p.voto)
|
|
if (entry) entry.total++
|
|
}
|
|
})
|
|
return totals
|
|
},
|
|
numPresentes: (s) => s.parlamentares.length,
|
|
totalVotados: (s) =>
|
|
s.parlamentares.filter((p) => p.voto && p.voto !== 'Não Votou').length
|
|
},
|
|
|
|
actions: {
|
|
// Aplica o payload completo (`type: "data"`) do WebSocket.
|
|
applyData (raw) {
|
|
const next = normalizePainelData(raw)
|
|
|
|
// flags simples
|
|
this.sessao_aberta = next.sessao_aberta
|
|
this.painel_aberto = next.painel_aberto
|
|
this.mostrar_voto = next.mostrar_voto
|
|
this.message = next.message
|
|
this.cronometro_ativo = next.cronometro_ativo
|
|
|
|
// objeto aninhado: patch (preserva reatividade dos campos)
|
|
Object.assign(this.sessao, next.sessao)
|
|
|
|
// cronômetros: só start/stop/reset explícitos (ver Cronometro.vue/
|
|
// CronometroList.vue) reagem a mudança de valor — nunca a um toggle
|
|
// por broadcast, que dispara em qualquer voto/matéria aberta, não só
|
|
// em ações de cronômetro.
|
|
this.cronometro_discurso = next.cronometro_discurso
|
|
this.cronometro_aparte = next.cronometro_aparte
|
|
this.cronometro_ordem = next.cronometro_ordem
|
|
this.cronometro_consideracoes = next.cronometro_consideracoes
|
|
this.registro_aberto = next.registro_aberto
|
|
|
|
// arrays: trocar referência (voto já vem aplicado por parlamentar
|
|
// dentro de next.parlamentares — get_votos() no backend já inlina o
|
|
// voto de cada presente, não há mais um mapa separado a reaplicar)
|
|
this.parlamentares = next.parlamentares
|
|
this.oradores = next.oradores
|
|
|
|
// objetos grandes: substituir
|
|
this.materia = next.materia
|
|
this.resultado = next.resultado
|
|
|
|
// tipos de resultado (app votacao): nunca vem no broadcast do
|
|
// WebSocket (ver GAP 3 do plano de merge) — só sobrescreve quando o
|
|
// payload realmente traz a lista, preservando o valor populado uma
|
|
// vez a partir do contexto inicial renderizado pelo Django.
|
|
if (next.tipos_resultado && next.tipos_resultado.length) {
|
|
this.tipos_resultado = next.tipos_resultado
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|