mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-09 21:58:28 +00:00
Merge branch 'ticket-app-master' into ticket/64-identifiants-person
# Conflicts: # package.json # src/Bundle/ChillMainBundle/Entity/User.php # src/Bundle/ChillMainBundle/Resources/public/types.ts # src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue # src/Bundle/ChillPersonBundle/chill.api.specs.yaml # src/Bundle/ChillTicketBundle/src/Messenger/Handler/PostTicketUpdateMessageHandler.php
This commit is contained in:
@@ -1,21 +1,22 @@
|
||||
<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,
|
||||
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/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;
|
||||
title: string;
|
||||
createdDate: string;
|
||||
exportGenerationId: string;
|
||||
title: string;
|
||||
createdDate: string;
|
||||
}
|
||||
|
||||
const props = defineProps<AppProps>();
|
||||
@@ -23,24 +24,27 @@ const props = defineProps<AppProps>();
|
||||
const exportGeneration = ref<ExportGeneration | null>(null);
|
||||
|
||||
const status = computed<StoredObjectStatus>(
|
||||
() => exportGeneration.value?.status ?? "pending",
|
||||
() => exportGeneration.value?.status ?? "pending",
|
||||
);
|
||||
const storedObject = computed<null | StoredObject>(() => {
|
||||
if (exportGeneration.value === null) {
|
||||
return null;
|
||||
}
|
||||
if (exportGeneration.value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return exportGeneration.value?.storedObject;
|
||||
return exportGeneration.value?.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
|
||||
*/
|
||||
@@ -52,87 +56,69 @@ let tryiesForReady = ref<number>(0);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
tryiesForReady.value = tryiesForReady.value + 1;
|
||||
setTimeout(onObjectNewStatusCallback, 5000);
|
||||
};
|
||||
|
||||
const onObjectNewStatusCallback = async function (): Promise<void> {
|
||||
exportGeneration.value = await fetchExportGenerationStatus(
|
||||
props.exportGenerationId,
|
||||
);
|
||||
exportGeneration.value = await fetchExportGenerationStatus(
|
||||
props.exportGenerationId,
|
||||
);
|
||||
|
||||
if (isPending.value) {
|
||||
checkForReady();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (isPending.value) {
|
||||
checkForReady();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onObjectNewStatusCallback();
|
||||
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>
|
||||
<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,23 +1,25 @@
|
||||
<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";
|
||||
import { trans, WORKFLOW_ATTACHMENTS_ADD_AN_ATTACHMENT } from "translator";
|
||||
|
||||
interface AppConfig {
|
||||
workflowId: number;
|
||||
accompanyingPeriodId: number;
|
||||
attachments: WorkflowAttachment[];
|
||||
workflowId: number;
|
||||
accompanyingPeriodId: number;
|
||||
attachments: WorkflowAttachment[];
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
(e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void;
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
(e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void;
|
||||
}>();
|
||||
|
||||
type PickGenericModalType = InstanceType<typeof PickGenericDocModal>;
|
||||
@@ -26,49 +28,66 @@ const pickDocModal = useTemplateRef<PickGenericModalType>("pickDocModal");
|
||||
const props = defineProps<AppConfig>();
|
||||
|
||||
const attachedGenericDoc = computed<GenericDocForAccompanyingPeriod[]>(
|
||||
() =>
|
||||
props.attachments
|
||||
.map((a: WorkflowAttachment) => a.genericDoc)
|
||||
.filter(
|
||||
(g: GenericDoc | null) => g !== null,
|
||||
) as GenericDocForAccompanyingPeriod[],
|
||||
() =>
|
||||
props.attachments
|
||||
.map((a: WorkflowAttachment) => a.genericDoc)
|
||||
.filter(
|
||||
(g: GenericDoc | null) => g !== null,
|
||||
) 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();
|
||||
pickDocModal.value?.openModal();
|
||||
};
|
||||
|
||||
const onPickGenericDoc = ({
|
||||
genericDoc,
|
||||
genericDoc,
|
||||
}: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => {
|
||||
emit("pickGenericDoc", { genericDoc });
|
||||
emit("pickGenericDoc", { genericDoc });
|
||||
};
|
||||
|
||||
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
|
||||
emit("removeAttachment", payload);
|
||||
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
|
||||
:accompanying-period-id="props.accompanyingPeriodId"
|
||||
:to-remove="attachedGenericDoc"
|
||||
ref="pickDocModal"
|
||||
@pickGenericDoc="onPickGenericDoc"
|
||||
></pick-generic-doc-modal>
|
||||
<attachment-list
|
||||
:attachments="props.attachments"
|
||||
@removeAttachment="onRemoveAttachment"
|
||||
></attachment-list>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button type="button" class="btn btn-create" @click="openModal">
|
||||
Ajouter une pièce jointe
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<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 v-if="canEditAttachement" class="record_actions">
|
||||
<li>
|
||||
<button type="button" class="btn btn-create" @click="openModal">
|
||||
{{ trans(WORKFLOW_ATTACHMENTS_ADD_AN_ATTACHMENT) }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,52 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import { WorkflowAttachment } from "ChillMainAssets/types";
|
||||
import {
|
||||
AttachmentWithDocAndStored,
|
||||
EntityWorkflow,
|
||||
isAttachmentWithDocAndStored,
|
||||
WorkflowAttachment,
|
||||
} from "ChillMainAssets/types";
|
||||
import GenericDocItemBox from "ChillMainAssets/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
import { computed } from "vue";
|
||||
import { trans, WORKFLOW_ATTACHMENTS_NO_ATTACHMENT } from "translator";
|
||||
|
||||
interface AttachmentListProps {
|
||||
attachments: WorkflowAttachment[];
|
||||
attachments: WorkflowAttachment[];
|
||||
workflow: EntityWorkflow | null;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
(e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void;
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
(e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<AttachmentListProps>();
|
||||
|
||||
const notNullAttachments = computed<AttachmentWithDocAndStored[]>(() =>
|
||||
props.attachments.filter(
|
||||
(a: WorkflowAttachment): a is AttachmentWithDocAndStored =>
|
||||
isAttachmentWithDocAndStored(a),
|
||||
),
|
||||
);
|
||||
|
||||
const canRemove = computed<boolean>((): boolean => {
|
||||
if (null === props.workflow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return props.workflow._permissions.CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
v-if="props.attachments.length === 0"
|
||||
class="chill-no-data-statement text-center"
|
||||
>
|
||||
Aucune pièce jointe
|
||||
</p>
|
||||
<!-- TODO translate -->
|
||||
<div else class="flex-table">
|
||||
<div v-for="a in props.attachments" :key="a.id" class="item-bloc">
|
||||
<generic-doc-item-box
|
||||
v-if="a.genericDoc !== null"
|
||||
:generic-doc="a.genericDoc"
|
||||
></generic-doc-item-box>
|
||||
<div class="item-row separator">
|
||||
<ul class="record_actions">
|
||||
<li v-if="a.genericDoc?.storedObject !== null">
|
||||
<document-action-buttons-group
|
||||
:stored-object="a.genericDoc.storedObject"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-delete"
|
||||
@click="emit('removeAttachment', { attachment: a })"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p
|
||||
v-if="notNullAttachments.length === 0"
|
||||
class="chill-no-data-statement text-center"
|
||||
>
|
||||
{{ trans(WORKFLOW_ATTACHMENTS_NO_ATTACHMENT) }}
|
||||
</p>
|
||||
<div v-else class="flex-table">
|
||||
<div v-for="a in notNullAttachments" :key="a.id" class="item-bloc">
|
||||
<generic-doc-item-box
|
||||
:generic-doc="a.genericDoc"
|
||||
></generic-doc-item-box>
|
||||
<div class="item-row separator">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<document-action-buttons-group
|
||||
:stored-object="a.genericDoc.storedObject"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li v-if="canRemove">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-delete"
|
||||
@click="emit('removeAttachment', { attachment: a })"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc";
|
||||
import { GenericDoc } from "ChillDocStoreAssets/types/generic_doc";
|
||||
|
||||
interface GenericDocItemBoxProps {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDoc;
|
||||
}
|
||||
|
||||
const props = defineProps<GenericDocItemBoxProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="'html' in props.genericDoc.metadata"
|
||||
v-html="props.genericDoc.metadata.html"
|
||||
></div>
|
||||
<div
|
||||
v-if="'html' in props.genericDoc.metadata"
|
||||
v-html="props.genericDoc.metadata.html"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,45 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
GenericDoc,
|
||||
GenericDocForAccompanyingPeriod,
|
||||
GenericDoc,
|
||||
GenericDocForAccompanyingPeriod,
|
||||
} from "ChillDocStoreAssets/types/generic_doc";
|
||||
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 {
|
||||
accompanyingPeriodId: number;
|
||||
pickedList: GenericDocForAccompanyingPeriod[];
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
workflow: EntityWorkflow | null;
|
||||
accompanyingPeriodId: number;
|
||||
pickedList: GenericDocForAccompanyingPeriod[];
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
}
|
||||
|
||||
const props = defineProps<PickGenericDocProps>();
|
||||
const emit = defineEmits<{
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
|
||||
(
|
||||
e: "removeGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
(
|
||||
e: "removeGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
}>();
|
||||
|
||||
const genericDocs = ref<GenericDocForAccompanyingPeriod[]>([]);
|
||||
const loaded = ref(false);
|
||||
|
||||
const isPicked = (genericDoc: GenericDocForAccompanyingPeriod): boolean =>
|
||||
props.pickedList.findIndex(
|
||||
(element: GenericDocForAccompanyingPeriod) =>
|
||||
element.uniqueKey === genericDoc.uniqueKey,
|
||||
) !== -1;
|
||||
props.pickedList.findIndex(
|
||||
(element: GenericDocForAccompanyingPeriod) =>
|
||||
element.uniqueKey === genericDoc.uniqueKey,
|
||||
) !== -1;
|
||||
|
||||
onMounted(async () => {
|
||||
genericDocs.value = await fetch_generic_docs_by_accompanying_period(
|
||||
props.accompanyingPeriodId,
|
||||
);
|
||||
loaded.value = true;
|
||||
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;
|
||||
});
|
||||
|
||||
const textFilter = ref<string>("");
|
||||
@@ -48,213 +62,229 @@ const dateToFilter = ref<string | null>(null);
|
||||
const placesFilter = ref<string[]>([]);
|
||||
|
||||
const availablePlaces = computed<string[]>(() => {
|
||||
const places = new Set<string>(
|
||||
genericDocs.value.map((genericDoc: GenericDoc) => genericDoc.key),
|
||||
);
|
||||
const places = new Set<string>(
|
||||
genericDocs.value.map((genericDoc: GenericDoc) => genericDoc.key),
|
||||
);
|
||||
|
||||
return Array.from(places).sort((a, b) => (a < b ? -1 : a === b ? 0 : 1));
|
||||
return Array.from(places).sort((a, b) => (a < b ? -1 : a === b ? 0 : 1));
|
||||
});
|
||||
|
||||
const placeTrans = (str: string): string => {
|
||||
switch (str) {
|
||||
case "accompanying_course_document":
|
||||
return "Documents du parcours";
|
||||
case "person_document":
|
||||
return "Documents de l'usager";
|
||||
case "accompanying_period_calendar_document":
|
||||
return "Document des rendez-vous des parcours";
|
||||
case "accompanying_period_activity_document":
|
||||
return "Document des échanges des parcours";
|
||||
case "accompanying_period_work_evaluation_document":
|
||||
return "Document des actions d'accompagnement";
|
||||
default:
|
||||
return str;
|
||||
}
|
||||
switch (str) {
|
||||
case "accompanying_course_document":
|
||||
return "Documents du parcours";
|
||||
case "person_document":
|
||||
return "Documents de l'usager";
|
||||
case "accompanying_period_calendar_document":
|
||||
return "Document des rendez-vous des parcours";
|
||||
case "accompanying_period_activity_document":
|
||||
return "Document des échanges des parcours";
|
||||
case "accompanying_period_work_evaluation_document":
|
||||
return "Document des actions d'accompagnement";
|
||||
default:
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
const onPickDocument = (payload: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => emit("pickGenericDoc", payload);
|
||||
|
||||
const onRemoveGenericDoc = (payload: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => emit("removeGenericDoc", payload);
|
||||
|
||||
const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
|
||||
if (false === loaded.value) {
|
||||
return [];
|
||||
}
|
||||
if (false === loaded.value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return genericDocs.value
|
||||
.filter(
|
||||
(genericDoc: GenericDocForAccompanyingPeriod) =>
|
||||
!props.toRemove
|
||||
.map((g: GenericDocForAccompanyingPeriod) => g.uniqueKey)
|
||||
.includes(genericDoc.uniqueKey),
|
||||
)
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (textFilter.value === "") {
|
||||
return true;
|
||||
}
|
||||
return genericDocs.value
|
||||
.filter(
|
||||
(genericDoc: GenericDocForAccompanyingPeriod) =>
|
||||
!props.toRemove
|
||||
.map((g: GenericDocForAccompanyingPeriod) => g.uniqueKey)
|
||||
.includes(genericDoc.uniqueKey),
|
||||
)
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (textFilter.value === "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const needles = textFilter.value
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((str: string) => str.trim().toLowerCase())
|
||||
.filter((str: string) => str.length > 0);
|
||||
const title: string =
|
||||
"title" in genericDoc.metadata
|
||||
? (genericDoc.metadata.title as string)
|
||||
: "";
|
||||
if (title === "") {
|
||||
return false;
|
||||
}
|
||||
const needles = textFilter.value
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((str: string) => str.trim().toLowerCase())
|
||||
.filter((str: string) => str.length > 0);
|
||||
const title: string =
|
||||
"title" in genericDoc.metadata
|
||||
? (genericDoc.metadata.title as string)
|
||||
: "";
|
||||
if (title === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return needles.every((n: string) => title.toLowerCase().includes(n));
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (placesFilter.value.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return needles.every((n: string) =>
|
||||
title.toLowerCase().includes(n),
|
||||
);
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (placesFilter.value.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return placesFilter.value.includes(genericDoc.key);
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (dateToFilter.value === null) {
|
||||
return true;
|
||||
}
|
||||
return placesFilter.value.includes(genericDoc.key);
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (dateToFilter.value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return genericDoc.doc_date.datetime8601 < dateToFilter.value;
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (dateFromFilter.value === null) {
|
||||
return true;
|
||||
}
|
||||
return genericDoc.doc_date.datetime8601 < dateToFilter.value;
|
||||
})
|
||||
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
|
||||
if (dateFromFilter.value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return genericDoc.doc_date.datetime8601 > dateFromFilter.value;
|
||||
});
|
||||
return genericDoc.doc_date.datetime8601 > dateFromFilter.value;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loaded">
|
||||
<div>
|
||||
<form name="f" method="get">
|
||||
<div class="accordion my-3" id="filterOrderAccordion">
|
||||
<h2 class="accordion-header" id="filterOrderHeading">
|
||||
<button
|
||||
class="accordion-button"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#filterOrderCollapse"
|
||||
aria-expanded="true"
|
||||
aria-controls="filterOrderCollapse"
|
||||
>
|
||||
<strong
|
||||
><i class="fa fa-fw fa-filter"></i>Filtrer la liste</strong
|
||||
>
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
class="accordion-collapse collapse"
|
||||
id="filterOrderCollapse"
|
||||
aria-labelledby="filterOrderHeading"
|
||||
data-bs-parent="#filterOrderAccordion"
|
||||
style=""
|
||||
>
|
||||
<div
|
||||
class="accordion-body chill_filter_order container-xxl p-5 py-2"
|
||||
>
|
||||
<div class="row my-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="textFilter"
|
||||
type="search"
|
||||
id="f_q"
|
||||
name="f[q]"
|
||||
placeholder="Chercher dans la liste"
|
||||
class="form-control"
|
||||
/>
|
||||
<button type="submit" class="btn btn-misc">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loaded">
|
||||
<div>
|
||||
<form name="f" method="get">
|
||||
<div class="accordion my-3" id="filterOrderAccordion">
|
||||
<h2 class="accordion-header" id="filterOrderHeading">
|
||||
<button
|
||||
class="accordion-button"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#filterOrderCollapse"
|
||||
aria-expanded="true"
|
||||
aria-controls="filterOrderCollapse"
|
||||
>
|
||||
<strong
|
||||
><i class="fa fa-fw fa-filter"></i>Filtrer la
|
||||
liste</strong
|
||||
>
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
class="accordion-collapse collapse"
|
||||
id="filterOrderCollapse"
|
||||
aria-labelledby="filterOrderHeading"
|
||||
data-bs-parent="#filterOrderAccordion"
|
||||
style=""
|
||||
>
|
||||
<div
|
||||
class="accordion-body chill_filter_order container-xxl p-5 py-2"
|
||||
>
|
||||
<div class="row my-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="textFilter"
|
||||
type="search"
|
||||
id="f_q"
|
||||
name="f[q]"
|
||||
placeholder="Chercher dans la liste"
|
||||
class="form-control"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-misc"
|
||||
>
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row my-2">
|
||||
<legend class="col-form-label col-sm-4 required">
|
||||
Date du document
|
||||
</legend>
|
||||
<div class="col-sm-8 pt-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Du</span>
|
||||
<input
|
||||
v-model="dateFromFilter"
|
||||
type="date"
|
||||
id="f_dateRanges_dateRange_from"
|
||||
name="f[dateRanges][dateRange][from]"
|
||||
class="form-control"
|
||||
/>
|
||||
<span class="input-group-text">Au</span>
|
||||
<input
|
||||
v-model="dateToFilter"
|
||||
type="date"
|
||||
id="f_dateRanges_dateRange_to"
|
||||
name="f[dateRanges][dateRange][to]"
|
||||
class="form-control"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-2">
|
||||
<legend
|
||||
class="col-form-label col-sm-4 required"
|
||||
>
|
||||
Date du document
|
||||
</legend>
|
||||
<div class="col-sm-8 pt-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Du</span>
|
||||
<input
|
||||
v-model="dateFromFilter"
|
||||
type="date"
|
||||
id="f_dateRanges_dateRange_from"
|
||||
name="f[dateRanges][dateRange][from]"
|
||||
class="form-control"
|
||||
/>
|
||||
<span class="input-group-text">Au</span>
|
||||
<input
|
||||
v-model="dateToFilter"
|
||||
type="date"
|
||||
id="f_dateRanges_dateRange_to"
|
||||
name="f[dateRanges][dateRange][to]"
|
||||
class="form-control"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row my-2">
|
||||
<div class="col-sm-4 col-form-label">Filtrer par</div>
|
||||
<div class="col-sm-8 pt-2">
|
||||
<div class="form-check" v-for="p in availablePlaces" :key="p">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="placesFilter"
|
||||
name="f[checkboxes][places][]"
|
||||
class="form-check-input"
|
||||
:value="p"
|
||||
/>
|
||||
<label class="form-check-label">{{ placeTrans(p) }}</label>
|
||||
</div>
|
||||
<div class="row my-2">
|
||||
<div class="col-sm-4 col-form-label">
|
||||
Filtrer par
|
||||
</div>
|
||||
<div class="col-sm-8 pt-2">
|
||||
<div
|
||||
class="form-check"
|
||||
v-for="p in availablePlaces"
|
||||
:key="p"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="placesFilter"
|
||||
name="f[checkboxes][places][]"
|
||||
class="form-check-input"
|
||||
:value="p"
|
||||
/>
|
||||
<label class="form-check-label">{{
|
||||
placeTrans(p)
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="genericDocs.length > 0" class="flex-table chill-task-list">
|
||||
<pick-generic-doc-item
|
||||
v-for="g in filteredDocuments"
|
||||
:key="g.uniqueKey"
|
||||
:accompanying-period-id="accompanyingPeriodId"
|
||||
:genericDoc="g"
|
||||
:is-picked="isPicked(g)"
|
||||
@pickGenericDoc="onPickDocument"
|
||||
@removeGenericDoc="onRemoveGenericDoc"
|
||||
></pick-generic-doc-item>
|
||||
<div v-if="genericDocs.length > 0" class="flex-table chill-task-list">
|
||||
<pick-generic-doc-item
|
||||
v-for="g in filteredDocuments"
|
||||
:key="g.uniqueKey"
|
||||
:accompanying-period-id="accompanyingPeriodId"
|
||||
:genericDoc="g"
|
||||
:is-picked="isPicked(g)"
|
||||
@pickGenericDoc="onPickDocument"
|
||||
@removeGenericDoc="onRemoveGenericDoc"
|
||||
></pick-generic-doc-item>
|
||||
</div>
|
||||
<div v-else class="text-center chill-no-data-statement">
|
||||
Aucun document dans ce parcours
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center chill-no-data-statement">
|
||||
Aucun document dans ce parcours
|
||||
<div v-else>
|
||||
<div class="d-flex align-items-center">
|
||||
<strong>Chargement…</strong>
|
||||
<div
|
||||
class="spinner-border ms-auto"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="d-flex align-items-center">
|
||||
<strong>Chargement…</strong>
|
||||
<div
|
||||
class="spinner-border ms-auto"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -3,21 +3,23 @@ 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 {
|
||||
accompanyingPeriodId: number;
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
workflow: EntityWorkflow | null;
|
||||
accompanyingPeriodId: number;
|
||||
toRemove: GenericDocForAccompanyingPeriod[];
|
||||
}
|
||||
|
||||
type PickGenericDocType = InstanceType<typeof PickGenericDoc>;
|
||||
|
||||
const props = defineProps<PickGenericDocModalProps>();
|
||||
const emit = defineEmits<{
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
(
|
||||
e: "pickGenericDoc",
|
||||
payload: { genericDoc: GenericDocForAccompanyingPeriod },
|
||||
): void;
|
||||
}>();
|
||||
const picker = useTemplateRef<PickGenericDocType>("picker");
|
||||
const modalOpened = ref<boolean>(false);
|
||||
@@ -27,87 +29,88 @@ const modalClasses = { "modal-xl": true, "modal-dialog-scrollable": true };
|
||||
const numberOfPicked = computed<number>(() => pickeds.value.length);
|
||||
|
||||
const onPicked = ({
|
||||
genericDoc,
|
||||
genericDoc,
|
||||
}: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => {
|
||||
pickeds.value.push(genericDoc);
|
||||
pickeds.value.push(genericDoc);
|
||||
};
|
||||
|
||||
const onRemove = ({
|
||||
genericDoc,
|
||||
genericDoc,
|
||||
}: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => {
|
||||
const index = pickeds.value.findIndex(
|
||||
(item) => item.uniqueKey === genericDoc.uniqueKey,
|
||||
);
|
||||
const index = pickeds.value.findIndex(
|
||||
(item) => item.uniqueKey === genericDoc.uniqueKey,
|
||||
);
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error("Remove generic doc that doesn't exist");
|
||||
}
|
||||
if (index === -1) {
|
||||
throw new Error("Remove generic doc that doesn't exist");
|
||||
}
|
||||
|
||||
pickeds.value.splice(index, 1);
|
||||
pickeds.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
for (let genericDoc of pickeds.value) {
|
||||
emit("pickGenericDoc", { genericDoc });
|
||||
}
|
||||
pickeds.value = [];
|
||||
closeModal();
|
||||
for (let genericDoc of pickeds.value) {
|
||||
emit("pickGenericDoc", { genericDoc });
|
||||
}
|
||||
pickeds.value = [];
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const closeModal = function () {
|
||||
modalOpened.value = false;
|
||||
modalOpened.value = false;
|
||||
};
|
||||
|
||||
const openModal = function () {
|
||||
modalOpened.value = true;
|
||||
modalOpened.value = true;
|
||||
};
|
||||
|
||||
defineExpose({ openModal, closeModal });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<modal
|
||||
v-if="modalOpened"
|
||||
@close="closeModal"
|
||||
:modal-dialog-class="modalClasses"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<h2 class="modal-title">Ajouter une pièce jointe</h2>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<pick-generic-doc
|
||||
:accompanying-period-id="props.accompanyingPeriodId"
|
||||
:to-remove="props.toRemove"
|
||||
:picked-list="pickeds"
|
||||
ref="picker"
|
||||
@pickGenericDoc="onPicked"
|
||||
@removeGenericDoc="onRemove"
|
||||
></pick-generic-doc>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<ul v-if="numberOfPicked > 0" class="record_actions">
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-chill-green text-white"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<template v-if="numberOfPicked > 1">
|
||||
<i class="fa fa-plus"></i> Ajouter {{ numberOfPicked }} pièces
|
||||
jointes
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-plus"></i> Ajouter une pièce jointe
|
||||
</template>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</modal>
|
||||
<modal
|
||||
v-if="modalOpened"
|
||||
@close="closeModal"
|
||||
:modal-dialog-class="modalClasses"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<h2 class="modal-title">Ajouter une pièce jointe</h2>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<pick-generic-doc
|
||||
:workflow="props.workflow"
|
||||
:accompanying-period-id="props.accompanyingPeriodId"
|
||||
:to-remove="props.toRemove"
|
||||
:picked-list="pickeds"
|
||||
ref="picker"
|
||||
@pickGenericDoc="onPicked"
|
||||
@removeGenericDoc="onRemove"
|
||||
></pick-generic-doc>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<ul v-if="numberOfPicked > 0" class="record_actions">
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-chill-green text-white"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<template v-if="numberOfPicked > 1">
|
||||
<i class="fa fa-plus"></i> Ajouter
|
||||
{{ numberOfPicked }} pièces jointes
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-plus"></i> Ajouter une pièce jointe
|
||||
</template>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,83 +1,79 @@
|
||||
<template>
|
||||
<div class="d-grid gap-2 my-3">
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
type="button"
|
||||
v-if="!subscriberFinal"
|
||||
@click="subscribeTo('subscribe', 'final')"
|
||||
>
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
type="button"
|
||||
v-if="subscriberFinal"
|
||||
@click="subscribeTo('unsubscribe', 'final')"
|
||||
>
|
||||
<i class="fa fa-times fa-fw"></i>
|
||||
{{ trans(WORKFLOW_UNSUBSCRIBE_FINAL) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
type="button"
|
||||
v-if="!subscriberStep"
|
||||
@click="subscribeTo('subscribe', 'step')"
|
||||
>
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
type="button"
|
||||
v-if="subscriberStep"
|
||||
@click="subscribeTo('unsubscribe', 'step')"
|
||||
>
|
||||
<i class="fa fa-times fa-fw"></i>
|
||||
{{ trans(WORKFLOW_UNSUBSCRIBE_ALL_STEPS) }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="d-grid gap-2 my-3">
|
||||
<button
|
||||
class="btn btn-outline-primary text-start d-flex align-items-center"
|
||||
:class="{ active: subscriberFinal }"
|
||||
type="button"
|
||||
@click="
|
||||
subscribeTo(
|
||||
subscriberFinal ? 'unsubscribe' : 'subscribe',
|
||||
'final',
|
||||
)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-fw me-2"
|
||||
:class="subscriberFinal ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
></i>
|
||||
<span>{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-outline-primary text-start d-flex align-items-center"
|
||||
:class="{ active: subscriberStep }"
|
||||
type="button"
|
||||
@click="
|
||||
subscribeTo(
|
||||
subscriberStep ? 'unsubscribe' : 'subscribe',
|
||||
'step',
|
||||
)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-fw me-2"
|
||||
:class="subscriberStep ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
></i>
|
||||
<span>{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
|
||||
import { defineProps, defineEmits } from "vue";
|
||||
import {
|
||||
trans,
|
||||
WORKFLOW_SUBSCRIBE_FINAL,
|
||||
WORKFLOW_UNSUBSCRIBE_FINAL,
|
||||
WORKFLOW_SUBSCRIBE_ALL_STEPS,
|
||||
WORKFLOW_UNSUBSCRIBE_ALL_STEPS,
|
||||
trans,
|
||||
WORKFLOW_SUBSCRIBE_FINAL,
|
||||
WORKFLOW_SUBSCRIBE_ALL_STEPS,
|
||||
} from "translator";
|
||||
|
||||
// props
|
||||
const props = defineProps({
|
||||
entityWorkflowId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
subscriberStep: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
subscriberFinal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
entityWorkflowId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
subscriberStep: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
subscriberFinal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
//methods
|
||||
const subscribeTo = (step, to) => {
|
||||
let params = new URLSearchParams();
|
||||
params.set("subscribe", to);
|
||||
let params = new URLSearchParams();
|
||||
params.set("subscribe", to);
|
||||
|
||||
const url =
|
||||
`/api/1.0/main/workflow/${props.entityWorkflowId}/${step}?` +
|
||||
params.toString();
|
||||
const url =
|
||||
`/api/1.0/main/workflow/${props.entityWorkflowId}/${step}?` +
|
||||
params.toString();
|
||||
|
||||
makeFetch("POST", url).then((response) => {
|
||||
emit("subscriptionUpdated", response);
|
||||
});
|
||||
makeFetch("POST", url).then((response) => {
|
||||
emit("subscriptionUpdated", response);
|
||||
});
|
||||
};
|
||||
|
||||
// emit
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
<template>
|
||||
<transition name="modal">
|
||||
<div class="modal-mask" v-if="show">
|
||||
<!-- :: styles bootstrap :: -->
|
||||
<div
|
||||
class="modal fade show"
|
||||
style="display: block"
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
>
|
||||
<div class="modal-dialog" :class="modalDialogClass || {}">
|
||||
<div class="modal-content">
|
||||
<transition name="modal">
|
||||
<div class="modal-mask">
|
||||
<!-- :: styles bootstrap :: -->
|
||||
<div
|
||||
class="modal-header d-flex justify-content-between align-items-center"
|
||||
class="modal fade show"
|
||||
style="display: block"
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
<button class="close btn ms-auto" @click="emits('close')">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="modal-dialog" :class="props.modalDialogClass || {}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<slot name="header"></slot>
|
||||
<button class="close btn" @click="emits('close')">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="body-head">
|
||||
<slot name="body-head"></slot>
|
||||
</div>
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="modal-footer" v-if="!hideFooter">
|
||||
<button
|
||||
class="btn btn-cancel"
|
||||
@click="emits('close')"
|
||||
>
|
||||
{{ trans(MODAL_ACTION_CLOSE) }}
|
||||
</button>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="body-head">
|
||||
<slot name="body-head"></slot>
|
||||
</div>
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="modal-footer" v-if="!hideFooter">
|
||||
<button class="btn btn-cancel" @click="emits('close')">
|
||||
{{ trans(MODAL_ACTION_CLOSE) }}
|
||||
</button>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<!-- :: end styles bootstrap :: -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- :: end styles bootstrap :: -->
|
||||
</div>
|
||||
</transition>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -64,7 +65,7 @@ const props = withDefaults(defineProps<ModalProps>(), {
|
||||
});
|
||||
|
||||
const emits = defineEmits<{
|
||||
close: [];
|
||||
close: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
@@ -73,17 +74,19 @@ const emits = defineEmits<{
|
||||
* This is a mask behind the modal.
|
||||
*/
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
transition: opacity 0.3s ease;
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.modal-header .close {
|
||||
border-top-right-radius: 0.3rem;
|
||||
border-top-right-radius: 0.3rem;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
@@ -94,23 +97,23 @@ const emits = defineEmits<{
|
||||
* these styles.
|
||||
*/
|
||||
.modal-enter {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
.modal-leave-active {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
.modal-enter .modal-container,
|
||||
.modal-leave-active .modal-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
h3.modal-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.modal-footer {
|
||||
button:first-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
button:first-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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