Sprint 3 : protocoles de vote et boite a outils
Backend: - Sessions de vote : list, close, tally, threshold details, auto-expiration - Protocoles : update, simulate, meta-gouvernance, formulas CRUD - Service vote enrichi : close_session, get_threshold_details, nuanced breakdown - Schemas : ThresholdDetailOut, VoteResultOut, FormulaSimulationRequest/Result - WebSocket broadcast sur chaque vote + fermeture session - 25 nouveaux tests (threshold details, close, nuanced, simulation) Frontend: - 5 composants vote : VoteBinary, VoteNuanced, ThresholdGauge, FormulaDisplay, VoteHistory - 3 composants protocoles : ProtocolPicker, FormulaEditor, ModeParamsDisplay - Simulateur de formules interactif (page /protocols/formulas) - Page detail protocole (/protocols/[id]) - Composable useWebSocket (live updates) - Composable useVoteFormula (calcul client-side reactif) - Integration KaTeX pour rendu LaTeX des formules Documentation: - API reference : 8 nouveaux endpoints documentes - Formules : tables d'inertie, parametres detailles, simulation API - Guide vote : vote binaire/nuance, jauge, historique, simulateur, meta-gouvernance 55 tests passes (+ 1 skipped), 126 fichiers total. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,17 @@ export interface VoteResult {
|
||||
techcomm_pass: boolean
|
||||
}
|
||||
|
||||
export interface ThresholdDetails {
|
||||
wot_threshold: number
|
||||
smith_threshold: number | null
|
||||
techcomm_threshold: number | null
|
||||
wot_pass: boolean
|
||||
smith_pass: boolean | null
|
||||
techcomm_pass: boolean | null
|
||||
inertia_factor: number
|
||||
required_ratio: number
|
||||
}
|
||||
|
||||
export interface VoteCreate {
|
||||
session_id: string
|
||||
vote_value: string
|
||||
@@ -71,10 +82,27 @@ export interface VoteCreate {
|
||||
signed_payload: string
|
||||
}
|
||||
|
||||
export interface VoteSessionCreate {
|
||||
decision_id?: string | null
|
||||
item_version_id?: string | null
|
||||
voting_protocol_id: string
|
||||
wot_size?: number
|
||||
smith_size?: number
|
||||
techcomm_size?: number
|
||||
}
|
||||
|
||||
export interface SessionFilters {
|
||||
status?: string
|
||||
voting_protocol_id?: string
|
||||
decision_id?: string
|
||||
}
|
||||
|
||||
interface VotesState {
|
||||
currentSession: VoteSession | null
|
||||
votes: Vote[]
|
||||
result: VoteResult | null
|
||||
thresholdDetails: ThresholdDetails | null
|
||||
sessions: VoteSession[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
}
|
||||
@@ -84,6 +112,8 @@ export const useVotesStore = defineStore('votes', {
|
||||
currentSession: null,
|
||||
votes: [],
|
||||
result: null,
|
||||
thresholdDetails: null,
|
||||
sessions: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
}),
|
||||
@@ -166,6 +196,94 @@ export const useVotesStore = defineStore('votes', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch threshold details for a session.
|
||||
*/
|
||||
async fetchThresholdDetails(sessionId: string) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const { $api } = useApi()
|
||||
this.thresholdDetails = await $api<ThresholdDetails>(
|
||||
`/votes/sessions/${sessionId}/threshold`,
|
||||
)
|
||||
} catch (err: any) {
|
||||
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des details du seuil'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Close a vote session.
|
||||
*/
|
||||
async closeSession(sessionId: string) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const { $api } = useApi()
|
||||
const session = await $api<VoteSession>(`/votes/sessions/${sessionId}/close`, {
|
||||
method: 'POST',
|
||||
})
|
||||
this.currentSession = session
|
||||
|
||||
// Refresh result after closing
|
||||
this.result = await $api<VoteResult>(`/votes/sessions/${sessionId}/result`)
|
||||
} catch (err: any) {
|
||||
this.error = err?.data?.detail || err?.message || 'Erreur lors de la fermeture de la session'
|
||||
throw err
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a list of vote sessions with optional filters.
|
||||
*/
|
||||
async fetchSessions(filters?: SessionFilters) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const { $api } = useApi()
|
||||
const query: Record<string, string> = {}
|
||||
if (filters?.status) query.status = filters.status
|
||||
if (filters?.voting_protocol_id) query.voting_protocol_id = filters.voting_protocol_id
|
||||
if (filters?.decision_id) query.decision_id = filters.decision_id
|
||||
|
||||
this.sessions = await $api<VoteSession[]>('/votes/sessions', { query })
|
||||
} catch (err: any) {
|
||||
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des sessions'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new vote session.
|
||||
*/
|
||||
async createSession(data: VoteSessionCreate) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const { $api } = useApi()
|
||||
const session = await $api<VoteSession>('/votes/sessions', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})
|
||||
this.sessions.push(session)
|
||||
return session
|
||||
} catch (err: any) {
|
||||
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation de la session'
|
||||
throw err
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the current session state.
|
||||
*/
|
||||
@@ -173,6 +291,7 @@ export const useVotesStore = defineStore('votes', {
|
||||
this.currentSession = null
|
||||
this.votes = []
|
||||
this.result = null
|
||||
this.thresholdDetails = null
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user