Add Vue.js module for polling and downloading stored objects

- Created `waiting_download` module with main `App.vue` component.
- Integrated polling logic to check stored object status using the `/is-ready` endpoint.
- Displayed messages for pending, stopped, failure, and ready states with a "Download" button for ready objects.
- Added `StoredObjectReady` type and updated types for improved type safety.
- Dynamically mounted the module using dataset attributes.
This commit is contained in:
2026-02-23 13:48:32 +01:00
parent dca59a9254
commit c5ed93eebd
4 changed files with 139 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { StoredObject, StoredObjectStatus, StoredObjectReady } from "../../types";
import WaitingScreen from "ChillMainAssets/vuejs/_components/WaitingScreen.vue";
import DownloadButton from "../../vuejs/StoredObjectButton/DownloadButton.vue";
import { WaitingScreenState } from "ChillMainAssets/types";
interface AppProps {
storedObjectUuid: string;
waitingText: string;
stoppedText: string;
failureText: string;
readyText: string;
}
const props = defineProps<AppProps>();
const storedObject = ref<StoredObject | null>(null);
const currentStatus = ref<StoredObjectStatus>("pending");
const state = computed<WaitingScreenState>((): WaitingScreenState => {
if (currentStatus.value === "empty") {
return "pending";
}
if (currentStatus.value === "pending" && tries >= maxTries) {
return "stopped";
}
return currentStatus.value;
});
let tries = 0;
const maxTries = 120; // 120 * 10s = 20 minutes
const pollStatus = async (): Promise<void> => {
if (
currentStatus.value === "ready" ||
currentStatus.value === "failure" ||
tries >= maxTries
) {
if (tries >= maxTries && currentStatus.value === "pending") {
currentStatus.value = "failure";
}
return;
}
try {
const result: StoredObjectReady = await makeFetch(
"GET",
`/api/1.0/doc-store/stored-object/${props.storedObjectUuid}/is-ready`,
null
);
currentStatus.value = result.status;
if (currentStatus.value === "ready") {
// Fetch full object to have currentVersion and links for DownloadButton
storedObject.value = await makeFetch(
"GET",
`/api/1.0/doc-store/stored-object/${props.storedObjectUuid}`,
null
);
} else {
tries++;
setTimeout(pollStatus, 10000);
}
} catch (e) {
console.error("Error polling status", e);
currentStatus.value = "failure";
}
};
onMounted(() => {
pollStatus();
});
</script>
<template>
<WaitingScreen :state="state">
<template v-slot:pending>
<p>{{ props.waitingText }}</p>
</template>
<template v-slot:stopped>
<p>{{ props.stoppedText }}</p>
</template>
<template v-slot:failure>
<p>{{ props.failureText }}</p>
</template>
<template v-slot:ready>
<p>{{ props.readyText }}</p>
<p v-if="storedObject !== null && storedObject.currentVersion !== null">
<download-button
:stored-object="storedObject"
:at-version="storedObject.currentVersion"
:classes="{ 'btn btn-primary': true }"
:display-action-string-in-button="true"
:direct-download="true"
></download-button>
</p>
</template>
</WaitingScreen>
</template>

View File

@@ -0,0 +1,22 @@
import { createApp } from "vue";
import App from "./App.vue";
const elements = document.querySelectorAll<HTMLElement>(
"[data-wait-download-stored-object]"
);
elements.forEach((el) => {
const storedObjectUuid = el.dataset.storedObjectUuid as string;
const waitingText = el.dataset.waitingText as string;
const stoppedText = el.dataset.stoppedText as string;
const failureText = el.dataset.failureText as string;
const readyText = el.dataset.readyText as string;
createApp(App, {
storedObjectUuid,
waitingText,
stoppedText,
failureText,
readyText,
}).mount(el);
});

View File

@@ -3,6 +3,13 @@ import { SignedUrlGet } from "ChillDocStoreAssets/vuejs/StoredObjectButton/helpe
export type StoredObjectStatus = "empty" | "ready" | "failure" | "pending";
export interface StoredObjectReady {
id: number;
title: string;
status: StoredObjectStatus;
type: string;
}
export interface StoredObject {
id: number;
title: string | null;

View File

@@ -18,4 +18,8 @@ module.exports = function (encore) {
"vue_document_signature",
__dirname + "/Resources/public/vuejs/DocumentSignature/index",
);
encore.addEntry(
"page_wait_download_stored_object",
__dirname + "/Resources/public/module/waiting_download/index.ts",
);
};