Mandats : origin→FK identité + nomination auto + boutons + tests intégration

- origin TEXT → origin_id UUID FK duniter_identities (migration e3f4a5b6c7d8)
- GET /auth/identities?q= : recherche d'identités par nom/adresse
- MandateCreate.nomination_mode : auto (auto-assign auteur), collective, postpone
- Wizard new.vue : champ origine = picker identité, checkbox "Démarrer maintenant"
- [id].vue : modal "Assigner" = search-picker (résout UUID vs adresse SS58), affiche
  origin_display_name + mandatee_display_name, inputs natifs (<input>/<textarea>)
- Erreurs API visibles dans l'UI (plus de catch silencieux)
- test_mandate_flows.py : 17 tests intégration SQLite réels (origin, nomination,
  assign, lifecycle, revocation, interactions croisées) — 241 tests total OK

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-04-25 20:48:27 +02:00
parent 3423ac2e7e
commit f56d84e76b
9 changed files with 883 additions and 427 deletions
+16 -78
View File
@@ -1,9 +1,3 @@
/**
* Mandates store: governance mandates and their lifecycle steps.
*
* Maps to the backend /api/v1/mandates endpoints.
*/
export interface MandateStep {
id: string
mandate_id: string
@@ -20,11 +14,13 @@ export interface MandateStep {
export interface Mandate {
id: string
title: string
origin: string | null
origin_id: string | null
origin_display_name: string | null
description: string | null
mandate_type: string
status: string
mandatee_id: string | null
mandatee_display_name: string | null
decision_id: string | null
starts_at: string | null
ends_at: string | null
@@ -35,9 +31,10 @@ export interface Mandate {
export interface MandateCreate {
title: string
origin?: string | null
origin_id?: string | null
description?: string | null
mandate_type: string
nomination_mode?: string
decision_id?: string | null
starts_at?: string | null
ends_at?: string | null
@@ -45,6 +42,7 @@ export interface MandateCreate {
export interface MandateUpdate {
title?: string
origin_id?: string | null
description?: string | null
mandate_type?: string
starts_at?: string | null
@@ -52,6 +50,7 @@ export interface MandateUpdate {
}
export interface MandateStepCreate {
step_order: number
step_type: string
title?: string | null
description?: string | null
@@ -73,31 +72,20 @@ export const useMandatesStore = defineStore('mandates', {
}),
getters: {
byStatus: (state) => {
return (status: string) => state.list.filter(m => m.status === status)
},
activeMandates: (state): Mandate[] => {
return state.list.filter(m => m.status === 'active')
},
completedMandates: (state): Mandate[] => {
return state.list.filter(m => m.status === 'completed')
},
byStatus: (state) => (status: string) => state.list.filter(m => m.status === status),
activeMandates: (state): Mandate[] => state.list.filter(m => m.status === 'active'),
completedMandates: (state): Mandate[] => state.list.filter(m => m.status === 'completed'),
},
actions: {
/**
* Fetch all mandates with optional filters.
*/
async fetchAll(params?: { mandate_type?: string; status?: string }) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const query: Record<string, string> = {}
if (params?.mandate_type) query.mandate_type = params.mandate_type
if (params?.status) query.status = params.status
this.list = await $api<Mandate[]>('/mandates/', { query })
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des mandats'
@@ -106,13 +94,9 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Fetch a single mandate by ID with all its steps.
*/
async fetchById(id: string) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
this.current = await $api<Mandate>(`/mandates/${id}`)
@@ -123,19 +107,12 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Create a new mandate.
*/
async create(payload: MandateCreate) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const mandate = await $api<Mandate>('/mandates/', {
method: 'POST',
body: payload,
})
const mandate = await $api<Mandate>('/mandates/', { method: 'POST', body: payload })
this.list.unshift(mandate)
return mandate
} catch (err: any) {
@@ -146,18 +123,11 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Update an existing mandate.
*/
async update(id: string, data: MandateUpdate) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Mandate>(`/mandates/${id}`, {
method: 'PUT',
body: data,
})
const updated = await $api<Mandate>(`/mandates/${id}`, { method: 'PUT', body: data })
if (this.current?.id === id) this.current = updated
const idx = this.list.findIndex(m => m.id === id)
if (idx >= 0) this.list[idx] = updated
@@ -168,12 +138,8 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Delete a mandate.
*/
async delete(id: string) {
this.error = null
try {
const { $api } = useApi()
await $api(`/mandates/${id}`, { method: 'DELETE' })
@@ -185,17 +151,11 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Advance the mandate to the next step in its workflow.
*/
async advance(id: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Mandate>(`/mandates/${id}/advance`, {
method: 'POST',
})
const updated = await $api<Mandate>(`/mandates/${id}/advance`, { method: 'POST' })
if (this.current?.id === id) this.current = updated
const idx = this.list.findIndex(m => m.id === id)
if (idx >= 0) this.list[idx] = updated
@@ -206,21 +166,12 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Add a step to a mandate.
*/
async addStep(id: string, step: MandateStepCreate) {
this.error = null
try {
const { $api } = useApi()
const newStep = await $api<MandateStep>(`/mandates/${id}/steps`, {
method: 'POST',
body: step,
})
if (this.current?.id === id) {
this.current.steps.push(newStep)
}
const newStep = await $api<MandateStep>(`/mandates/${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'
@@ -228,12 +179,8 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Assign a mandatee to the mandate.
*/
async assignMandatee(id: string, mandateeId: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Mandate>(`/mandates/${id}/assign`, {
@@ -250,17 +197,11 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Revoke the mandate.
*/
async revoke(id: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<Mandate>(`/mandates/${id}/revoke`, {
method: 'POST',
})
const updated = await $api<Mandate>(`/mandates/${id}/revoke`, { method: 'POST' })
if (this.current?.id === id) this.current = updated
const idx = this.list.findIndex(m => m.id === id)
if (idx >= 0) this.list[idx] = updated
@@ -271,9 +212,6 @@ export const useMandatesStore = defineStore('mandates', {
}
},
/**
* Clear the current mandate.
*/
clearCurrent() {
this.current = null
},