feat: enhance document actions in UI

- Add edit and delete options in translation files
- Refactor `DocumentsList.vue` to group replace, delete, move, and duplicate actions in a dropdown menu
This commit is contained in:
2025-08-25 11:26:05 +02:00
parent 2a1762ea8d
commit ab6ab19499
6 changed files with 333 additions and 234 deletions

View File

@@ -78,7 +78,7 @@ function closeModal(): void {
> >
{{ trans(DOCUMENT_ADD) }} {{ trans(DOCUMENT_ADD) }}
</button> </button>
<button v-else @click="openModal" class="btn btn-edit"> <button v-else @click="openModal" class="dropdown-item">
{{ trans(DOCUMENT_REPLACE) }} {{ trans(DOCUMENT_REPLACE) }}
</button> </button>
<modal <modal

View File

@@ -1,176 +1,262 @@
<template> <template>
<div class="row mb-3"> <div class="row mb-3">
<h5>{{ trans(EVALUATION_DOCUMENTS) }} :</h5> <h5>{{ trans(EVALUATION_DOCUMENTS) }} :</h5>
<div class="flex-table"> <div class="flex-table">
<div <div
class="item-bloc" class="item-bloc"
v-for="(d, i) in documents" v-for="(d, i) in documents"
:key="d.id" :key="d.id"
:class="[parseInt(docAnchorId) === d.id ? 'bg-blink' : 'nothing']" :class="[
> parseInt(docAnchorId) === d.id ? 'bg-blink' : 'nothing',
<div :id="'document_' + d.id" class="item-row"> ]"
<div class="input-group input-group-lg mb-3 row"> >
<label class="col-sm-3 col-form-label">Titre du document:</label> <div :id="'document_' + d.id" class="item-row">
<div class="col-sm-9"> <div class="input-group input-group-lg mb-3 row">
<input <label class="col-sm-3 col-form-label"
class="form-control document-title" >Titre du document:</label
type="text" >
:value="d.title" <div class="col-sm-9">
:id="d.id" <input
:data-key="i" class="form-control document-title"
@input="$emit('inputDocumentTitle', $event)" type="text"
/> :value="d.title"
</div> :id="d.id"
</div> :data-key="i"
</div> @input="$emit('inputDocumentTitle', $event)"
<div class="item-row"> />
<div class="item-col item-meta"> </div>
<p v-if="d.createdBy" class="createdBy"> </div>
Créé par {{ d.createdBy.text }}<br />
Le {{ $d(ISOToDatetime(d.createdAt.datetime), "long") }}
</p>
</div>
</div>
<div class="item-row">
<div class="item-col">
<ul class="record_actions">
<li
v-if="
d.workflows_availables.length > 0 || d.workflows.length > 0
"
>
<list-workflow-modal
:workflows="d.workflows"
:allowCreate="true"
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
:relatedEntityId="d.id"
:workflowsAvailables="d.workflows_availables"
:preventDefaultMoveToGenerate="true"
:goToGenerateWorkflowPayload="{ doc: d }"
@go-to-generate-workflow="
$emit('goToGenerateWorkflow', $event)
"
></list-workflow-modal>
</li>
<li>
<button
v-if="AmIRefferer"
class="btn btn-notify"
@click="$emit('goToGenerateNotification', d, false)"
></button>
<template v-else>
<button
id="btnGroupNotifyButtons"
type="button"
class="btn btn-notify dropdown-toggle"
:title="trans(EVALUATION_NOTIFICATION_SEND)"
data-bs-toggle="dropdown"
aria-expanded="false"
>
&nbsp;
</button>
<ul
class="dropdown-menu"
aria-labelledby="btnGroupNotifyButtons"
>
<li>
<a
class="dropdown-item"
@click="$emit('goToGenerateNotification', d, true)"
>
{{ trans(EVALUATION_NOTIFICATION_NOTIFY_REFERRER) }}
</a>
</li>
<li>
<a
class="dropdown-item"
@click="$emit('goToGenerateNotification', d, false)"
>
{{ trans(EVALUATION_NOTIFICATION_NOTIFY_ANY) }}
</a>
</li>
</ul>
</template>
</li>
<li>
<document-action-buttons-group
:stored-object="d.storedObject"
:filename="d.title"
:can-edit="true"
:execute-before-leave="submitBeforeLeaveToEditor"
:davLink="d.storedObject._links?.dav_link.href"
:davLinkExpiration="
d.storedObject._links?.dav_link.expiration
"
@on-stored-object-status-change="
$emit('statusDocumentChanged', $event)
"
></document-action-buttons-group>
</li>
<li v-if="Number.isInteger(d.id)">
<div class="duplicate-dropdown">
<button
class="btn btn-duplicate dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ trans(EVALUATION_DOCUMENT_DUPLICATE) }}
</button>
<ul class="dropdown-menu">
<li>
<a
class="dropdown-item"
@click="$emit('duplicateDocument', d)"
>{{ trans(EVALUATION_DOCUMENT_DUPLICATE_HERE) }}</a
>
</li>
<li>
<a
class="dropdown-item"
@click="prepareDocumentDuplicationToWork(d)"
>{{
trans(
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
)
}}</a
>
</li>
</ul>
</div> </div>
</li> <div class="item-row">
<li v-if="d.storedObject._permissions.canEdit"> <div class="item-col item-meta">
<a <p v-if="d.createdBy" class="createdBy">
class="btn btn-choose" Créé par {{ d.createdBy.text }}<br />
@click="prepareDocumentMoveToWork(d)" Le
>{{ trans(EVALUATION_DOCUMENT_MOVE) }}</a {{
> $d(ISOToDatetime(d.createdAt.datetime), "long")
</li> }}
<li v-if="d.storedObject._permissions.canEdit"> </p>
<drop-file-modal </div>
:existing-doc="d.storedObject" </div>
:allow-remove="false" <div class="item-row">
@add-document=" <div class="item-col">
(arg) => <ul class="record_actions">
$emit( <li
'replaceDocument', v-if="
d, d.workflows_availables.length > 0 ||
arg.stored_object, d.workflows.length > 0
arg.stored_object_version, "
) >
" <list-workflow-modal
></drop-file-modal> :workflows="d.workflows"
</li> :allowCreate="true"
<li v-if="d.workflows.length === 0"> relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
<a class="btn btn-delete" @click="$emit('removeDocument', d)"> :relatedEntityId="d.id"
</a> :workflowsAvailables="
</li> d.workflows_availables
</ul> "
</div> :preventDefaultMoveToGenerate="true"
:goToGenerateWorkflowPayload="{ doc: d }"
@go-to-generate-workflow="
$emit('goToGenerateWorkflow', $event)
"
></list-workflow-modal>
</li>
<li>
<button
v-if="AmIRefferer"
class="btn btn-notify"
@click="
$emit(
'goToGenerateNotification',
d,
false,
)
"
></button>
<template v-else>
<button
id="btnGroupNotifyButtons"
type="button"
class="btn btn-notify dropdown-toggle"
:title="
trans(EVALUATION_NOTIFICATION_SEND)
"
data-bs-toggle="dropdown"
aria-expanded="false"
>
&nbsp;
</button>
<ul
class="dropdown-menu"
aria-labelledby="btnGroupNotifyButtons"
>
<li>
<a
class="dropdown-item"
@click="
$emit(
'goToGenerateNotification',
d,
true,
)
"
>
{{
trans(
EVALUATION_NOTIFICATION_NOTIFY_REFERRER,
)
}}
</a>
</li>
<li>
<a
class="dropdown-item"
@click="
$emit(
'goToGenerateNotification',
d,
false,
)
"
>
{{
trans(
EVALUATION_NOTIFICATION_NOTIFY_ANY,
)
}}
</a>
</li>
</ul>
</template>
</li>
<li>
<document-action-buttons-group
:stored-object="d.storedObject"
:filename="d.title"
:can-edit="true"
:execute-before-leave="
submitBeforeLeaveToEditor
"
:davLink="
d.storedObject._links?.dav_link.href
"
:davLinkExpiration="
d.storedObject._links?.dav_link
.expiration
"
@on-stored-object-status-change="
$emit('statusDocumentChanged', $event)
"
></document-action-buttons-group>
</li>
<li v-if="Number.isInteger(d.id)">
<div class="duplicate-dropdown">
<button
class="btn btn-edit dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ trans(EVALUATION_DOCUMENT_EDIT) }}
</button>
<ul class="dropdown-menu">
<!--delete-->
<li v-if="d.workflows.length === 0">
<a
class="dropdown-item"
@click="
$emit('removeDocument', d)
"
>
{{
trans(
EVALUATION_DOCUMENT_DELETE,
)
}}
</a>
</li>
<!--replace document-->
<li
v-if="
d.storedObject._permissions
.canEdit
"
>
<drop-file-modal
:existing-doc="d.storedObject"
:allow-remove="false"
@add-document="
(arg) =>
$emit(
'replaceDocument',
d,
arg.stored_object,
arg.stored_object_version,
)
"
></drop-file-modal>
</li>
<!--duplicate document-->
<li>
<a
class="dropdown-item"
@click="
$emit(
'duplicateDocument',
d,
)
"
>{{
trans(
EVALUATION_DOCUMENT_DUPLICATE_HERE,
)
}}</a
>
</li>
<li>
<a
class="dropdown-item"
@click="
prepareDocumentDuplicationToWork(
d,
)
"
>{{
trans(
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
)
}}</a
>
</li>
<!--move document-->
<li
v-if="
d.storedObject._permissions
.canEdit
"
>
<a
class="dropdown-item"
@click="
prepareDocumentMoveToWork(d)
"
>{{
trans(
EVALUATION_DOCUMENT_MOVE,
)
}}</a
>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
</div> </div>
</div>
</div> </div>
</div>
<AccompanyingPeriodWorkSelectorModal <AccompanyingPeriodWorkSelectorModal
v-if="showAccompanyingPeriodSelector" v-if="showAccompanyingPeriodSelector"
@@ -188,15 +274,16 @@ import ListWorkflowModal from "ChillMainAssets/vuejs/_components/EntityWorkflow/
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue"; import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue"; import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue";
import { import {
EVALUATION_NOTIFICATION_NOTIFY_REFERRER, EVALUATION_NOTIFICATION_NOTIFY_REFERRER,
EVALUATION_NOTIFICATION_NOTIFY_ANY, EVALUATION_NOTIFICATION_NOTIFY_ANY,
EVALUATION_NOTIFICATION_SEND, EVALUATION_NOTIFICATION_SEND,
EVALUATION_DOCUMENTS, EVALUATION_DOCUMENTS,
EVALUATION_DOCUMENT_MOVE, EVALUATION_DOCUMENT_MOVE,
EVALUATION_DOCUMENT_DUPLICATE, EVALUATION_DOCUMENT_DELETE,
EVALUATION_DOCUMENT_DUPLICATE_HERE, EVALUATION_DOCUMENT_EDIT,
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION, EVALUATION_DOCUMENT_DUPLICATE_HERE,
trans, EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
trans,
} from "translator"; } from "translator";
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import AccompanyingPeriodWorkSelectorModal from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue"; import AccompanyingPeriodWorkSelectorModal from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue";
@@ -218,32 +305,32 @@ const selectedDocumentToDuplicate = ref(null);
const selectedDocumentToMove = ref(null); const selectedDocumentToMove = ref(null);
const prepareDocumentDuplicationToWork = (d) => { const prepareDocumentDuplicationToWork = (d) => {
selectedDocumentToDuplicate.value = d; selectedDocumentToDuplicate.value = d;
/** ensure selectedDocumentToMove is null */ /** ensure selectedDocumentToMove is null */
selectedDocumentToMove.value = null; selectedDocumentToMove.value = null;
showAccompanyingPeriodSelector.value = true; showAccompanyingPeriodSelector.value = true;
}; };
const prepareDocumentMoveToWork = (d) => { const prepareDocumentMoveToWork = (d) => {
selectedDocumentToMove.value = d; selectedDocumentToMove.value = d;
/** ensure selectedDocumentToDuplicate is null */ /** ensure selectedDocumentToDuplicate is null */
selectedDocumentToDuplicate.value = null; selectedDocumentToDuplicate.value = null;
showAccompanyingPeriodSelector.value = true; showAccompanyingPeriodSelector.value = true;
}; };
watch(selectedEvaluation, (val) => { watch(selectedEvaluation, (val) => {
if (selectedDocumentToDuplicate.value) { if (selectedDocumentToDuplicate.value) {
emit("duplicateDocumentToEvaluation", { emit("duplicateDocumentToEvaluation", {
evaluation: val, evaluation: val,
document: selectedDocumentToDuplicate.value, document: selectedDocumentToDuplicate.value,
}); });
} else { } else {
emit("moveDocumentToEvaluation", { emit("moveDocumentToEvaluation", {
evaluationDest: val, evaluationDest: val,
document: selectedDocumentToMove.value, document: selectedDocumentToMove.value,
}); });
} }
}); });
</script> </script>

