{#
First-run signature/hash onboarding modal (TODO-A, see APPROVAL_SYSTEM_AUDIT.md §2).
Included once from the global ERP footer. On first page load per session it asks
/signature_status; if the user has no signature yet it pops this modal so they set
their signature + approval hash before hitting an approval action and getting stuck.
Posts to the same update_signature endpoint (with redirectTo so they land back here).
Purely additive — fails safe if the status call errors.
#}
<div class="modal fade" id="hbSignatureSetupModal" tabindex="-1" role="dialog" aria-labelledby="hbSigSetupTitle">
<div class="modal-dialog" role="document" style="max-width:520px;">
<div class="modal-content">
<form role="form" method="post" action="{{ path('update_signature') }}"
enctype="multipart/form-data" id="hbSigSetupForm">
<input type="hidden" name="redirectTo" id="hbSigRedirectTo" value="">
<div class="modal-header" style="background:#5e35b1;color:#fff;border-radius:6px 6px 0 0;">
<h4 class="modal-title" id="hbSigSetupTitle" style="color:#fff;">
<i class="fa fa-pencil-square-o"></i> Set or update your approval signature
</h4>
</div>
<div class="modal-body">
<p style="color:#555;margin-bottom:18px;">
Before you can approve documents you need a one-time digital signature and a
secret <b>approval hash</b>. Your signature image is encrypted with this hash —
<b>memorise it and never share it</b>.
</p>
<div id="hbSigSetupError" class="text-danger" style="margin-bottom:12px;"></div>
<div class="form-group">
<label for="hbSigHash">Digital Encryption Hash</label>
<input type="password" required class="form-control" id="hbSigHash"
autocomplete="off" name="approvalHash" placeholder="Enter a secret code">
</div>
<div class="form-group">
<label for="hbSigHashAgain">Re-enter Encryption Hash</label>
<input type="password" required class="form-control" id="hbSigHashAgain"
autocomplete="off" name="approvalHashAgain" placeholder="Retype the code">
</div>
<div class="form-group">
<label for="hbSigFile" style="display:block;">Upload Signature <small style="color:#888;">(image, width > height, < 200 kB)</small></label>
<input type="file" name="myfile" accept="image/*" id="hbSigFile"
style="display:block;width:100%;padding:8px;border:1px solid #ced4da;border-radius:6px;background:#fff;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Later</button>
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> Save signature</button>
</div>
</form>
</div>
</div>
</div>
<script>
(function () {
if (typeof jQuery === 'undefined') return;
var $ = jQuery;
// Show/hide that works WITH or WITHOUT Bootstrap's modal plugin. The cp-shell
// module pages (Approval & Access etc.) load jQuery but not the codecovers
// Bootstrap JS bundle, so $.fn.modal is undefined there — fall back to a
// manual overlay (uses Bootstrap CSS if present, plus inline styles to be safe).
function hbShowSig() {
var $m = $('#hbSignatureSetupModal');
if ($.fn && typeof $.fn.modal === 'function') { $m.modal({ backdrop: 'static', keyboard: true }); return; }
if (!$('#hbSigBackdrop').length) {
$('<div id="hbSigBackdrop"></div>').css({
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
background: 'rgba(0,0,0,.5)', zIndex: 1040
}).appendTo('body');
}
$m.addClass('in').css({ display: 'block', position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1050, overflow: 'auto' });
$('body').addClass('modal-open');
}
function hbHideSig() {
var $m = $('#hbSignatureSetupModal');
if ($.fn && typeof $.fn.modal === 'function') { $m.modal('hide'); }
else { $m.removeClass('in').css('display', 'none'); }
$('#hbSigBackdrop').remove();
$('body').removeClass('modal-open');
}
// Manual close (Bootstrap's data-dismiss won't fire without the plugin).
$(document).on('click', '#hbSignatureSetupModal [data-dismiss="modal"], #hbSigBackdrop', function () { hbHideSig(); });
// Client-side validation mirroring the dedicated page.
$('#hbSigSetupForm').on('submit', function (event) {
event.preventDefault();
$('#hbSigSetupError').text('');
var hash = $('#hbSigHash').val(), hashAgain = $('#hbSigHashAgain').val();
if (!hash) { $('#hbSigSetupError').text('Please enter a hash.'); return; }
if (hash !== hashAgain) { $('#hbSigSetupError').text('Hashes did not match.'); return; }
var files = $('#hbSigFile')[0].files;
if (!files.length) { $('#hbSigSetupError').text('Please select a signature image.'); return; }
var file = files[0];
if (((file.size / 1024) / 1024) > 0.2) { $('#hbSigSetupError').text('File must be under 200 kB.'); return; }
var img = new Image();
img.onload = function () {
if (img.width <= img.height) { $('#hbSigSetupError').text('Width should be greater than height.'); }
else { $('#hbSigSetupForm')[0].submit(); }
};
img.onerror = function () { $('#hbSigSetupError').text('Invalid image file.'); };
var reader = new FileReader();
reader.onload = function (e) { img.src = e.target.result; };
reader.readAsDataURL(file);
});
// Manual trigger: any element with [data-open-signature-modal] opens the modal
// (to set OR update the signature/hash on demand, not just first-run).
$(document).on('click', '[data-open-signature-modal]', function (e) {
e.preventDefault();
$('#hbSigRedirectTo').val(window.location.href);
hbShowSig();
});
// First-run check — once per session, skip the signature page itself.
$(function () {
try {
if (sessionStorage.getItem('hb_sig_checked') === '1') return;
if (window.location.pathname.indexOf('update_signature') !== -1) return;
$.getJSON('{{ path('signature_status') }}')
.done(function (res) {
sessionStorage.setItem('hb_sig_checked', '1');
if (res && res.hasSignature === false) {
$('#hbSigRedirectTo').val(window.location.href);
hbShowSig();
}
})
.fail(function () { /* fail safe: never block the page */ });
} catch (e) { /* no-op */ }
});
}());
</script>