353 lines
12 KiB
JavaScript
353 lines
12 KiB
JavaScript
const ActionType = {
|
|
Created: 0,
|
|
Saved: 1,
|
|
Sent: 2,
|
|
EmailSent: 3,
|
|
Delivered: 4,
|
|
Seen: 5,
|
|
Signed: 6,
|
|
Rejected: 7,
|
|
}
|
|
|
|
class App {
|
|
constructor(envelopeKey, envelopeReceiver, documentBytes, licenseKey, locale, container) {
|
|
this.container = container ?? `#${this.constructor.name.toLowerCase()}`;
|
|
this.envelopeKey = envelopeKey
|
|
|
|
this.Network = new Network()
|
|
|
|
this.Instance = null
|
|
this.currentDocument = null
|
|
this.currentReceiver = null
|
|
this.signatureCount = 0;
|
|
this.envelopeReceiver = envelopeReceiver;
|
|
this.documentBytes = documentBytes;
|
|
this.licenseKey = licenseKey;
|
|
this.locale = locale;
|
|
}
|
|
|
|
// This function will be called from the ShowEnvelope.razor page
|
|
// and will trigger loading of the Editor Interface
|
|
async init() {
|
|
// Load the envelope from the database
|
|
this.currentDocument = this.envelopeReceiver.envelope.documents[0]
|
|
this.currentReceiver = this.envelopeReceiver.receiver
|
|
|
|
// Load the document from the filestore
|
|
const documentResponse = this.documentBytes
|
|
|
|
if (documentResponse.fatal || documentResponse.error) {
|
|
return Swal.fire({
|
|
title: 'Fehler',
|
|
text: 'Dokument konnte nicht geladen werden!',
|
|
icon: 'error',
|
|
})
|
|
}
|
|
|
|
const arrayBuffer = this.documentBytes
|
|
|
|
// Load PSPDFKit
|
|
this.Instance = await UI.loadPSPDFKit(arrayBuffer, this.container, this.licenseKey, this.locale)
|
|
UI.addToolbarItems(this.Instance, this.handleClick.bind(this))
|
|
|
|
this.Instance.addEventListener(
|
|
'annotations.load',
|
|
this.handleAnnotationsLoad.bind(this)
|
|
)
|
|
this.Instance.addEventListener(
|
|
'annotations.change',
|
|
this.handleAnnotationsChange.bind(this)
|
|
)
|
|
this.Instance.addEventListener(
|
|
'annotations.create',
|
|
this.handleAnnotationsCreate.bind(this)
|
|
)
|
|
|
|
this.Instance.addEventListener("annotations.willChange", _ => {
|
|
Comp.ActPanel.Toggle();
|
|
});
|
|
|
|
// Load annotations into PSPDFKit
|
|
try {
|
|
this.signatureCount = this.currentDocument.elements.length
|
|
await Annotation.createAnnotations(this.currentDocument, this.Instance)
|
|
|
|
const openResponse = await this.Network.openDocument(this.envelopeKey)
|
|
|
|
if (openResponse.fatal || openResponse.error) {
|
|
return Swal.fire({
|
|
title: 'Fehler',
|
|
text: 'Umschlag konnte nicht geöffnet werden!',
|
|
icon: 'error',
|
|
})
|
|
}
|
|
} catch (e) {
|
|
}
|
|
|
|
//add click events of external buttons
|
|
[...document.getElementsByClassName('btn_refresh')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('RESET')));
|
|
[...document.getElementsByClassName('btn_complete')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('FINISH')));
|
|
[...document.getElementsByClassName('btn_reject')].forEach(btn => btn.addEventListener('click', _ => this.handleClick('REJECT')));
|
|
}
|
|
|
|
handleAnnotationsLoad(loadedAnnotations) {
|
|
loadedAnnotations.toJS()
|
|
}
|
|
|
|
handleAnnotationsChange() { }
|
|
|
|
async handleAnnotationsCreate(createdAnnotations) {
|
|
const annotation = createdAnnotations.toJS()[0]
|
|
const isFormField = !!annotation.formFieldName
|
|
const isSignature = !!annotation.isSignature
|
|
|
|
if (isFormField === false && isSignature === true) {
|
|
|
|
const left = annotation.boundingBox.left - 20
|
|
const top = annotation.boundingBox.top - 20
|
|
const width = 150
|
|
const height = 75
|
|
const timestamp = new Date()
|
|
|
|
const imageUrl = await Annotation.createAnnotationFrameBlob(
|
|
this.envelopeReceiver.name,
|
|
this.currentReceiver.signature,
|
|
timestamp,
|
|
width,
|
|
height
|
|
)
|
|
|
|
const request = await fetch(imageUrl)
|
|
const blob = await request.blob()
|
|
const imageAttachmentId = await this.Instance.createAttachment(blob)
|
|
|
|
const frameAnnotation = Annotation.createImageAnnotation(
|
|
new PSPDFKit.Geometry.Rect({
|
|
left: left,
|
|
top: top,
|
|
width: width,
|
|
height: height,
|
|
}),
|
|
annotation.pageIndex,
|
|
imageAttachmentId
|
|
)
|
|
|
|
this.Instance.create(frameAnnotation)
|
|
}
|
|
}
|
|
|
|
async handleClick(eventType) {
|
|
let result = false
|
|
|
|
switch (eventType) {
|
|
case 'RESET':
|
|
result = await this.handleReset(null)
|
|
Comp.SignatureProgress.SignedCount = 0;
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
title: 'Erfolg',
|
|
text: 'Dokument wurde zurückgesetzt',
|
|
icon: 'info',
|
|
})
|
|
}
|
|
break;
|
|
|
|
case 'FINISH':
|
|
result = await this.handleFinish(null)
|
|
|
|
if (result == true) {
|
|
// Redirect to success page after saving to database
|
|
window.location.href = `/EnvelopeKey/${this.envelopeKey}/Success`
|
|
}
|
|
break;
|
|
|
|
case 'REJECT':
|
|
Swal.fire({
|
|
title: localized.rejection,
|
|
html: `<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,
|
|
icon: "question",
|
|
input: "text",
|
|
inputAttributes: {
|
|
autocapitalize: "off"
|
|
},
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#3085d6",
|
|
cancelButtonColor: "#d33",
|
|
confirmButtonText: localized.complete,
|
|
cancelButtonText: localized.back,
|
|
showLoaderOnConfirm: true,
|
|
preConfirm: async (reason) => {
|
|
try {
|
|
var res = await rejectEnvelope(reason);
|
|
return res;
|
|
} catch (error) {
|
|
Swal.showValidationMessage(`
|
|
Request failed: ${error}
|
|
`);
|
|
}
|
|
},
|
|
allowOutsideClick: () => !Swal.isLoading()
|
|
}).then((result) => {
|
|
if (!result.isConfirmed)
|
|
return;
|
|
const res = result.value;
|
|
if (res.ok) {
|
|
redirRejected()
|
|
}
|
|
else
|
|
Swal.showValidationMessage(`Request failed: ${res.message}`);
|
|
});
|
|
break;
|
|
case 'COPY_URL':
|
|
const url = window.location.href.replace(/\/readonly/gi, '');
|
|
navigator.clipboard.writeText(url).then(function () {
|
|
bsNotify('Kopiert', { alert_type: 'success', delay: 4, icon_name: 'check_circle' });
|
|
}).catch(function (err) {
|
|
bsNotify('Unerwarteter Fehler', { alert_type: 'danger', delay: 4, icon_name: 'error' });
|
|
});
|
|
break;
|
|
|
|
case 'SHARE':
|
|
// Show the modal
|
|
Comp.ShareBackdrop.show();
|
|
break;
|
|
case 'LOCATION':
|
|
// Show the modal
|
|
Comp.LocationBackdrop.show();
|
|
break;
|
|
}
|
|
}
|
|
|
|
async handleFinish(event) {
|
|
const iJSON = await this.Instance.exportInstantJSON()
|
|
const iFormFieldValues = await iJSON.formFieldValues;
|
|
|
|
//check required
|
|
const iReqFields = iFormFieldValues.filter(f => Annotation.isFieldRequired(f))
|
|
const hasEmptyReq = iReqFields.some(f => (f.value === undefined || f.value === null || f.value === ""))
|
|
|
|
if (hasEmptyReq) {
|
|
Swal.fire({
|
|
title: 'Warnung',
|
|
text: 'Bitte füllen Sie alle Standortinformationen vollständig aus!',
|
|
icon: 'warning',
|
|
})
|
|
return false;
|
|
}
|
|
|
|
//check city
|
|
const city_regex = new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$")
|
|
const iCityFields = iFormFieldValues.filter(f => Annotation.isCityField(f))
|
|
for (var f of iCityFields)
|
|
if (!IS_MOBILE_DEVICE && !city_regex.test(f.value)) {
|
|
Swal.fire({
|
|
title: 'Warnung',
|
|
text: `Bitte überprüfen Sie die eingegebene Ortsangabe "${f.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,
|
|
icon: 'warning',
|
|
})
|
|
return false;
|
|
}
|
|
|
|
//check # of signature
|
|
const validationResult = await this.validateAnnotations(this.signatureCount)
|
|
if (validationResult === false) {
|
|
Swal.fire({
|
|
title: 'Warnung',
|
|
text: 'Es wurden nicht alle Signaturfelder ausgefüllt!',
|
|
icon: 'warning',
|
|
})
|
|
return false
|
|
}
|
|
|
|
return Swal.fire({
|
|
title: localized.confirmation,
|
|
html: `<div class="text-start fs-6 p-0 m-0">${localized.sigAgree}</div>`,
|
|
icon: "question",
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#3085d6",
|
|
cancelButtonColor: "#d33",
|
|
confirmButtonText: localized.finalize,
|
|
cancelButtonText: localized.back
|
|
}).then(async (result) => {
|
|
if (result.isConfirmed) {
|
|
//---
|
|
// Save changes before doing anything
|
|
try {
|
|
await this.Instance.save()
|
|
} catch (e) {
|
|
Swal.fire({
|
|
title: 'Fehler',
|
|
text: 'Umschlag konnte nicht signiert werden!',
|
|
icon: 'error',
|
|
})
|
|
return false
|
|
}
|
|
|
|
// Export annotation data and save to database
|
|
try {
|
|
const json = await iJSON
|
|
const postEnvelopeResult = await this.Network.postEnvelope(
|
|
this.envelopeKey,
|
|
this.currentDocument.id,
|
|
json
|
|
)
|
|
|
|
if (postEnvelopeResult.fatal) {
|
|
Swal.fire({
|
|
title: 'Fehler',
|
|
text: 'Umschlag konnte nicht signiert werden!',
|
|
icon: 'error',
|
|
})
|
|
return false
|
|
}
|
|
|
|
if (postEnvelopeResult.error) {
|
|
Swal.fire({
|
|
title: 'Warnung',
|
|
text: 'Umschlag ist nicht mehr verfügbar.',
|
|
icon: 'warning',
|
|
})
|
|
return false
|
|
}
|
|
|
|
return true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
//---
|
|
}
|
|
else
|
|
return false;
|
|
});
|
|
}
|
|
|
|
|
|
async validateAnnotations(totalSignatures) {
|
|
const annotations = await Annotation.getAnnotations(this.Instance)
|
|
const filtered = annotations
|
|
.map(a => a.toJS())
|
|
.filter(a => a.isSignature)
|
|
|
|
if (totalSignatures > filtered.length) {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
async handleReset(event) {
|
|
const result = await Swal.fire({
|
|
title: 'Sind sie sicher?',
|
|
text: 'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?',
|
|
icon: 'question',
|
|
showCancelButton: true
|
|
})
|
|
|
|
if (result.isConfirmed) {
|
|
const result = await Annotation.deleteAnnotations(this.Instance)
|
|
}
|
|
|
|
return result
|
|
}
|
|
}
|