mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Migrate export generation flow from plain JS to Vue
Replaced the old JavaScript-based export generation logic with a Vue.js implementation to improve maintainability and modularity. Introduced a new API endpoint to fetch export status, updated the Webpack config, and integrated translations and Twig templates for the new flow. The Vue-based solution enhances user feedback and error handling during the export process.
This commit is contained in:
parent
80ce7f0bf1
commit
bd61eedfbb
@ -1,14 +0,0 @@
|
|||||||
import { download_report } from "../../lib/download-report/download-report";
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", function (e) {
|
|
||||||
const export_generate_url = window.export_generate_url;
|
|
||||||
|
|
||||||
if (typeof export_generate_url === "undefined") {
|
|
||||||
console.error("Alias not found!");
|
|
||||||
throw new Error("Alias not found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = window.location.search,
|
|
||||||
container = document.querySelector("#download_container");
|
|
||||||
download_report(export_generate_url + query.toString(), container);
|
|
||||||
});
|
|
@ -0,0 +1,129 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
EXPORT_GENERATION_EXPORT_GENERATION_IS_PENDING,
|
||||||
|
EXPORT_GENERATION_TOO_MANY_RETRIES,
|
||||||
|
EXPORT_GENERATION_ERROR_WHILE_GENERATING_EXPORT,
|
||||||
|
EXPORT_GENERATION_EXPORT_READY,
|
||||||
|
} from "translator";
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types";
|
||||||
|
import { fetchExportGenerationStatus } from "ChillMainAssets/vuejs/DownloadExport/api";
|
||||||
|
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||||
|
|
||||||
|
interface AppProps {
|
||||||
|
exportGenerationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<AppProps>();
|
||||||
|
|
||||||
|
const status = ref<StoredObjectStatus>("pending");
|
||||||
|
const storedObject = ref<null | StoredObject>(null);
|
||||||
|
|
||||||
|
const isPending = computed<boolean>(() => status.value === "pending");
|
||||||
|
const isFetching = computed<boolean>(
|
||||||
|
() => tryiesForReady.value < maxTryiesForReady,
|
||||||
|
);
|
||||||
|
const isReady = computed<boolean>(() => status.value === "ready");
|
||||||
|
const isFailure = computed<boolean>(() => status.value === "failure");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* counter for the number of times that we check for a new status
|
||||||
|
*/
|
||||||
|
let tryiesForReady = ref<number>(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* how many times we may check for a new status, once loaded
|
||||||
|
*/
|
||||||
|
const maxTryiesForReady = 120;
|
||||||
|
|
||||||
|
const checkForReady = function (): void {
|
||||||
|
if (
|
||||||
|
"ready" === status.value ||
|
||||||
|
"empty" === status.value ||
|
||||||
|
"failure" === status.value ||
|
||||||
|
// stop reloading if the page stays opened for a long time
|
||||||
|
tryiesForReady.value > maxTryiesForReady
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryiesForReady.value = tryiesForReady.value + 1;
|
||||||
|
setTimeout(onObjectNewStatusCallback, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onObjectNewStatusCallback = async function (): Promise<void> {
|
||||||
|
let status_response = await fetchExportGenerationStatus(
|
||||||
|
props.exportGenerationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (status_response.status === "pending") {
|
||||||
|
checkForReady();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
status.value = status_response.status;
|
||||||
|
storedObject.value = status_response.stored_object;
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
onObjectNewStatusCallback();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="waiting-screen">
|
||||||
|
<div
|
||||||
|
v-if="isPending && isFetching"
|
||||||
|
class="alert alert-danger text-center"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ trans(EXPORT_GENERATION_EXPORT_GENERATION_IS_PENDING) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
||||||
|
<span class="sr-only">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isPending && !isFetching" class="alert alert-info">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ trans(EXPORT_GENERATION_TOO_MANY_RETRIES) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isFailure" class="alert alert-danger text-center">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ trans(EXPORT_GENERATION_ERROR_WHILE_GENERATING_EXPORT) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isReady" class="alert alert-success text-center">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ trans(EXPORT_GENERATION_EXPORT_READY) }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p v-if="storedObject !== null">
|
||||||
|
<document-action-buttons-group
|
||||||
|
:stored-object="storedObject"
|
||||||
|
></document-action-buttons-group>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#waiting-screen {
|
||||||
|
> .alert {
|
||||||
|
min-height: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,14 @@
|
|||||||
|
import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types";
|
||||||
|
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||||
|
|
||||||
|
export const fetchExportGenerationStatus = async (
|
||||||
|
exportGenerationId: string,
|
||||||
|
): Promise<
|
||||||
|
| { status: "pending" }
|
||||||
|
| { status: StoredObjectStatus; stored_object: StoredObject }
|
||||||
|
> => {
|
||||||
|
return makeFetch(
|
||||||
|
"GET",
|
||||||
|
`/api/1.0/main/export-generation/${exportGenerationId}/object`,
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,12 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
|
||||||
|
const el = document.getElementById("app");
|
||||||
|
|
||||||
|
if (null === el) {
|
||||||
|
console.error("div element app was not found");
|
||||||
|
throw new Error("div element app was not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportGenerationId = el?.dataset.exportGenerationId as string;
|
||||||
|
createApp(App, { exportGenerationId }).mount(el);
|
@ -0,0 +1,23 @@
|
|||||||
|
{% extends '@ChillMain/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ parent() }}
|
||||||
|
{{ encore_entry_script_tags('page_download_exports') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ parent() }}
|
||||||
|
{{ encore_entry_link_tags('page_download_exports') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="app" data-export-generation-id="{{ exportGeneration.id | escape('html_attr') }}"></div>
|
||||||
|
|
||||||
|
<ul class="sticky-form-buttons record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<a href="{{ chill_return_path_or('chill_main_export_index') }}" class="btn btn-cancel">
|
||||||
|
{{ 'export.generation.Come back later'|trans|chill_return_path_label }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endblock content %}
|
@ -1101,3 +1101,25 @@ paths:
|
|||||||
204:
|
204:
|
||||||
description: "resource was deleted successfully"
|
description: "resource was deleted successfully"
|
||||||
|
|
||||||
|
/1.0/main/export-generation/{id}/object:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- export
|
||||||
|
summary: get the object status and details of an export-generation
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The entity export generation id
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
403:
|
||||||
|
description: Access denied
|
||||||
|
200:
|
||||||
|
description: "ok"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
@ -34,7 +34,7 @@ module.exports = function (encore, entries) {
|
|||||||
);
|
);
|
||||||
encore.addEntry(
|
encore.addEntry(
|
||||||
"page_download_exports",
|
"page_download_exports",
|
||||||
__dirname + "/Resources/public/page/export/download-export.js",
|
__dirname + "/Resources/public/vuejs/DownloadExport/index.ts",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Modules entrypoints
|
// Modules entrypoints
|
||||||
@ -120,4 +120,5 @@ module.exports = function (encore, entries) {
|
|||||||
"vue_onthefly",
|
"vue_onthefly",
|
||||||
__dirname + "/Resources/public/vuejs/OnTheFly/index.js",
|
__dirname + "/Resources/public/vuejs/OnTheFly/index.js",
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -716,6 +716,12 @@ notification:
|
|||||||
|
|
||||||
|
|
||||||
export:
|
export:
|
||||||
|
generation:
|
||||||
|
Export generation is pending: La génération de l'export est en cours
|
||||||
|
Come back later: Retour à l'index
|
||||||
|
Too many retries: Le nombre de vérification de la disponibilité de l'export a échoué. Essayez de recharger la page.
|
||||||
|
Error while generating export: Erreur interne lors de la génération de l'export
|
||||||
|
Export ready: L'export est prêt à être téléchargé
|
||||||
address_helper:
|
address_helper:
|
||||||
id: Identifiant de l'adresse
|
id: Identifiant de l'adresse
|
||||||
street: Voie
|
street: Voie
|
||||||
|
Loading…
x
Reference in New Issue
Block a user