Files
decision/frontend/app/components/documents/ItemVersionDiff.vue
Yvv 2bdc731639 Sprint 2 : moteur de documents + sanctuaire
Backend:
- CRUD complet documents/items/versions (update, delete, accept, reject, reorder)
- Service IPFS (upload/retrieve/pin via kubo HTTP API)
- Service sanctuaire : pipeline SHA-256 + IPFS + on-chain (system.remark)
- Verification integrite des entrees sanctuaire
- Recherche par reference (document -> entrees sanctuaire)
- Serialisation deterministe des documents pour archivage
- 14 tests unitaires supplementaires (document service)

Frontend:
- 9 composants : StatusBadge, MarkdownRenderer, DiffView, ItemCard,
  ItemVersionDiff, DocumentList, SanctuaryEntry, IPFSLink, ChainAnchor
- Page detail item avec historique des versions et diff
- Page detail sanctuaire avec verification integrite
- Modal de creation de document + proposition de version
- Archivage document vers sanctuaire depuis la page detail

Documentation:
- API reference mise a jour (9 nouveaux endpoints)
- Guides utilisateur documents et sanctuaire enrichis

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

96 lines
2.8 KiB
Vue

<script setup lang="ts">
import type { ItemVersion } from '~/stores/documents'
const props = defineProps<{
version: ItemVersion
}>()
const emit = defineEmits<{
accept: [versionId: string]
reject: [versionId: string]
}>()
const auth = useAuthStore()
function formatDate(dateStr: string): string {
return new Date(dateStr).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
}
function truncateAddress(address: string | null): string {
if (!address) return 'Inconnu'
if (address.length <= 16) return address
return address.slice(0, 8) + '...' + address.slice(-6)
}
</script>
<template>
<UCard>
<div class="space-y-4">
<!-- Header -->
<div class="flex items-start justify-between">
<div class="flex items-center gap-3">
<CommonStatusBadge :status="version.status" type="version" />
<span class="text-sm text-gray-500">
Propose par
<span class="font-medium text-gray-700 dark:text-gray-300 font-mono text-xs">
{{ truncateAddress(version.proposed_by) }}
</span>
</span>
</div>
<span class="text-xs text-gray-400">
{{ formatDate(version.created_at) }}
</span>
</div>
<!-- Rationale -->
<div v-if="version.rationale" class="bg-gray-50 dark:bg-gray-800/50 rounded-lg p-3">
<p class="text-xs font-semibold text-gray-500 uppercase mb-1">Justification</p>
<p class="text-sm text-gray-700 dark:text-gray-300">{{ version.rationale }}</p>
</div>
<!-- Diff view -->
<div v-if="version.diff">
<p class="text-xs font-semibold text-gray-500 uppercase mb-2">Modifications</p>
<CommonDiffView :diff="version.diff" />
</div>
<!-- Proposed text (fallback if no diff) -->
<div v-else-if="version.proposed_text">
<p class="text-xs font-semibold text-gray-500 uppercase mb-2">Texte propose</p>
<div class="bg-green-50 dark:bg-green-900/10 border border-green-200 dark:border-green-800 rounded-lg p-3">
<CommonMarkdownRenderer :content="version.proposed_text" />
</div>
</div>
<!-- Actions for authenticated users -->
<div
v-if="auth.isAuthenticated && version.status === 'proposed'"
class="flex items-center gap-3 pt-2 border-t border-gray-100 dark:border-gray-800"
>
<UButton
label="Accepter"
icon="i-lucide-check"
color="success"
variant="soft"
size="xs"
@click="emit('accept', version.id)"
/>
<UButton
label="Rejeter"
icon="i-lucide-x"
color="error"
variant="soft"
size="xs"
@click="emit('reject', version.id)"
/>
</div>
</div>
</UCard>
</template>