View File

@@ -20,20 +20,22 @@
<CommentInput :comment="comment" @update:comment="updateComment" /> <CommentInput :comment="comment" @update:comment="updateComment" />
<DocumentsList <DocumentsList
v-if="evaluation.documents.length > 0" v-if="evaluation.documents.length > 0"
:documents="evaluation.documents" :documents="evaluation.documents"
:docAnchorId="docAnchorId" :docAnchorId="docAnchorId"
:accompanyingPeriodId="store.state.work.accompanyingPeriod.id" :accompanyingPeriodId="store.state.work.accompanyingPeriod.id"
@inputDocumentTitle="onInputDocumentTitle" @inputDocumentTitle="onInputDocumentTitle"
@removeDocument="removeDocument" @removeDocument="removeDocument"
@duplicateDocument="duplicateDocument" @duplicateDocument="duplicateDocument"
@duplicate-document-to-evaluation="duplicateDocumentToEvaluation" @duplicate-document-to-evaluation="
@move-document-to-evaluation="moveDocumentToEvaluation" duplicateDocumentToEvaluation
@statusDocumentChanged="onStatusDocumentChanged" "
@goToGenerateWorkflow="goToGenerateWorkflowEvaluationDocument" @move-document-to-evaluation="moveDocumentToEvaluation"
@goToGenerateNotification="goToGenerateDocumentNotification" @statusDocumentChanged="onStatusDocumentChanged"
/> @goToGenerateWorkflow="goToGenerateWorkflowEvaluationDocument"
@goToGenerateNotification="goToGenerateDocumentNotification"
/>
<DocumentActions <DocumentActions
:evaluation="evaluation" :evaluation="evaluation"
@@ -53,7 +55,11 @@ import TimeSpentInput from "./TimeSpentInput.vue";
import CommentInput from "./CommentInput.vue"; import CommentInput from "./CommentInput.vue";
import DocumentsList from "./DocumentsList.vue"; import DocumentsList from "./DocumentsList.vue";
import DocumentActions from "./DocumentActions.vue"; import DocumentActions from "./DocumentActions.vue";
import { trans, EVALUATION_DOCUMENT_DUPLICATE_SUCCESS, EVALUATION_DOCUMENT_MOVE_SUCCESS } from "translator"; import {
trans,
EVALUATION_DOCUMENT_DUPLICATE_SUCCESS,
EVALUATION_DOCUMENT_MOVE_SUCCESS,
} from "translator";
import { useToast } from "vue-toast-notification"; import { useToast } from "vue-toast-notification";
const props = defineProps(["evaluation", "docAnchorId"]); const props = defineProps(["evaluation", "docAnchorId"]);
@@ -259,20 +265,21 @@ function duplicateDocumentToEvaluation({ evaluation, document }) {
} }
function moveDocumentToEvaluation({ evaluationDest, document }) { function moveDocumentToEvaluation({ evaluationDest, document }) {
console.log("dest eval in formEvaluation", evaluationDest); console.log("dest eval in formEvaluation", evaluationDest);
store.dispatch("moveDocumentToEvaluation", { store
evaluationInitial: props.evaluation, .dispatch("moveDocumentToEvaluation", {
evaluationDest: evaluationDest, evaluationInitial: props.evaluation,
document: document, evaluationDest: evaluationDest,
}) document: document,
.then(() => { })
$toast.open({ .then(() => {
message: trans(EVALUATION_DOCUMENT_MOVE_SUCCESS), $toast.open({
}); message: trans(EVALUATION_DOCUMENT_MOVE_SUCCESS),
}) });
.catch((e) => { })
console.log(e); .catch((e) => {
}); console.log(e);
});
} }
function onStatusDocumentChanged(newStatus) { function onStatusDocumentChanged(newStatus) {

View File

@@ -656,7 +656,10 @@ const store = createStore({
{ evaluationInitial, evaluationDest, document }, { evaluationInitial, evaluationDest, document },
) { ) {
try { try {
const response = await moveDocumentToEvaluation(document.id, evaluationDest.id); const response = await moveDocumentToEvaluation(
document.id,
evaluationDest.id,
);
commit("moveDocumentToEvaluation", { commit("moveDocumentToEvaluation", {
evaluationInitial, evaluationInitial,
evaluationDest, evaluationDest,

View File

@@ -21,11 +21,11 @@ export const duplicateDocumentToEvaluation = async (
}; };
export const moveDocumentToEvaluation = async ( export const moveDocumentToEvaluation = async (
document_id: number, document_id: number,
evaluation_id: number, evaluation_id: number,
): Promise<AccompanyingPeriodWorkEvaluationDocument> => { ): Promise<AccompanyingPeriodWorkEvaluationDocument> => {
return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>( return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>(
"POST", "POST",
`/api/1.0/person/accompanying-course-work-evaluation-document/${document_id}/evaluation/${evaluation_id}/move`, `/api/1.0/person/accompanying-course-work-evaluation-document/${document_id}/evaluation/${evaluation_id}/move`,
); );
}; };

View File

@@ -777,6 +777,8 @@ evaluation:
notification_notify_any: Notifier d'autres utilisateurs notification_notify_any: Notifier d'autres utilisateurs
notification_send: Envoyer une notification notification_send: Envoyer une notification
document: document:
edit: Modifier
delete: Supprimer
move: Déplacer move: Déplacer
duplicate: Dupliquer duplicate: Dupliquer
duplicate_here: Dupliquer ici duplicate_here: Dupliquer ici