Compare commits

..

7 Commits

Author SHA1 Message Date
ed556d9ee8 FIX [review] process review and make minor visual changes 2023-05-31 09:47:48 +02:00
ff03299f80 FEATURE [workflow][doc] scroll immediately to document in workflow and let background flash 2023-05-25 17:06:10 +02:00
5749660760 Merge branch '108-fix-acc-period-transition-notification' into 'master'
Fixed: Do not send a confirmation message when the accompanying period is back to CONFIRM state

Closes #108

See merge request Chill-Projet/chill-bundles!549
2023-05-25 13:30:15 +00:00
b679dbe26c Fixed: Do not send a confirmation message when period is mark_active back 2023-05-25 15:24:14 +02:00
6c3fa5cb98 Fixed: [UI] in designation and redispatch list, the period's statuses
were'nt shown correctly
2023-05-25 15:22:52 +02:00
6bdd1f31d3 Merge branch '98-rewrite-download-button' into 'master'
Fixed: vue downloadButton: add more log and various improvements

See merge request Chill-Projet/chill-bundles!547
2023-05-25 10:42:10 +00:00
ff3dab0934 Fixed: vue downloadButton: add more log and various improvements
- create a dedicated button for opening
- use nextTick before clicking on the "opening" button
2023-05-25 12:36:47 +02:00
9 changed files with 124 additions and 45 deletions

View File

@@ -1,52 +1,90 @@
<template> <template>
<a :class="props.classes" @click="download_and_open($event)"> <a v-if="!state.is_ready" :class="props.classes" @click="download_and_open($event)">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
Télécharger Télécharger
</a> </a>
<a v-else :class="props.classes" target="_blank" :type="props.storedObject.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button">
<i class="fa fa-external-link"></i>
Ouvrir
</a>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {reactive} from "vue"; import {reactive, ref, nextTick, onMounted} from "vue";
import {build_download_info_link, download_and_decrypt_doc} from "./helpers"; import {build_download_info_link, download_and_decrypt_doc} from "./helpers";
import mime from "mime"; import mime from "mime";
import {StoredObject} from "../../types"; import {StoredObject} from "../../types";
interface DownloadButtonConfig { interface DownloadButtonConfig {
storedObject: StoredObject, storedObject: StoredObject,
classes: {[k: string]: boolean}, classes: { [k: string]: boolean },
filename?: string, filename?: string,
} }
interface DownloadButtonState { interface DownloadButtonState {
is_ready: boolean is_ready: boolean,
is_running: boolean,
href_url: string,
} }
const props = defineProps<DownloadButtonConfig>(); const props = defineProps<DownloadButtonConfig>();
const state: DownloadButtonState = reactive({is_ready: false}); const state: DownloadButtonState = reactive({is_ready: false, is_running: false, href_url: "#"});
const open_button = ref<HTMLAnchorElement | null>(null);
function buildDocumentName(): string {
const document_name = props.filename || 'document';
const ext = mime.getExtension(props.storedObject.type);
if (null !== ext) {
return document_name + '.' + ext;
}
return document_name;
}
async function download_and_open(event: Event): Promise<void> { async function download_and_open(event: Event): Promise<void> {
const button = event.target as HTMLAnchorElement; const button = event.target as HTMLAnchorElement;
if (!state.is_ready) { if (state.is_running) {
event.preventDefault(); console.log('state is running, aborting');
return;
}
state.is_running = true;
if (state.is_ready) {
console.log('state is ready. This should not happens');
return;
}
const urlInfo = build_download_info_link(props.storedObject.filename); const urlInfo = build_download_info_link(props.storedObject.filename);
let raw;
const raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv)); try {
raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv));
button.href = window.URL.createObjectURL(raw); } catch (e) {
button.target = '_blank'; console.error("error while downloading and decrypting document");
button.type = props.storedObject.type; console.error(e);
throw e;
button.download = props.filename || 'document';
const ext = mime.getExtension(props.storedObject.type);
if (null !== ext) {
button.download = button.download + '.' + ext;
} }
console.log('document downloading (and decrypting) successfully');
console.log('creating the url')
state.href_url = window.URL.createObjectURL(raw);
console.log('url created', state.href_url);
state.is_running = false;
state.is_ready = true; state.is_ready = true;
console.log('new button marked as ready');
console.log('will click on button');
button.click(); console.log('openbutton is now', open_button.value);
}
await nextTick();
console.log('next tick actions');
console.log('openbutton after next tick', open_button.value);
open_button.value?.click();
console.log('open button should have been clicked');
} }
</script> </script>

