diff --git a/EnvelopeGenerator.Web/WebKey.cs b/EnvelopeGenerator.Web/WebKey.cs index f65aeeee..ed4042a5 100644 --- a/EnvelopeGenerator.Web/WebKey.cs +++ b/EnvelopeGenerator.Web/WebKey.cs @@ -1,4 +1,6 @@ -namespace EnvelopeGenerator.Web +using System.Numerics; + +namespace EnvelopeGenerator.Web { public static class WebKey { @@ -16,5 +18,11 @@ public static readonly string LockedAccessCode = nameof(LockedAccessCode); public static readonly string LockedFooterTitle = nameof(LockedFooterTitle); public static readonly string LockedFooterBody = nameof(LockedFooterBody); + public static readonly string WrongAccessCode = nameof(WrongAccessCode); + public static readonly string SignDoc = nameof(SignDoc); + public static readonly string DocProtected = nameof(DocProtected); + public static readonly string Complete = nameof(Complete); + public static readonly string EnvelopeInfo1 = nameof(EnvelopeInfo1); + public static readonly string EnvelopeInfo2 = nameof(EnvelopeInfo2); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index d8779239..eecaa27b 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -1,4 +1,5 @@ { + "DiPMode": false, //Please be careful when enabling Development in Production (DiP) mode. It allows Swagger and test controllers to be enabled in a production environment. "EnableSwagger": true, "EnableTestControllers": true, "DetailedErrors": true, @@ -10,17 +11,13 @@ "Microsoft.AspNetCore.Hosting.Diagnostics": "Warning" } }, - "Config": { - "ConnectionString": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;", - "LogPath": "E:\\EnvelopeGenerator\\Logs", - "LogDebug": true, - "LogJson": true, - "AdminPassword": "dd" + "ConnectionStrings": { + "Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;" }, - "PSPDFKitLicenseKey": null, - /* The first format parameter {0} will be replaced by the nonce value. */ - "TestCSP": true, - "Content-Security-Policy": [ + "AdminPassword": "dd", + "PSPDFKitLicenseKey_SignFlow": "y8VgCpBgUfNlpKJZC-GwjpPs-S-KFBHv4RywfHbqpBAbO0XxRuWDaMGZtIaMrXBDlndlJLk---Ve93xjI_ZR4sbFymf4Ot97yTYUMeDdL2LYXhspkEnSAtkXf9zqepNL1v_0DMibjpqXFQMkVB1y3D5xdnOg-iJuCCZEMhZ780qg04_H_nmx63uSgxjN0GJxC8YvsbnRcUZ2l_idImMvWL0HMqB5B7oEpNenasA0RK0uapFRTa7NIQok0phpTHZYKB4qvj7od2yxlytGB7qBl4-lwT70DSQ9mrLkCWbuzZ9cV9D8fDzdFXr6WoZdOYpkrUadRbsy2bhPq_ukxszDWN4JGhebo0XKUK_YfgvSlS7lFOxHNblHeC9B7gZ8T-VuQ_z1QA2JYRf1dmhSuclnW00diShIg-N0I79PWGsQE4j40XtVpyWcN9uT9hMuiRpL0LzHV4YgsgBrgKgs_moqL7f0L4-MwaS25Dx4Wcz4ttKaerLavwMM4CJHI3DNqTC5UUEG6EViFxBQtrmuAS7kiw2nWjvXO7kUA24NARtsRCphjWE4l6wSMdh7kpqhfbV7_hdb5xXYGALNPkv8En6zPpFIew8DDcOH9dgxfKMI34LLhkEWqovZW_7fXNJTEIHVpR0DSPbZrmyEwkECnbDcNzjyFk2M1fzstJj_dSotyZvS57XJK2DgojbRgXL9pncs", + "UseCSPInDev": false, + "Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value. "default-src 'self'", "script-src 'self' 'nonce-{0}' 'unsafe-inline' 'unsafe-eval' blob: data:", "style-src 'self' 'unsafe-inline'", @@ -32,24 +29,27 @@ "object-src 'self'", "worker-src 'self' blob: data:" ], - "AdminPassword": "dd", "AllowedOrigins": [ "https://localhost:7202", "https://digitale.unterschrift.wisag.de/" ], "NLog": { "throwConfigExceptions": true, + "variables": { + "logDirectory": "E:\\EnvelopeGenerator\\Logs", + "logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web" + }, "targets": { "infoLogs": { "type": "File", - "fileName": "E:\\EnvelopeGenerator\\Logs\\${shortdate}-ECM.EnvelopeGenerator.Web-Info.log", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log", "maxArchiveDays": 30 }, "errorLogs": { "type": "File", - "fileName": "E:\\EnvelopeGenerator\\Logs\\${shortdate}-ECM.EnvelopeGenerator.Web-Error.log", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log", "maxArchiveDays": 30 }, "criticalLogs": { "type": "File", - "fileName": "E:\\EnvelopeGenerator\\Logs\\${shortdate}-ECM.EnvelopeGenerator.Web-Critical.log", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log", "maxArchiveDays": 30 } }, diff --git a/EnvelopeGenerator.Web/wwwroot/css/site.css b/EnvelopeGenerator.Web/wwwroot/css/site.css index 1fb08ec5..e320c9c2 100644 --- a/EnvelopeGenerator.Web/wwwroot/css/site.css +++ b/EnvelopeGenerator.Web/wwwroot/css/site.css @@ -4,14 +4,28 @@ */ /* Toolbar Buttons */ - #app { background: gray; width: 100vw; - height: 100vh; - margin: 0 auto; + height: 80vh; } +.btn-group { + margin-right: 10vw; + margin-bottom: 10vh; +} + +.btn_refresh, .btn_complete { +} + + .btn_complete .icon { + width: 1.1rem; + } + + .btn_complete span { + vertical-align: middle; + } + .button-finish { transition: background-color linear 300ms; background-color: #059669; /* emerald-600 */ @@ -60,46 +74,45 @@ body { max-width: 40rem; } -.page section { - max-width: 30rem; - margin: 0 auto; + .page section { + max-width: 30rem; + margin: 0 auto; + } + + .page header .icon { + display: inline-block; + border-radius: 100px; + padding: 15px; + margin-bottom: 2rem; + } + + .page header .icon.admin { + background-color: #331904; + color: #fecba1; + } + + .page header .icon.locked { + background-color: #ffc107; + color: #000; + } + + .page header .icon.signed { + background-color: #146c43; + color: #fff; + } + + .page .form { + max-width: 30rem; + margin: 2rem auto; + display: flex; + gap: 1rem; + } + +#form-access-code > .input, +#form-admin-password > .input { + flex-grow: 1; } -.page header .icon { - display: inline-block; - border-radius: 100px; - padding: 15px; - margin-bottom: 2rem; -} - - .page header .icon.admin { - background-color: #331904; - color: #fecba1; - } - - .page header .icon.locked { - background-color: #ffc107; - color: #000; - } - - .page header .icon.signed { - background-color: #146c43; - color: #fff; - } - -.page .form { - max-width: 30rem; - margin: 2rem auto; - - display: flex; - gap: 1rem; -} - - #form-access-code > .input, - #form-admin-password > .input { - flex-grow: 1; - } - #page-admin header .icon { background-color: #331904; color: #fecba1; @@ -120,26 +133,29 @@ footer#page-footer { font-size: 0.85rem; } -footer#page-footer a, -footer#page-footer a:link, -footer#page-footer a:hover, -footer#page-footer a:visited, -footer#page-footer a:focus { - color: #444; -} + footer#page-footer a, + footer#page-footer a:link, + footer#page-footer a:hover, + footer#page-footer a:visited, + footer#page-footer a:focus { + color: #444; + } .sender-card { background-color: transparent; border: none; } -.sender-card .row { - height: 7vh; -} -.sender-card img{ - height: 7vh; - background-color: rgb(209, 207, 207); - border-radius: 50px; -} + + .sender-card .row { + height: 7vh; + } + + .sender-card img { + height: 7vh; + background-color: rgb(209, 207, 207); + border-radius: 50px; + } + .envelope-message { font-family: 'Roboto', sans-serif; } @@ -152,32 +168,12 @@ footer#page-footer a:focus { width: 30%; height: 70%; } + .dropdown-flag { height: 75%; width: 75%; } -/* --- */ -/* Adjusting the height of the select2 container */ -.dropdown-flag .select2-container--default .select2-selection--single { - height: 40px; /* Desired height */ -} - -/* Adjusting the height and vertical alignment of the selected item */ -.dropdown-flag .select2-container--default .select2-selection--single .select2-selection__rendered { - line-height: 38px; /* Should be 2px less than the height for internal padding */ -} - -/* Adjusting the height of the dropdown arrow */ -.dropdown-flag .select2-container--default .select2-selection--single .select2-selection__arrow { - height: 38px; /* Again, 2px less than the height */ -} - -/* Adjusting the height of dropdown list items */ -.dropdown-flag .select2-container--default .select2-dropdown .select2-results>.select2-results__options { - max-height: 200px; /* Optional, adjust for larger dropdown height */ -} - /* CSS for custom class to increase dropdown height */ .increase-dropdown-height { min-height: 400px; /* Optional, larger value for increased height */ @@ -190,27 +186,74 @@ footer#page-footer a:focus { max-width: 180px; /* Suitable maximum width for the form */ } - -.select2-container--default .select2-search--dropdown .select2-search__field { - border-color: #86b7fe; - outline: 0; - mask-border-width: 0 -} - -.select2-container--default .select2-search--dropdown .select2-search__field:hover { - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 .25rem rgba(13, 110, 253, .25); -} - -.select2-search__field { - display:none -} - .lang-item { font-size: 0.85rem; } -#langDropdownMenuButton{ +#langDropdownMenuButton { min-width: 4vw; -} \ No newline at end of file +} + +/* Additional styles for better mobile responsiveness */ +@media (max-width: 767px) { + .navbar { + flex-direction: column; + align-items: flex-start; + } + + .navbar-toggler { + } + + .navbar-brand { + font-size: 0.5rem; + text-align: center; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } + + .collapse .card-text, .collapsing .card-text { + font-size: 0.6rem; /* Font size reduced */ + margin: 0rem; + padding: 0rem; + } + + .sender-card .card-body { + padding: 0.5rem; + } + + .btn_group { + position: fixed; + flex-direction: row; + bottom: 0.5rem; + right: 0.5rem; + } + + .btn_complete, .btn_refresh { + padding: 0.5rem; + font-size: 0.9rem; + height: 100%; /* Adjust height for better fit */ + width: 10rem; /* Full width for better touch area */ + margin-bottom: 0.5rem; /* Space between buttons */ + } + + .img-fluid { + width: 1.2rem; + height: 100%; + display: none; + } + + img { + max-width: 4rem; + } + + .page { + margin-top: 1rem; + max-width: 90%; + padding: 0.5rem; + } + + .page section { + max-width: 90%; + } +} diff --git a/EnvelopeGenerator.Web/wwwroot/js/annotation.js b/EnvelopeGenerator.Web/wwwroot/js/annotation.js index 9cd2f0d5..cde6cacf 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/annotation.js +++ b/EnvelopeGenerator.Web/wwwroot/js/annotation.js @@ -3,8 +3,6 @@ const annotations = [] document.elements.forEach((element) => { - console.debug('Creating annotation for element', element.id) - const [annotation, formField] = this.createAnnotationFromElement(element) annotations.push(annotation) annotations.push(formField) diff --git a/EnvelopeGenerator.Web/wwwroot/js/api.js b/EnvelopeGenerator.Web/wwwroot/js/api.js new file mode 100644 index 00000000..6a47cd3c --- /dev/null +++ b/EnvelopeGenerator.Web/wwwroot/js/api.js @@ -0,0 +1,7 @@ +const submitForm = async form => await fetch(form.action, { + method: form.method, + body: new FormData(form), + headers: { + "X-Requested-With": "XMLHttpRequest" + } +}) \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/js/app.js b/EnvelopeGenerator.Web/wwwroot/js/app.js index 37a71451..22c3a7f9 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/app.js +++ b/EnvelopeGenerator.Web/wwwroot/js/app.js @@ -10,8 +10,8 @@ const ActionType = { } class App { - constructor(container, envelopeKey, envelopeReceiver, documentBytes, licenseKey) { - this.container = container + constructor(envelopeKey, envelopeReceiver, documentBytes, licenseKey, locale, container) { + this.container = container ?? `#${this.constructor.name.toLowerCase()}`; this.envelopeKey = envelopeKey this.UI = new UI() @@ -25,6 +25,7 @@ class App { this.envelopeReceiver = envelopeReceiver; this.documentBytes = documentBytes; this.licenseKey = licenseKey; + this.locale = locale; } // This function will be called from the ShowEnvelope.razor page @@ -38,7 +39,6 @@ class App { const documentResponse = this.documentBytes if (documentResponse.fatal || documentResponse.error) { - console.error(documentResponse.error) return Swal.fire({ title: 'Fehler', text: 'Dokument konnte nicht geladen werden!', @@ -49,7 +49,7 @@ class App { const arrayBuffer = this.documentBytes // Load PSPDFKit - this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container, this.licenseKey) + this.Instance = await this.UI.loadPSPDFKit(arrayBuffer, this.container, this.licenseKey, this.locale) this.UI.configurePSPDFKit(this.Instance, this.handleClick.bind(this)) this.Instance.addEventListener( @@ -66,8 +66,6 @@ class App { ) // Load annotations into PSPDFKit - console.debug('Loading annotations..') - try { this.signatureCount = this.currentDocument.elements.length const annotations = this.Annotation.createAnnotations( @@ -85,12 +83,15 @@ class App { }) } } catch (e) { - console.error(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'))); } handleAnnotationsLoad(loadedAnnotations) { - console.debug('annotations loaded', loadedAnnotations.toJS()) + loadedAnnotations.toJS() } handleAnnotationsChange() { } @@ -168,7 +169,6 @@ class App { } async handleFinish(event) { - const validationResult = await this.validateAnnotations(this.signatureCount) if (validationResult === false) { Swal.fire({ @@ -184,7 +184,6 @@ class App { try { await this.Instance.save() } catch (e) { - console.error(e) Swal.fire({ title: 'Fehler', text: 'Umschlag konnte nicht signiert werden!', @@ -196,7 +195,6 @@ class App { // Export annotation data and save to database try { const json = await this.Instance.exportInstantJSON() - console.log(json) const postEnvelopeResult = await this.Network.postEnvelope( this.envelopeKey, this.currentDocument.id, @@ -223,7 +221,6 @@ class App { return true } catch (e) { - console.error(e) return false } } diff --git a/EnvelopeGenerator.Web/wwwroot/js/ui.js b/EnvelopeGenerator.Web/wwwroot/js/ui.js index d284fe2c..bf5ce19e 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/ui.js +++ b/EnvelopeGenerator.Web/wwwroot/js/ui.js @@ -14,8 +14,9 @@ // Load the PSPDFKit UI by setting a target element as the container to render in // and a arraybuffer which represents the document that should be displayed. - loadPSPDFKit(arrayBuffer, container, licenseKey) { + loadPSPDFKit(arrayBuffer, container, licenseKey, locale) { return PSPDFKit.load({ + locale: locale, licenseKey: licenseKey, styleSheets: ['/css/site.css'], container: container, @@ -47,8 +48,6 @@ configurePSPDFKit(instance, handler) { const toolbarItems = this.getToolbarItems(instance, handler) instance.setToolbarItems(toolbarItems) - - console.debug('PSPDFKit configured!') } annotationRenderer(data) { @@ -70,6 +69,7 @@ } getCustomItems = function (callback) { + return [] return [ { type: 'custom', @@ -124,4 +124,4 @@ return annotationPresets } -} +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/js/util.js b/EnvelopeGenerator.Web/wwwroot/js/util.js new file mode 100644 index 00000000..bba9fce3 --- /dev/null +++ b/EnvelopeGenerator.Web/wwwroot/js/util.js @@ -0,0 +1 @@ +const B64ToBuff = (base64String) => new Uint8Array(Array.from(atob(base64String), char => char.charCodeAt(0))).buffer; \ No newline at end of file