Files
decision/frontend/app/stores/decisions.ts
Yvv 25437f24e3 Sprint 1 : scaffolding complet de Glibredecision
Plateforme de decisions collectives pour Duniter/G1.
Backend FastAPI async + PostgreSQL (14 tables, 8 routers, 6 services,
moteur de vote avec formule d'inertie WoT/Smith/TechComm).
Frontend Nuxt 4 + Nuxt UI v3 + Pinia (9 pages, 5 stores).
Infrastructure Docker + Woodpecker CI + Traefik.
Documentation technique et utilisateur (15 fichiers).
Seed : Licence G1, Engagement Forgeron v2.0.0, 4 protocoles de vote.
30 tests unitaires (formules, mode params, vote nuance) -- tous verts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:46:11 +01:00

139 lines
3.3 KiB
TypeScript

/**
* Decisions store: decision processes and their steps.
*
* Maps to the backend /api/v1/decisions endpoints.
*/
export interface DecisionStep {
id: string
decision_id: string
step_order: number
step_type: string
title: string | null
description: string | null
status: string
vote_session_id: string | null
outcome: string | null
created_at: string
}
export interface Decision {
id: string
title: string
description: string | null
context: string | null
decision_type: string
status: string
voting_protocol_id: string | null
created_by_id: string | null
created_at: string
updated_at: string
steps: DecisionStep[]
}
export interface DecisionCreate {
title: string
description?: string | null
context?: string | null
decision_type: string
voting_protocol_id?: string | null
}
interface DecisionsState {
list: Decision[]
current: Decision | null
loading: boolean
error: string | null
}
export const useDecisionsStore = defineStore('decisions', {
state: (): DecisionsState => ({
list: [],
current: null,
loading: false,
error: null,
}),
getters: {
byStatus: (state) => {
return (status: string) => state.list.filter(d => d.status === status)
},
activeDecisions: (state): Decision[] => {
return state.list.filter(d => d.status === 'active' || d.status === 'in_progress')
},
completedDecisions: (state): Decision[] => {
return state.list.filter(d => d.status === 'completed' || d.status === 'closed')
},
},
actions: {
/**
* Fetch all decisions with optional filters.
*/
async fetchAll(params?: { decision_type?: string; status?: string }) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const query: Record<string, string> = {}
if (params?.decision_type) query.decision_type = params.decision_type
if (params?.status) query.status = params.status
this.list = await $api<Decision[]>('/decisions/', { query })
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des decisions'
} finally {
this.loading = false
}
},
/**
* Fetch a single decision by ID with all its steps.
*/
async fetchById(id: string) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
this.current = await $api<Decision>(`/decisions/${id}`)
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Decision introuvable'
} finally {
this.loading = false
}
},
/**
* Create a new decision.
*/
async create(payload: DecisionCreate) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const decision = await $api<Decision>('/decisions/', {
method: 'POST',
body: payload,
})
this.list.unshift(decision)
return decision
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation de la decision'
throw err
} finally {
this.loading = false
}
},
/**
* Clear the current decision.
*/
clearCurrent() {
this.current = null
},
},
})