/** * Votes store: vote sessions, individual votes, and result computation. * * Maps to the backend /api/v1/votes endpoints. */ export interface Vote { id: string session_id: string voter_id: string vote_value: string nuanced_level: number | null comment: string | null signature: string signed_payload: string voter_wot_status: string voter_is_smith: boolean voter_is_techcomm: boolean is_active: boolean created_at: string } export interface VoteSession { id: string decision_id: string | null item_version_id: string | null voting_protocol_id: string wot_size: number smith_size: number techcomm_size: number starts_at: string ends_at: string status: string votes_for: number votes_against: number votes_total: number smith_votes_for: number techcomm_votes_for: number threshold_required: number result: string | null chain_recorded: boolean chain_tx_hash: string | null created_at: string } export interface VoteResult { session_id: string status: string votes_for: number votes_against: number votes_total: number wot_size: number smith_size: number techcomm_size: number smith_votes_for: number techcomm_votes_for: number threshold_required: number result: string smith_threshold: number | null smith_pass: boolean techcomm_threshold: number | null techcomm_pass: boolean } export interface VoteCreate { session_id: string vote_value: string nuanced_level?: number | null comment?: string | null signature: string signed_payload: string } interface VotesState { currentSession: VoteSession | null votes: Vote[] result: VoteResult | null loading: boolean error: string | null } export const useVotesStore = defineStore('votes', { state: (): VotesState => ({ currentSession: null, votes: [], result: null, loading: false, error: null, }), getters: { isSessionOpen: (state): boolean => { if (!state.currentSession) return false return state.currentSession.status === 'open' && new Date(state.currentSession.ends_at) > new Date() }, participationRate: (state): number => { if (!state.currentSession || state.currentSession.wot_size === 0) return 0 return (state.currentSession.votes_total / state.currentSession.wot_size) * 100 }, forPercentage: (state): number => { if (!state.currentSession || state.currentSession.votes_total === 0) return 0 return (state.currentSession.votes_for / state.currentSession.votes_total) * 100 }, }, actions: { /** * Fetch a vote session by ID with its votes and result. */ async fetchSession(sessionId: string) { this.loading = true this.error = null try { const { $api } = useApi() const [session, votes, result] = await Promise.all([ $api(`/votes/sessions/${sessionId}`), $api(`/votes/sessions/${sessionId}/votes`), $api(`/votes/sessions/${sessionId}/result`), ]) this.currentSession = session this.votes = votes this.result = result } catch (err: any) { this.error = err?.data?.detail || err?.message || 'Session de vote introuvable' } finally { this.loading = false } }, /** * Submit a vote to the current session. */ async submitVote(payload: VoteCreate) { this.loading = true this.error = null try { const { $api } = useApi() const vote = await $api(`/votes/sessions/${payload.session_id}/vote`, { method: 'POST', body: payload, }) // Update local state this.votes.push(vote) // Refresh session tallies and result if (this.currentSession) { const [session, result] = await Promise.all([ $api(`/votes/sessions/${payload.session_id}`), $api(`/votes/sessions/${payload.session_id}/result`), ]) this.currentSession = session this.result = result } return vote } catch (err: any) { this.error = err?.data?.detail || err?.message || 'Erreur lors du vote' throw err } finally { this.loading = false } }, /** * Clear the current session state. */ clearSession() { this.currentSession = null this.votes = [] this.result = null }, }, })