View File

@@ -149,16 +149,21 @@ async function download_and_decrypt_doc(urlGenerator: string, keyData: JsonWebKe
} }
if (iv.length === 0) { if (iv.length === 0) {
console.log('returning document immediatly');
return rawResponse.blob(); return rawResponse.blob();
} }
console.log('start decrypting doc');
const rawBuffer = await rawResponse.arrayBuffer(); const rawBuffer = await rawResponse.arrayBuffer();
try { try {
const key = await window.crypto.subtle const key = await window.crypto.subtle
.importKey('jwk', keyData, { name: algo }, false, ['decrypt']); .importKey('jwk', keyData, { name: algo }, false, ['decrypt']);
console.log('key created');
const decrypted = await window.crypto.subtle const decrypted = await window.crypto.subtle
.decrypt({ name: algo, iv: iv }, key, rawBuffer); .decrypt({ name: algo, iv: iv }, key, rawBuffer);
console.log('doc decrypted');
return Promise.resolve(new Blob([decrypted])); return Promise.resolve(new Blob([decrypted]));
} catch (e) { } catch (e) {

View File

@@ -51,7 +51,10 @@ class UserRefEventSubscriber implements EventSubscriberInterface
public function onStateEntered(EnteredEvent $enteredEvent): void public function onStateEntered(EnteredEvent $enteredEvent): void
{ {
if ($enteredEvent->getMarking()->has(AccompanyingPeriod::STEP_CONFIRMED)) { if (
$enteredEvent->getMarking()->has(AccompanyingPeriod::STEP_CONFIRMED)
and $enteredEvent->getTransition()->getName() === 'confirm'
) {
$this->onPeriodConfirmed($enteredEvent->getSubject()); $this->onPeriodConfirmed($enteredEvent->getSubject());
} }
} }

View File

@@ -122,7 +122,8 @@
<add-evaluation <add-evaluation
v-for="e in pickedEvaluations" v-for="e in pickedEvaluations"
v-bind:key="e.key" v-bind:key="e.key"
v-bind:evaluation="e"> v-bind:evaluation="e"
v-bind:docAnchorId="this.docAnchorId">
</add-evaluation> </add-evaluation>
<!-- box to add new evaluation --> <!-- box to add new evaluation -->
@@ -389,6 +390,7 @@ export default {
i18n, i18n,
data() { data() {
return { return {
docAnchorId: null,
isExpanded: false, isExpanded: false,
editor: ClassicEditor, editor: ClassicEditor,
showAddObjective: false, showAddObjective: false,
@@ -428,6 +430,13 @@ export default {
}, },
}; };
}, },
beforeMount() {
const urlParams = new URLSearchParams(window.location.search);
this.docAnchorId = urlParams.get('doc_id');
},
mounted() {
this.scrollToElement(this.docAnchorId);
},
computed: { computed: {
...mapState([ ...mapState([
'work', 'work',
@@ -559,7 +568,7 @@ export default {
}); });
}, },
saveFormOnTheFly(payload) { saveFormOnTheFly(payload) {
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data); // console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
let body = { type: payload.type }; let body = { type: payload.type };
body.name = payload.data.text; body.name = payload.data.text;
@@ -581,6 +590,12 @@ export default {
this.$toast.open({message: 'An error occurred'}); this.$toast.open({message: 'An error occurred'});
} }
}) })
},
scrollToElement(docAnchorId) {
const documentEl = document.getElementById(`document_${docAnchorId}`);
if (documentEl) {
documentEl.scrollIntoView({behavior: 'smooth'});
}
} }
} }
}; };

View File

