Merge branch 'button-signature-zone' into 'master'

Add button unique signature zone

See merge request Chill-Projet/chill-bundles!812
This commit is contained in:
Julien Fastré 2025-04-15 13:09:54 +00:00
commit 8ca377d5d4
3 changed files with 152 additions and 57 deletions

View File

@ -0,0 +1,6 @@
kind: Feature
body: 'Signature: add a button to go directly to the signature zone, even if there is only one'
time: 2025-04-15T15:03:24.436112828+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@ -2,26 +2,28 @@
<teleport to="body"> <teleport to="body">
<modal v-if="modalOpen" @close="modalOpen = false"> <modal v-if="modalOpen" @close="modalOpen = false">
<template v-slot:header> <template v-slot:header>
<h2>{{ $t("signature_confirmation") }}</h2> <h2>{{ trans(SIGNATURES_SIGNATURE_CONFIRMATION) }}</h2>
</template> </template>
<template v-slot:body> <template v-slot:body>
<div class="signature-modal-body text-center" v-if="loading"> <div class="signature-modal-body text-center" v-if="loading">
<p>{{ $t("electronic_signature_in_progress") }}</p> <p>
{{ trans(SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS) }}
</p>
<div class="loading"> <div class="loading">
<i <i
class="fa fa-circle-o-notch fa-spin fa-3x" class="fa fa-circle-o-notch fa-spin fa-3x"
:title="$t('loading')" :title="trans(SIGNATURES_LOADING)"
></i> ></i>
</div> </div>
</div> </div>
<div class="signature-modal-body text-center" v-else> <div class="signature-modal-body text-center" v-else>
<p>{{ $t("you_are_going_to_sign") }}</p> <p>{{ trans(SIGNATURES_YOU_ARE_GOING_TO_SIGN) }}</p>
<p>{{ $t("are_you_sure") }}</p> <p>{{ trans(SIGNATURES_ARE_YOU_SURE) }}</p>
</div> </div>
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<button class="btn btn-action" @click.prevent="confirmSign"> <button class="btn btn-action" @click.prevent="confirmSign">
{{ $t("yes") }} {{ trans(SIGNATURES_YES) }}
</button> </button>
</template> </template>
</modal> </modal>
@ -82,28 +84,39 @@
@change="toggleMultiPage" @change="toggleMultiPage"
/> />
<label class="form-check-label" for="checkboxMulti"> <label class="form-check-label" for="checkboxMulti">
{{ $t("all_pages") }} {{ trans(SIGNATURES_ALL_PAGES) }}
</label> </label>
</template> </template>
</div> </div>
<div <div
v-if="signature.zones.length > 0" v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-5 p-0 text-center turnSignature" class="col-5 p-0 text-center turnSignature"
> >
<button <button
:disabled="isFirstSignatureZone" class="btn btn-light btn-sm"
@click="goToSignatureZoneUnique"
>
{{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
</div>
<div
v-if="signature.zones.length > 1"
class="col-5 p-0 text-center turnSignature"
>
<button
:disabled="isFirstSignatureZone()"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(-1)" @click="turnSignature(-1)"
> >
{{ $t("last_zone") }} {{ trans(SIGNATURES_LAST_ZONE) }}
</button> </button>
<span>|</span> <span>|</span>
<button <button
:disabled="isLastSignatureZone" :disabled="isLastSignatureZone()"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(1)" @click="turnSignature(1)"
> >
{{ $t("next_zone") }} {{ trans(SIGNATURES_NEXT_ZONE) }}
</button> </button>
</div> </div>
<div class="col text-end" v-if="signedState !== 'signed'"> <div class="col text-end" v-if="signedState !== 'signed'">
@ -112,9 +125,9 @@
:hidden="!userSignatureZone" :hidden="!userSignatureZone"
@click="undoSign" @click="undoSign"
v-if="signature.zones.length > 1" v-if="signature.zones.length > 1"
:title="$t('choose_another_signature')" :title="trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE)"
> >
{{ $t("another_zone") }} {{ trans(SIGNATURES_ANOTHER_ZONE) }}
</button> </button>
<button <button
class="btn btn-misc btn-sm" class="btn btn-misc btn-sm"
@ -122,7 +135,7 @@
@click="undoSign" @click="undoSign"
v-else v-else
> >
{{ $t("cancel") }} {{ trans(SIGNATURES_CANCEL) }}
</button> </button>
<button <button
v-if="userSignatureZone === null" v-if="userSignatureZone === null"
@ -134,7 +147,7 @@
active: canvasEvent === 'add', active: canvasEvent === 'add',
}" }"
@click="toggleAddZone()" @click="toggleAddZone()"
:title="$t('add_sign_zone')" :title="trans(SIGNATURES_ADD_SIGN_ZONE)"
> >
<template v-if="canvasEvent === 'add'"> <template v-if="canvasEvent === 'add'">
<div <div
@ -186,48 +199,70 @@
@change="toggleMultiPage" @change="toggleMultiPage"
/> />
<label class="form-check-label" for="checkboxMulti"> <label class="form-check-label" for="checkboxMulti">
{{ $t("see_all_pages") }} {{ trans(SIGNATURES_SEE_ALL_PAGES) }}
</label> </label>
</template> </template>
</div> </div>
<div <div
v-if="signature.zones.length > 0 && signedState !== 'signed'" v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-4 d-xl-none text-center turnSignature p-0" class="col-4 d-xl-none text-center turnSignature p-0"
> >
<button <button
:disabled="!hasSignatureZoneSelected"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(-1)" @click="goToSignatureZoneUnique"
> >
{{ $t("last_zone") }} {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
<span>|</span>
<button
:disabled="isLastSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(1)"
>
{{ $t("next_zone") }}
</button> </button>
</div> </div>
<div <div
v-if="signature.zones.length > 0 && signedState !== 'signed'" v-if="signature.zones.length > 1 && signedState !== 'signed'"
class="col-4 d-none d-xl-flex p-0 text-center turnSignature" class="col-4 d-xl-none text-center turnSignature p-0"
> >
<button <button
:disabled="isFirstSignatureZone" :disabled="isFirstSignatureZone()"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(-1)" @click="turnSignature(-1)"
> >
{{ $t("last_sign_zone") }} {{ trans(SIGNATURES_LAST_ZONE) }}
</button> </button>
<span>|</span> <span>|</span>
<button <button
:disabled="isLastSignatureZone" :disabled="isLastSignatureZone()"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(1)" @click="turnSignature(1)"
> >
{{ $t("next_sign_zone") }} {{ trans(SIGNATURES_NEXT_ZONE) }}
</button>
</div>
<div
v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
>
<button
class="btn btn-light btn-sm"
@click="goToSignatureZoneUnique"
>
{{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
</div>
<div
v-if="signature.zones.length > 1 && signedState !== 'signed'"
class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
>
<button
:disabled="isFirstSignatureZone()"
class="btn btn-light btn-sm"
@click="turnSignature(-1)"
>
{{ trans(SIGNATURES_LAST_SIGN_ZONE) }}
</button>
<span>|</span>
<button
:disabled="isLastSignatureZone()"
class="btn btn-light btn-sm"
@click="turnSignature(1)"
>
{{ trans(SIGNATURES_NEXT_SIGN_ZONE) }}
</button> </button>
</div> </div>
<div class="col text-end" v-if="signedState !== 'signed'"> <div class="col text-end" v-if="signedState !== 'signed'">
@ -237,7 +272,7 @@
@click="undoSign" @click="undoSign"
v-if="signature.zones.length > 1" v-if="signature.zones.length > 1"
> >
{{ $t("choose_another_signature") }} {{ trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE) }}
</button> </button>
<button <button
class="btn btn-misc btn-sm" class="btn btn-misc btn-sm"
@ -245,7 +280,7 @@
@click="undoSign" @click="undoSign"
v-else v-else
> >
{{ $t("cancel") }} {{ trans(SIGNATURES_CANCEL) }}
</button> </button>
<button <button
v-if="userSignatureZone === null" v-if="userSignatureZone === null"
@ -257,13 +292,13 @@
active: canvasEvent === 'add', active: canvasEvent === 'add',
}" }"
@click="toggleAddZone()" @click="toggleAddZone()"
:title="$t('add_sign_zone')" :title="trans(SIGNATURES_ADD_SIGN_ZONE)"
> >
<template v-if="canvasEvent !== 'add'"> <template v-if="canvasEvent !== 'add'">
{{ $t("add_zone") }} {{ trans(SIGNATURES_ADD_ZONE) }}
</template> </template>
<template v-else> <template v-else>
{{ $t("click_on_document") }} {{ trans(SIGNATURES_CLICK_ON_DOCUMENT) }}
<div <div
class="spinner-border spinner-border-sm" class="spinner-border spinner-border-sm"
role="status" role="status"
@ -297,10 +332,10 @@
v-if="signedState !== 'signed'" v-if="signedState !== 'signed'"
:href="getReturnPath()" :href="getReturnPath()"
> >
{{ $t("cancel") }} {{ trans(SIGNATURES_CANCEL) }}
</a> </a>
<a class="btn btn-misc" v-else :href="getReturnPath()"> <a class="btn btn-misc" v-else :href="getReturnPath()">
{{ $t("return") }} {{ trans(SIGNATURES_RETURN) }}
</a> </a>
</div> </div>
<div class="col text-end" v-if="signedState !== 'signed'"> <div class="col text-end" v-if="signedState !== 'signed'">
@ -309,7 +344,7 @@
:disabled="!userSignatureZone" :disabled="!userSignatureZone"
@click="sign" @click="sign"
> >
{{ $t("sign") }} {{ trans(SIGNATURES_SIGN) }}
</button> </button>
</div> </div>
<div class="col-4" v-else></div> <div class="col-4" v-else></div>
@ -318,7 +353,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, Ref, computed } from "vue"; import { ref, Ref } from "vue";
import { useToast } from "vue-toast-notification"; import { useToast } from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css"; import "vue-toast-notification/dist/theme-sugar.css";
import { import {
@ -329,13 +364,38 @@ import {
SignedState, SignedState,
ZoomLevel, ZoomLevel,
} from "../../types"; } from "../../types";
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import * as pdfjsLib from "pdfjs-dist"; import * as pdfjsLib from "pdfjs-dist";
import { import {
PDFDocumentProxy, PDFDocumentProxy,
PDFPageProxy, PDFPageProxy,
} from "pdfjs-dist/types/src/display/api"; } from "pdfjs-dist/types/src/display/api";
import {
SIGNATURES_YES,
SIGNATURES_ARE_YOU_SURE,
SIGNATURES_YOU_ARE_GOING_TO_SIGN,
SIGNATURES_SIGNATURE_CONFIRMATION,
SIGNATURES_SIGN,
SIGNATURES_CHOOSE_ANOTHER_SIGNATURE,
SIGNATURES_CANCEL,
SIGNATURES_LAST_SIGN_ZONE,
SIGNATURES_NEXT_SIGN_ZONE,
SIGNATURES_ADD_SIGN_ZONE,
SIGNATURES_CLICK_ON_DOCUMENT,
SIGNATURES_LAST_ZONE,
SIGNATURES_NEXT_ZONE,
SIGNATURES_ADD_ZONE,
SIGNATURES_ANOTHER_ZONE,
SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS,
SIGNATURES_LOADING,
SIGNATURES_RETURN,
SIGNATURES_SEE_ALL_PAGES,
SIGNATURES_ALL_PAGES,
SIGNATURES_GO_TO_SIGNATURE_UNIQUE,
trans,
} from "translator";
// @ts-ignore incredible but the console.log is needed // @ts-ignore incredible but the console.log is needed
import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs"; import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs";
console.log(PdfWorker); console.log(PdfWorker);
@ -416,19 +476,15 @@ const $toast = useToast();
const signature = window.signature; const signature = window.signature;
const isFirstSignatureZone = () => const isFirstSignatureZone = () =>
userSignatureZone.value?.index ? userSignatureZone.value.index < 1 : false; userSignatureZone.value?.index != null
? userSignatureZone.value.index < 1
: false;
const isLastSignatureZone = () => const isLastSignatureZone = () =>
userSignatureZone.value?.index userSignatureZone.value?.index
? userSignatureZone.value.index >= signature.zones.length - 1 ? userSignatureZone.value.index >= signature.zones.length - 1
: false; : false;
/**
* Return true if the user has selected a user zone (existing on the doc or created by the user)
*/
const hasSignatureZoneSelected = computed<boolean>(
() => userSignatureZone.value !== null,
);
const setZoomLevel = async (zoomLevel: string) => { const setZoomLevel = async (zoomLevel: string) => {
zoom.value = Number.parseFloat(zoomLevel); zoom.value = Number.parseFloat(zoomLevel);
await resetPages(); await resetPages();
@ -600,6 +656,15 @@ const turnPage = async (upOrDown: number) => {
} }
}; };
const selectZoneInCanvas = (signatureZone: SignatureZone) => {
page.value = signatureZone.PDFPage.index + 1;
const canvas = getCanvas(signatureZone.PDFPage.index + 1);
selectZone(signatureZone, canvas);
canvas.scrollIntoView();
};
const goToSignatureZoneUnique = () => selectZoneInCanvas(signature.zones[0]);
const turnSignature = async (upOrDown: number) => { const turnSignature = async (upOrDown: number) => {
let zoneIndex = userSignatureZone.value?.index ?? -1; let zoneIndex = userSignatureZone.value?.index ?? -1;
if (zoneIndex < -1) { if (zoneIndex < -1) {
@ -612,10 +677,7 @@ const turnSignature = async (upOrDown: number) => {
} }
let currentZone = signature.zones[zoneIndex]; let currentZone = signature.zones[zoneIndex];
if (currentZone) { if (currentZone) {
page.value = currentZone.PDFPage.index + 1; selectZoneInCanvas(currentZone);
const canvas = getCanvas(currentZone.PDFPage.index + 1);
selectZone(currentZone, canvas);
canvas.scrollIntoView();
} }
}; };

View File

@ -99,3 +99,30 @@ CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE: Modifier un document
entity_display_title: entity_display_title:
Document (n°%doc%): "Document (n°%doc%)" Document (n°%doc%): "Document (n°%doc%)"
Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval% Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval%
# SIGNATURES
signatures:
yes: Oui
are_you_sure: Êtes-vous sûr·e?
you_are_going_to_sign: Vous allez signer le document
signature_confirmation: Confirmation de la signature
sign: Signer
choose_another_signature: Choisir une autre zone
cancel: Annuler
last_sign_zone: Zone de signature précédente
next_sign_zone: Zone de signature suivante
add_sign_zone: Ajouter une zone de signature
click_on_document: Cliquer sur le document
last_zone: Zone précédente
next_zone: Zone suivante
add_zone: Ajouter une zone
another_zone: Autre zone
electronic_signature_in_progress: Signature électronique en cours...
loading: Chargement...
remove_sign_zone: Enlever la zone
return: Retour
see_all_pages: Voir toutes les pages
all_pages: Toutes les pages
go_to_signature_unique: Aller à la zone de signature