Add document history button with modal viewer

This commit introduces a History button to the DocumentActionButtonsGroup component to view document versions. It includes new components for the modal dialog and API integrations to fetch and display version histories. This feature allows users to view and restore previous versions of stored objects.
This commit is contained in:
Julien Fastré 2024-09-17 17:08:27 +02:00
parent dd3f6fb0ab
commit b0e2e65885
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
7 changed files with 184 additions and 1 deletions

View File

@ -62,6 +62,16 @@ export interface StoredObjectStatusChange {
type: string;
}
export interface StoredObjectVersionWithPointInTime extends StoredObjectVersionPersisted {
"point-in-times": StoredObjectPointInTime[];
}
export interface StoredObjectPointInTime {
id: number;
byUser: User | null;
reason: 'keep-before-conversion'|'keep-by-user';
}
/**
* Function executed by the WopiEditButton component.
*/

View File

@ -16,6 +16,9 @@
<li v-if="isDownloadable">
<download-button :stored-object="props.storedObject" :at-version="props.storedObject.currentVersion" :filename="filename" :classes="{'dropdown-item': true}"></download-button>
</li>
<li v-if="isHistoryViewable">
<history-button :stored-object="props.storedObject" :can-edit="canEdit && props.storedObject._permissions.canEdit"></history-button>
</li>
</ul>
</div>
<div v-else-if="'pending' === props.storedObject.status">
@ -40,6 +43,7 @@ import {
WopiEditButtonExecutableBeforeLeaveFunction
} from "../types";
import DesktopEditButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DesktopEditButton.vue";
import HistoryButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton.vue";
interface DocumentActionButtonsGroupConfig {
storedObject: StoredObject,
@ -126,7 +130,11 @@ const isConvertibleToPdf = computed<boolean>(() => {
&& is_extension_viewable(props.storedObject.currentVersion.type)
&& props.storedObject.currentVersion.type !== 'application/pdf'
&& props.storedObject.currentVersion.persisted !== false;
})
});
const isHistoryViewable = computed<boolean>(() => {
return props.storedObject.status === 'ready';
});
const checkForReady = function(): void {
if (

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import HistoryButtonModal from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue";
import {StoredObject, StoredObjectVersionWithPointInTime} from "./../../types";
import {computed, reactive, ref, useTemplateRef} from "vue";
import {get_versions} from "./HistoryButton/api";
interface HistoryButtonConfig {
storedObject: StoredObject;
canEdit: boolean;
}
interface HistoryButtonState {
versions: StoredObjectVersionWithPointInTime[];
loaded: boolean;
}
const props = defineProps<HistoryButtonConfig>();
const state = reactive<HistoryButtonState>({versions: [], loaded: false});
const modal = useTemplateRef<typeof HistoryButtonModal>('modal');
const download_version_and_open_modal = async function (): Promise<void> {
if (null !== modal.value) {
modal.value.open();
} else {
console.log("modal is null");
}
if (!state.loaded) {
const versions = await get_versions(props.storedObject);
for (const version of versions) {
state.versions.push(version);
}
state.loaded = true;
}
}
</script>
<template>
<a @click="download_version_and_open_modal" class="dropdown-item">
<history-button-modal ref="modal" :versions="state.versions" :can-edit="canEdit"></history-button-modal>
<i class="fa fa-history"></i>
Historique
</a>
</template>
<style scoped lang="scss">
i.fa::before {
color: var(--bs-dropdown-link-hover-color);
}
</style>

View File

@ -0,0 +1,30 @@
<script setup lang="ts">
import {StoredObjectVersionWithPointInTime} from "./../../../types";
import HistoryButtonListItem from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue";
interface HistoryButtonListConfig {
versions: StoredObjectVersionWithPointInTime[],
canEdit: boolean;
}
const props = defineProps<HistoryButtonListConfig>();
</script>
<template>
<template v-if="versions.length > 0">
<div class="container">
<template v-for="v in versions">
<history-button-list-item :version="v" :can-edit="canEdit"></history-button-list-item>
</template>
</div>
</template>
<template v-else>
<p>Chargement des versions</p>
</template>
</template>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,35 @@
<script setup lang="ts">
import {StoredObjectVersionWithPointInTime} from "./../../../types";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
import {ISOToDatetime} from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
interface HistoryButtonListItemConfig {
version: StoredObjectVersionWithPointInTime;
canEdit: boolean;
}
const props = defineProps<HistoryButtonListItemConfig>();
</script>
<template>
<div class="row">
<div class="col">
<file-icon :type="version.type"></file-icon>
<strong>Par</strong> <UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge>
<strong>Le</strong> {{ $d(ISOToDatetime(version.createdAt.datetime8601)) }}
</div>
<div class="col" v-if="canEdit || canRestore">
<ul class="record_actions">
<li v-if="canEdit">
<button class="btn btn-misc">Restaurer</button>
</li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,39 @@
<script setup lang="ts">
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import {reactive} from "vue";
import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
import {StoredObjectVersionWithPointInTime} from "./../../../types";
interface HistoryButtonListConfig {
versions: StoredObjectVersionWithPointInTime[];
canEdit: boolean;
}
interface HistoryButtonModalState {
opened: boolean;
}
const props = defineProps<HistoryButtonListConfig>();
const state = reactive<HistoryButtonModalState>({opened: false});
const open = () => {
console.log('open');
state.opened = true;
}
defineExpose({open});
</script>
<template>
<Teleport to="body">
<modal v-if="state.opened" @close="state.opened = false">
<template v-slot:body>
<history-button-list :versions="props.versions" :can-edit="canEdit"></history-button-list>
</template>
</modal>
</Teleport>
</template>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,8 @@
import {StoredObject, StoredObjectVersion, StoredObjectVersionWithPointInTime} from "../../../types";
import {fetchResults, makeFetch} from "../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
export const get_versions = async (storedObject: StoredObject): Promise<StoredObjectVersionWithPointInTime[]> => {
const versions = await fetchResults<StoredObjectVersionWithPointInTime>(`/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`);
return versions.sort((a: StoredObjectVersionWithPointInTime, b: StoredObjectVersionWithPointInTime) => a.version - b.version);
}