Files
decision/frontend/app/stores/decisions.ts
Yvv 3cb1754592 Sprint 4 : decisions et mandats -- workflow complet + vote integration
Backend: 7 nouveaux endpoints (advance, assign, revoke, create-vote-session),
services enrichis avec creation de sessions de vote, assignation de mandataire
et revocation. 35 nouveaux tests (104 total). Frontend: store mandates, page
cadrage decisions, detail mandats, composants DecisionWorkflow, DecisionCadrage,
DecisionCard, MandateTimeline, MandateCard. Documentation mise a jour.

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

257 lines
6.6 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
}
export interface DecisionUpdate {
title?: string
description?: string | null
context?: string | null
decision_type?: string
voting_protocol_id?: string | null
}
export interface DecisionStepCreate {
step_type: string
title?: string | null
description?: 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 === 'qualification' || d.status === 'review' || d.status === 'voting',
)
},
completedDecisions: (state): Decision[] => {
return state.list.filter(d => d.status === 'executed' || 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
}
},
/**
* Update an existing decision.
*/
async update(id: string, data: DecisionUpdate) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Decision>(`/decisions/${id}`, {
method: 'PUT',
body: data,
})
if (this.current?.id === id) this.current = updated
const idx = this.list.findIndex(d => d.id === id)
if (idx >= 0) this.list[idx] = updated
return updated
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la mise a jour de la decision'
throw err
}
},
/**
* Delete a decision.
*/
async delete(id: string) {
this.error = null
try {
const { $api } = useApi()
await $api(`/decisions/${id}`, { method: 'DELETE' })
this.list = this.list.filter(d => d.id !== id)
if (this.current?.id === id) this.current = null
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la suppression de la decision'
throw err
}
},
/**
* Advance the decision to the next step in its workflow.
*/
async advance(id: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Decision>(`/decisions/${id}/advance`, {
method: 'POST',
})
if (this.current?.id === id) this.current = updated
const idx = this.list.findIndex(d => d.id === id)
if (idx >= 0) this.list[idx] = updated
return updated
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de l\'avancement de la decision'
throw err
}
},
/**
* Add a step to a decision.
*/
async addStep(id: string, step: DecisionStepCreate) {
this.error = null
try {
const { $api } = useApi()
const newStep = await $api<DecisionStep>(`/decisions/${id}/steps`, {
method: 'POST',
body: step,
})
if (this.current?.id === id) {
this.current.steps.push(newStep)
}
return newStep
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de l\'ajout de l\'etape'
throw err
}
},
/**
* Create a vote session for a specific step.
*/
async createVoteSession(decisionId: string, stepId: string) {
this.error = null
try {
const { $api } = useApi()
const result = await $api<any>(`/decisions/${decisionId}/steps/${stepId}/create-vote-session`, {
method: 'POST',
})
// Refresh decision to get updated step with vote_session_id
await this.fetchById(decisionId)
return result
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation de la session de vote'
throw err
}
},
/**
* Clear the current decision.
*/
clearCurrent() {
this.current = null
},
},
})