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>
229 lines
5.6 KiB
TypeScript
229 lines
5.6 KiB
TypeScript
/**
|
|
* Protocols store: voting protocols and formula configurations.
|
|
*
|
|
* Maps to the backend /api/v1/protocols endpoints.
|
|
*/
|
|
|
|
export interface FormulaConfig {
|
|
id: string
|
|
name: string
|
|
description: string | null
|
|
duration_days: number
|
|
majority_pct: number
|
|
base_exponent: number
|
|
gradient_exponent: number
|
|
constant_base: number
|
|
smith_exponent: number | null
|
|
techcomm_exponent: number | null
|
|
nuanced_min_participants: number | null
|
|
nuanced_threshold_pct: number | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface VotingProtocol {
|
|
id: string
|
|
name: string
|
|
description: string | null
|
|
vote_type: string
|
|
formula_config_id: string
|
|
mode_params: string | null
|
|
is_meta_governed: boolean
|
|
created_at: string
|
|
formula_config: FormulaConfig
|
|
}
|
|
|
|
export interface ProtocolCreate {
|
|
name: string
|
|
description: string | null
|
|
vote_type: string
|
|
formula_config_id: string
|
|
}
|
|
|
|
export interface FormulaCreate {
|
|
name: string
|
|
description?: string | null
|
|
duration_days: number
|
|
majority_pct: number
|
|
base_exponent: number
|
|
gradient_exponent: number
|
|
constant_base: number
|
|
smith_exponent?: number | null
|
|
techcomm_exponent?: number | null
|
|
nuanced_min_participants?: number | null
|
|
nuanced_threshold_pct?: number | null
|
|
}
|
|
|
|
export interface SimulateParams {
|
|
wot_size: number
|
|
total_votes: number
|
|
majority_pct: number
|
|
base_exponent: number
|
|
gradient_exponent: number
|
|
constant_base: number
|
|
smith_wot_size?: number
|
|
smith_exponent?: number
|
|
techcomm_size?: number
|
|
techcomm_exponent?: number
|
|
}
|
|
|
|
export interface SimulateResult {
|
|
threshold: number
|
|
smith_threshold: number | null
|
|
techcomm_threshold: number | null
|
|
inertia_factor: number
|
|
required_ratio: number
|
|
}
|
|
|
|
interface ProtocolsState {
|
|
protocols: VotingProtocol[]
|
|
formulas: FormulaConfig[]
|
|
currentProtocol: VotingProtocol | null
|
|
loading: boolean
|
|
error: string | null
|
|
}
|
|
|
|
export const useProtocolsStore = defineStore('protocols', {
|
|
state: (): ProtocolsState => ({
|
|
protocols: [],
|
|
formulas: [],
|
|
currentProtocol: null,
|
|
loading: false,
|
|
error: null,
|
|
}),
|
|
|
|
getters: {
|
|
binaryProtocols: (state): VotingProtocol[] => {
|
|
return state.protocols.filter(p => p.vote_type === 'binary')
|
|
},
|
|
nuancedProtocols: (state): VotingProtocol[] => {
|
|
return state.protocols.filter(p => p.vote_type === 'nuanced')
|
|
},
|
|
metaGovernedProtocols: (state): VotingProtocol[] => {
|
|
return state.protocols.filter(p => p.is_meta_governed)
|
|
},
|
|
},
|
|
|
|
actions: {
|
|
/**
|
|
* Fetch all voting protocols with their formula configurations.
|
|
*/
|
|
async fetchProtocols(params?: { vote_type?: string }) {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
const query: Record<string, string> = {}
|
|
if (params?.vote_type) query.vote_type = params.vote_type
|
|
|
|
this.protocols = await $api<VotingProtocol[]>('/protocols/', { query })
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des protocoles'
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Fetch all formula configurations.
|
|
*/
|
|
async fetchFormulas() {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
this.formulas = await $api<FormulaConfig[]>('/protocols/formulas')
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des formules'
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Fetch a single protocol by ID.
|
|
*/
|
|
async fetchProtocolById(id: string) {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
this.currentProtocol = await $api<VotingProtocol>(`/protocols/${id}`)
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Protocole introuvable'
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Create a new voting protocol.
|
|
*/
|
|
async createProtocol(data: ProtocolCreate) {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
const protocol = await $api<VotingProtocol>('/protocols/', {
|
|
method: 'POST',
|
|
body: data,
|
|
})
|
|
this.protocols.push(protocol)
|
|
return protocol
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation du protocole'
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Create a new formula configuration.
|
|
*/
|
|
async createFormula(data: FormulaCreate) {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
const formula = await $api<FormulaConfig>('/protocols/formulas', {
|
|
method: 'POST',
|
|
body: data,
|
|
})
|
|
this.formulas.push(formula)
|
|
return formula
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation de la formule'
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Simulate formula computation on the backend.
|
|
*/
|
|
async simulate(params: SimulateParams) {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const { $api } = useApi()
|
|
return await $api<SimulateResult>('/protocols/simulate', {
|
|
method: 'POST',
|
|
body: params,
|
|
})
|
|
} catch (err: any) {
|
|
this.error = err?.data?.detail || err?.message || 'Erreur lors de la simulation'
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
},
|
|
})
|