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>
257 lines
6.6 KiB
TypeScript
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
|
|
},
|
|
},
|
|
})
|