Files
decision/frontend/app/stores/documents.ts
Yvv 62808b974d Composants engagement: GenesisBlock, InertiaSlider, MiniVoteBoard, EngagementCard, DocumentTuto
Backend: genesis_json sur Document, section_tag/inertia_preset/is_permanent_vote sur DocumentItem
Frontend: 5 nouveaux composants pour vue detail document enrichie
- GenesisBlock: sources, outils, synthese forum, contributeurs (depliable)
- InertiaSlider: visualisation inertie 4 niveaux avec params formule G/M
- MiniVoteBoard: tableau vote compact (barre seuil, pour/contre, participation)
- EngagementCard: carte item enrichie integrant vote + inertie + actions
- DocumentTuto: modal pedagogique vote permanent/inertie/seuils
Seed et page [slug] enrichis pour exploiter les nouveaux champs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 07:59:05 +01:00

286 lines
7.1 KiB
TypeScript

/**
* Documents store: reference documents, their items, and item versions.
*
* Maps to the backend /api/v1/documents endpoints.
*/
export interface DocumentItem {
id: string
document_id: string
position: string
item_type: string
title: string | null
current_text: string
voting_protocol_id: string | null
sort_order: number
section_tag: string | null
inertia_preset: string
is_permanent_vote: boolean
created_at: string
updated_at: string
}
export interface Document {
id: string
slug: string
title: string
doc_type: string
version: string
status: string
description: string | null
ipfs_cid: string | null
chain_anchor: string | null
genesis_json: string | null
created_at: string
updated_at: string
items_count: number
}
export interface ItemVersion {
id: string
item_id: string
version_number: number
proposed_text: string
rationale: string | null
diff: string | null
status: string
proposed_by: string | null
reviewed_by: string | null
created_at: string
updated_at: string
}
export interface DocumentCreate {
slug: string
title: string
doc_type: string
description?: string | null
version?: string
}
export interface VersionProposal {
proposed_text: string
rationale?: string | null
}
interface DocumentsState {
list: Document[]
current: Document | null
items: DocumentItem[]
versions: ItemVersion[]
loading: boolean
error: string | null
}
export const useDocumentsStore = defineStore('documents', {
state: (): DocumentsState => ({
list: [],
current: null,
items: [],
versions: [],
loading: false,
error: null,
}),
getters: {
byType: (state) => {
return (docType: string) => state.list.filter(d => d.doc_type === docType)
},
activeDocuments: (state): Document[] => {
return state.list.filter(d => d.status === 'active')
},
draftDocuments: (state): Document[] => {
return state.list.filter(d => d.status === 'draft')
},
},
actions: {
/**
* Fetch all documents with optional filters.
*/
async fetchAll(params?: { doc_type?: string; status?: string }) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const query: Record<string, string> = {}
if (params?.doc_type) query.doc_type = params.doc_type
if (params?.status) query.status = params.status
this.list = await $api<Document[]>('/documents/', { query })
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des documents'
} finally {
this.loading = false
}
},
/**
* Fetch a single document by slug and its items.
*/
async fetchBySlug(slug: string) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const [doc, items] = await Promise.all([
$api<Document>(`/documents/${slug}`),
$api<DocumentItem[]>(`/documents/${slug}/items`),
])
this.current = doc
this.items = items
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Document introuvable'
} finally {
this.loading = false
}
},
/**
* Create a new reference document.
*/
async createDocument(payload: DocumentCreate) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
const doc = await $api<Document>('/documents/', {
method: 'POST',
body: payload,
})
this.list.unshift(doc)
return doc
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la creation du document'
throw err
} finally {
this.loading = false
}
},
/**
* Fetch all versions for a specific item within a document.
*/
async fetchItemVersions(slug: string, itemId: string) {
this.loading = true
this.error = null
try {
const { $api } = useApi()
this.versions = await $api<ItemVersion[]>(
`/documents/${slug}/items/${itemId}/versions`,
)
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors du chargement des versions'
} finally {
this.loading = false
}
},
/**
* Propose a new version for a document item.
*/
async proposeVersion(slug: string, itemId: string, data: VersionProposal) {
this.error = null
try {
const { $api } = useApi()
const version = await $api<ItemVersion>(
`/documents/${slug}/items/${itemId}/versions`,
{
method: 'POST',
body: data,
},
)
this.versions.unshift(version)
return version
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de la proposition'
throw err
}
},
/**
* Accept a proposed version.
*/
async acceptVersion(slug: string, itemId: string, versionId: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<ItemVersion>(
`/documents/${slug}/items/${itemId}/versions/${versionId}/accept`,
{ method: 'POST' },
)
const idx = this.versions.findIndex(v => v.id === versionId)
if (idx >= 0) this.versions[idx] = updated
return updated
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de l\'acceptation'
throw err
}
},
/**
* Reject a proposed version.
*/
async rejectVersion(slug: string, itemId: string, versionId: string) {
this.error = null
try {
const { $api } = useApi()
const updated = await $api<ItemVersion>(
`/documents/${slug}/items/${itemId}/versions/${versionId}/reject`,
{ method: 'POST' },
)
const idx = this.versions.findIndex(v => v.id === versionId)
if (idx >= 0) this.versions[idx] = updated
return updated
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors du rejet'
throw err
}
},
/**
* Archive a document into the Sanctuary.
*/
async archiveDocument(slug: string) {
this.error = null
try {
const { $api } = useApi()
const doc = await $api<Document>(
`/documents/${slug}/archive`,
{ method: 'POST' },
)
// Update current if viewing this document
if (this.current?.slug === slug) {
this.current = doc
}
// Update in list
const idx = this.list.findIndex(d => d.slug === slug)
if (idx >= 0) this.list[idx] = doc
return doc
} catch (err: any) {
this.error = err?.data?.detail || err?.message || 'Erreur lors de l\'archivage'
throw err
}
},
/**
* Clear the current document, items and versions.
*/
clearCurrent() {
this.current = null
this.items = []
this.versions = []
},
},
})