@@ -11,7 +11,7 @@
</div> </div>
<div> <div>
<form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation"></form-evaluation> <form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation" :docAnchorId="docAnchorId"></form-evaluation>
<ul class="record_actions"> <ul class="record_actions">
<li v-if="evaluation.workflows_availables.length > 0"> <li v-if="evaluation.workflows_availables.length > 0">
@@ -85,7 +85,7 @@ export default {
Modal, Modal,
ListWorkflowModal, ListWorkflowModal,
}, },
props: ['evaluation'], props: ['evaluation', 'docAnchorId'],
i18n, i18n,
data() { data() {
return { return {

View File

@@ -80,12 +80,13 @@
<div class="flex-table"> <div class="flex-table">
<div class="item-bloc" v-for="(d, i) in evaluation.documents" :key="d.id"> <div class="item-bloc" v-for="(d, i) in evaluation.documents" :key="d.id">
<div class="item-row"> <div :id="`document_${d.id}`" class="item-row">
<div class="input-group input-group-lg mb-3 row"> <div class="input-group input-group-lg mb-3 row">
<label class="col-sm-3 col-form-label">Titre du document:</label> <label class="col-sm-3 col-form-label">Titre du document:</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input <input
class="form-control document-title" class="form-control document-title"
:class="[parseInt(this.docAnchorId) === d.id ? 'bg-blink' : 'nothing']"
type="text" type="text"
:value="d.title" :value="d.title"
:id="d.id" :id="d.id"
@@ -221,7 +222,7 @@ const i18n = {
export default { export default {
name: "FormEvaluation", name: "FormEvaluation",
props: ['evaluation'], props: ['evaluation', 'docAnchorId'],
components: { components: {
ckeditor: CKEditor.component, ckeditor: CKEditor.component,
PickTemplate, PickTemplate,
@@ -402,4 +403,19 @@ export default {
ul.document-upload { ul.document-upload {
justify-content: flex-start; justify-content: flex-start;
} }
.bg-blink{
color: #050000;
padding: 10px;
display: inline-block;
border-radius: 5px;
animation: blinkingBackground 2.2s infinite;
animation-iteration-count: 2;
}
@keyframes blinkingBackground{
0% { background-color: #ed776d;}
50% { background-color: #ffffff;}
100% { background-color: #ed776d;}
}
</style> </style>

View File

@@ -207,7 +207,7 @@
</template> </template>
<script> <script>
import {dateToISO, ISOToDate} from 'ChillMainAssets/chill/js/date'; import {dateToISO} from 'ChillMainAssets/chill/js/date';
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue'; import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue'; import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'; import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
@@ -262,7 +262,7 @@ export default {
}, },
birthdate: function () { birthdate: function () {
if (this.person.birthdate !== null || this.person.birthdate === "undefined") { if (this.person.birthdate !== null || this.person.birthdate === "undefined") {
return ISOToDate(this.person.birthdate.datetime); return new Date(this.person.birthdate.datetime);
} else { } else {
return ""; return "";
} }

View File

@@ -16,11 +16,13 @@
</div> </div>
<div class="wh-col"> <div class="wh-col">
{% if period.step == 'DRAFT' %} {% if period.step == 'DRAFT' %}
<span class="badge bg-secondary">{{- 'Draft'|trans|upper -}}</span> <span class="badge bg-secondary" style="font-size: 85%;" title="{{ 'course.draft'|trans|e('html_attr') }}">{{ 'course.draft'|trans }}</span>
{% elseif period.step == 'CONFIRMED' %} {% elseif period.step == 'CLOSED' %}
<span class="badge bg-primary">{{- 'Confirmed'|trans|upper -}}</span> <span class="badge bg-danger" style="font-size: 85%;" title="{{ 'course.closed'|trans|e('html_attr') }}">{{ 'course.closed'|trans }}</span>
{% else %} {% elseif period.step == 'CONFIRMED_INACTIVE_SHORT' %}
<span class="badge bg-danger">{{- 'Closed'|trans|upper -}}</span> <span class="badge bg-chill-yellow text-primary" style="font-size: 85%;" title="{{ 'course.inactive_short'|trans|e('html_attr') }}">{{ 'course.inactive_short'|trans }}</span>
{% elseif period.step == 'CONFIRMED_INACTIVE_LONG' %}
<span class="badge bg-danger" style="font-size: 85%;" title="{{ 'course.inactive_long'|trans|e('html_attr') }}">{{ 'course.inactive_long'|trans }}</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -123,7 +123,7 @@
<ul class="record_actions"> <ul class="record_actions">
<li>{{ doc.storedObject|chill_document_button_group(doc.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork)) }}</li> <li>{{ doc.storedObject|chill_document_button_group(doc.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork)) }}</li>
<li> <li>
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id}) }}"> <a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id, 'doc_id': doc.id}) }}">
{{ 'Show'|trans }} {{ 'Show'|trans }}
</a> </a>
</li> </li>