mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-10-06 05:19:43 +00:00
Signature fixes
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Extracts the "returnPath" parameter from the current URL's query string and returns it.
|
||||
* If the parameter is not present, returns the provided fallback path.
|
||||
*
|
||||
* @param {string} fallbackPath - The fallback path to use if "returnPath" is not found in the query string.
|
||||
* @return {string} The "returnPath" from the query string, or the fallback path if "returnPath" is not present.
|
||||
*/
|
||||
export function returnPathOr(fallbackPath: string): string {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const returnPath = urlParams.get("returnPath");
|
||||
|
||||
return returnPath ?? fallbackPath;
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { EntityWorkflow } from "ChillMainAssets/types";
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
export const fetchWorkflow = async (
|
||||
workflowId: number,
|
||||
): Promise<EntityWorkflow> => {
|
||||
try {
|
||||
return await makeFetch<null, EntityWorkflow>(
|
||||
"GET",
|
||||
`/api/1.0/main/workflow/${workflowId}.json`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch workflow ${workflowId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
@@ -1,5 +1,6 @@
|
||||
import { GenericDoc } from "ChillDocStoreAssets/types/generic_doc";
|
||||
import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types";
|
||||
import { Person } from "../../../ChillPersonBundle/Resources/public/types";
|
||||
|
||||
export interface DateTime {
|
||||
datetime: string;
|
||||
@@ -202,6 +203,58 @@ export interface WorkflowAttachment {
|
||||
genericDoc: null | GenericDoc;
|
||||
}
|
||||
|
||||
export interface Workflow {
|
||||
name: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface EntityWorkflowStep {
|
||||
type: "entity_workflow_step";
|
||||
id: number;
|
||||
comment: string;
|
||||
currentStep: StepDefinition;
|
||||
isFinal: boolean;
|
||||
isFreezed: boolean;
|
||||
isFinalized: boolean;
|
||||
transitionPrevious: Transition | null;
|
||||
transitionAfter: Transition | null;
|
||||
previousId: number | null;
|
||||
nextId: number | null;
|
||||
transitionPreviousBy: User | null;
|
||||
transitionPreviousAt: DateTime | null;
|
||||
}
|
||||
|
||||
export interface Transition {
|
||||
name: string;
|
||||
text: string;
|
||||
isForward: boolean;
|
||||
}
|
||||
|
||||
export interface StepDefinition {
|
||||
name: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface EntityWorkflow {
|
||||
type: "entity_workflow";
|
||||
id: number;
|
||||
relatedEntityClass: string;
|
||||
relatedEntityId: number;
|
||||
workflow: Workflow;
|
||||
currentStep: EntityWorkflowStep;
|
||||
steps: EntityWorkflowStep[];
|
||||
datas: WorkflowData;
|
||||
title: string;
|
||||
isOnHoldAtCurrentStep: boolean;
|
||||
_permissions: {
|
||||
CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WorkflowData {
|
||||
persons: Person[];
|
||||
}
|
||||
|
||||
export interface ExportGeneration {
|
||||
id: string;
|
||||
type: "export_generation";
|
||||
@@ -215,3 +268,8 @@ export interface ExportGeneration {
|
||||
export interface PrivateCommentEmbeddable {
|
||||
comments: Record<number, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible states for the WaitingScreen Component.
|
||||
*/
|
||||
export type WaitingScreenState = "pending" | "failure" | "stopped" | "ready";
|
||||
|
@@ -10,7 +10,8 @@ import { computed, onMounted, ref } from "vue";
|
||||
import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types";
|
||||
import { fetchExportGenerationStatus } from "ChillMainAssets/lib/api/export";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
import { ExportGeneration } from "ChillMainAssets/types";
|
||||
import WaitingScreen from "../_components/WaitingScreen.vue";
|
||||
import { ExportGeneration, WaitingScreenState } from "ChillMainAssets/types";
|
||||
|
||||
interface AppProps {
|
||||
exportGenerationId: string;
|
||||
@@ -34,13 +35,16 @@ const storedObject = computed<null | StoredObject>(() => {
|
||||
});
|
||||
|
||||
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");
|
||||
const filename = computed<string>(() => `${props.title}-${props.createdDate}`);
|
||||
|
||||
const state = computed<WaitingScreenState>((): WaitingScreenState => {
|
||||
if (status.value === "empty") {
|
||||
return "pending";
|
||||
}
|
||||
|
||||
return status.value;
|
||||
});
|
||||
|
||||
/**
|
||||
* counter for the number of times that we check for a new status
|
||||
*/
|
||||
@@ -85,57 +89,36 @@ onMounted(() => {
|
||||
</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>
|
||||
<WaitingScreen :state="state">
|
||||
<template v-slot:pending>
|
||||
<p>
|
||||
{{ trans(EXPORT_GENERATION_EXPORT_GENERATION_IS_PENDING) }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<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>
|
||||
<template v-slot:stopped>
|
||||
<p>
|
||||
{{ trans(EXPORT_GENERATION_TOO_MANY_RETRIES) }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<p v-if="storedObject !== null">
|
||||
<document-action-buttons-group
|
||||
:stored-object="storedObject"
|
||||
:filename="filename"
|
||||
></document-action-buttons-group>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:failure>
|
||||
<p>
|
||||
{{ trans(EXPORT_GENERATION_ERROR_WHILE_GENERATING_EXPORT) }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template v-slot:ready>
|
||||
<p>
|
||||
{{ trans(EXPORT_GENERATION_EXPORT_READY) }}
|
||||
</p>
|
||||
|
||||
<p v-if="storedObject !== null">
|
||||
<document-action-buttons-group
|
||||
:stored-object="storedObject"
|
||||
:filename="filename"
|
||||
></document-action-buttons-group>
|
||||
</p>
|
||||
</template>
|
||||
</WaitingScreen>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#waiting-screen {
|
||||
> .alert {
|
||||
min-height: 350px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { useIntervalFn } from "@vueuse/core";
|
||||
import { fetchWorkflow } from "ChillMainAssets/lib/workflow/api";
|
||||
import { returnPathOr } from "ChillMainAssets/lib/return_path/returnPathHelper";
|
||||
import { ref } from "vue";
|
||||
import WaitingScreen from "ChillMainAssets/vuejs/_components/WaitingScreen.vue";
|
||||
import { WaitingScreenState } from "ChillMainAssets/types";
|
||||
import {
|
||||
trans,
|
||||
WORKFLOW_WAIT_TITLE,
|
||||
WORKFLOW_WAIT_ERROR_WHILE_WAITING,
|
||||
WORKFLOW_WAIT_SUCCESS,
|
||||
} from "translator";
|
||||
|
||||
interface WaitPostProcessWorkflowComponentProps {
|
||||
workflowId: number;
|
||||
expectedStep: string;
|
||||
}
|
||||
|
||||
const props = defineProps<WaitPostProcessWorkflowComponentProps>();
|
||||
const counter = ref<number>(0);
|
||||
const MAX_TRYIES = 50;
|
||||
|
||||
const state = ref<WaitingScreenState>("pending");
|
||||
|
||||
const { pause, resume } = useIntervalFn(
|
||||
async () => {
|
||||
try {
|
||||
const workflow = await fetchWorkflow(props.workflowId);
|
||||
counter.value++;
|
||||
if (workflow.currentStep.currentStep.name === props.expectedStep) {
|
||||
window.location.assign(
|
||||
returnPathOr("/fr/main/workflow" + workflow.id + "/show"),
|
||||
);
|
||||
resume();
|
||||
state.value = "ready";
|
||||
}
|
||||
|
||||
if (counter.value > MAX_TRYIES) {
|
||||
pause();
|
||||
state.value = "failure";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
pause();
|
||||
}
|
||||
},
|
||||
2000,
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<WaitingScreen :state="state">
|
||||
<template v-slot:pending>
|
||||
<p>
|
||||
{{ trans(WORKFLOW_WAIT_TITLE) }}
|
||||
</p>
|
||||
</template>
|
||||
<template v-slot:failure>
|
||||
<p>
|
||||
{{ trans(WORKFLOW_WAIT_ERROR_WHILE_WAITING) }}
|
||||
</p>
|
||||
</template>
|
||||
<template v-slot:ready>
|
||||
<p>
|
||||
{{ trans(WORKFLOW_WAIT_SUCCESS) }}
|
||||
</p>
|
||||
</template>
|
||||
</WaitingScreen>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@@ -0,0 +1,51 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
|
||||
function mountApp(): void {
|
||||
const el = document.querySelector<HTMLDivElement>(".screen-wait");
|
||||
if (!el) {
|
||||
console.error(
|
||||
"WaitPostProcessWorkflow: mount element .screen-wait not found",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const workflowIdAttr = el.getAttribute("data-workflow-id");
|
||||
const expectedStep = el.getAttribute("data-expected-step") || "";
|
||||
|
||||
if (!workflowIdAttr) {
|
||||
console.error(
|
||||
"WaitPostProcessWorkflow: data-workflow-id attribute missing on mount element",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!expectedStep) {
|
||||
console.error(
|
||||
"WaitPostProcessWorkflow: data-expected-step attribute missing on mount element",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const workflowId = Number(workflowIdAttr);
|
||||
if (Number.isNaN(workflowId)) {
|
||||
console.error(
|
||||
"WaitPostProcessWorkflow: data-workflow-id is not a valid number:",
|
||||
workflowIdAttr,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const app = createApp(App, {
|
||||
workflowId,
|
||||
expectedStep,
|
||||
});
|
||||
|
||||
app.mount(el);
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", mountApp);
|
||||
} else {
|
||||
mountApp();
|
||||
}
|
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useTemplateRef } from "vue";
|
||||
import type { WorkflowAttachment } from "ChillMainAssets/types";
|
||||
import { computed, onMounted, ref, useTemplateRef } from "vue";
|
||||
import type { EntityWorkflow, WorkflowAttachment } from "ChillMainAssets/types";
|
||||
import PickGenericDocModal from "ChillMainAssets/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue";
|
||||
import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc";
|
||||
import AttachmentList from "ChillMainAssets/vuejs/WorkflowAttachment/Component/AttachmentList.vue";
|
||||
import { GenericDoc } from "ChillDocStoreAssets/types";
|
||||
import { fetchWorkflow } from "ChillMainAssets/lib/workflow/api";
|
||||
|
||||
interface AppConfig {
|
||||
workflowId: number;
|
||||
@@ -34,6 +35,13 @@ const attachedGenericDoc = computed<GenericDocForAccompanyingPeriod[]>(
|
||||
) as GenericDocForAccompanyingPeriod[],
|
||||
);
|
||||
|
||||
const workflow = ref<EntityWorkflow | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
workflow.value = await fetchWorkflow(Number(props.workflowId));
|
||||
console.log("workflow", workflow.value);
|
||||
});
|
||||
|
||||
const openModal = function () {
|
||||
pickDocModal.value?.openModal();
|
||||
};
|
||||
@@ -49,20 +57,30 @@ const onPickGenericDoc = ({
|
||||
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
|
||||
emit("removeAttachment", payload);
|
||||
};
|
||||
|
||||
const canEditAttachement = computed<boolean>(() => {
|
||||
if (null === workflow.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return workflow.value._permissions.CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pick-generic-doc-modal
|
||||
:workflow="workflow"
|
||||
:accompanying-period-id="props.accompanyingPeriodId"
|
||||
:to-remove="attachedGenericDoc"
|
||||
ref="pickDocModal"
|
||||
@pickGenericDoc="onPickGenericDoc"
|
||||
></pick-generic-doc-modal>
|
||||
<attachment-list
|
||||
:workflow="workflow"
|
||||
:attachments="props.attachments"
|
||||
@removeAttachment="onRemoveAttachment"
|
||||
></attachment-list>
|
||||
<ul class="record_actions">
|
||||
<ul v-if="canEditAttachement" class="record_actions">
|
||||
<li>
|
||||
<button type="button" class="btn btn-create" @click="openModal">
|
||||
Ajouter une pièce jointe
|
||||
|
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { WorkflowAttachment } from "ChillMainAssets/types";
|
||||
import { EntityWorkflow, WorkflowAttachment } from "ChillMainAssets/types";
|
||||
import GenericDocItemBox from "ChillMainAssets/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
|
||||
interface AttachmentListProps {
|
||||
attachments: WorkflowAttachment[];
|
||||
workflow: EntityWorkflow | null;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -36,7 +37,12 @@ const props = defineProps<AttachmentListProps>();
|
||||
:stored-object="a.genericDoc.storedObject"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li>
|
||||
<li
|
||||
v-if="
|
||||
!workflow?._permissions
|
||||
.CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT
|
||||
"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-delete"
|
||||
|
@@ -6,8 +6,10 @@ import {
|
||||
import PickGenericDocItem from "ChillMainAssets/vuejs/WorkflowAttachment/Component/PickGenericDocItem.vue";
|
||||
import { fetch_generic_docs_by_accompanying_period } from "ChillDocStoreAssets/js/generic-doc-api";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { EntityWorkflow } from "ChillMainAssets/types";
|
||||
|
||||
interface PickGenericDocProps {
|
||||
workflow: EntityWorkflow | null;
|
||||
accompanyingPeriodId: number;
|
||||
pickedList: GenericDocForAccompanyingPeriod[];
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
@@ -36,9 +38,21 @@ const isPicked = (genericDoc: GenericDocForAccompanyingPeriod): boolean =>
|
||||
) !== -1;
|
||||
|
||||
onMounted(async () => {
|
||||
genericDocs.value = await fetch_generic_docs_by_accompanying_period(
|
||||
const fetchedGenericDocs = await fetch_generic_docs_by_accompanying_period(
|
||||
props.accompanyingPeriodId,
|
||||
);
|
||||
const documentClasses = [
|
||||
"Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument",
|
||||
"Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument",
|
||||
"Chill\\DocStoreBundle\\Entity\\PersonDocument",
|
||||
];
|
||||
|
||||
genericDocs.value = fetchedGenericDocs.filter(
|
||||
(doc) =>
|
||||
!documentClasses.includes(
|
||||
props.workflow?.relatedEntityClass || "",
|
||||
) || props.workflow?.relatedEntityId !== doc.identifiers.id,
|
||||
);
|
||||
loaded.value = true;
|
||||
});
|
||||
|
||||
|
@@ -3,8 +3,10 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { computed, ref, useTemplateRef } from "vue";
|
||||
import PickGenericDoc from "ChillMainAssets/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue";
|
||||
import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc";
|
||||
import { EntityWorkflow } from "ChillMainAssets/types";
|
||||
|
||||
interface PickGenericDocModalProps {
|
||||
workflow: EntityWorkflow | null;
|
||||
accompanyingPeriodId: number;
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
}
|
||||
@@ -80,6 +82,7 @@ defineExpose({ openModal, closeModal });
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<pick-generic-doc
|
||||
:workflow="props.workflow"
|
||||
:accompanying-period-id="props.accompanyingPeriodId"
|
||||
:to-remove="props.toRemove"
|
||||
:picked-list="pickeds"
|
||||
|
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { WaitingScreenState } from "ChillMainAssets/types";
|
||||
|
||||
interface Props {
|
||||
state: WaitingScreenState;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="waiting-screen">
|
||||
<div
|
||||
v-if="props.state === 'pending' && !!$slots.pending"
|
||||
class="alert alert-danger text-center"
|
||||
>
|
||||
<div>
|
||||
<slot name="pending"></slot>
|
||||
</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="props.state === 'stopped' && !!$slots.stopped"
|
||||
class="alert alert-info"
|
||||
>
|
||||
<div>
|
||||
<slot name="stopped"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="props.state === 'failure' && !!$slots.failure"
|
||||
class="alert alert-danger text-center"
|
||||
>
|
||||
<div>
|
||||
<slot name="failure"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="props.state === 'ready' && !!$slots.ready"
|
||||
class="alert alert-success text-center"
|
||||
>
|
||||
<div>
|
||||
<slot name="ready"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#waiting-screen {
|
||||
> .alert {
|
||||
min-height: 350px;
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user