fix(cropper): render reason checkboxes, gate the Others field, add cropped preview

Three defects from the Carbon rewrite, found while fixing the review panel:

- reason checkboxes were invisible: Carbon's .cds--checkbox input is
  visually clipped and needs the .cds--form-item/.cds--checkbox-wrapper +
  .cds--checkbox-label markup to draw the box
- 'Others (please specify)' now appears only when 'Others not specified.'
  is ticked, and clears when unticked (hidden text must not be submitted);
  the textarea gets Carbon's __wrapper so it has a proper height
- new 'Cropped preview' between the original and cleaned previews, so the
  operator sees what will be uploaded; refreshed on pointer release over the
  pad and after load/clean
- REGRESSION: Vue.use(Croppa) had been dropped — the crop canvas never
  rendered, and saving only worked because the cleaned image bypassed
  generateDataUrl. Restored.
- REGRESSION: the OTP field bound v-model="otpSave" while the data property
  is "otp", so Vue's render threw ReferenceError and froze the panel as
  soon as accept was clicked (the OTP could never be entered). Bound to otp;
  the croppa instance is now reached via $refs.croppa (the v-model object is
  not always the live instance)
- after a successful save the review panel stayed open: reset() recreated an
  empty message object, which the template treats as truthy. message is null
  when idle now.

45 tests pass; save round-trip and reject/Others flow verified in the browser.
This commit is contained in:
2026-10-01 04:26:26 +01:00
parent 2e4a51d018
commit cb5b151988
2 changed files with 65 additions and 15 deletions
+20 -9
View File
@@ -81,11 +81,17 @@
<div class="editor-grid mt-2">
<div class="editor-media">
<label class="cds--label">Original — crop it</label>
<div class="croppa-pad mt-1">
<croppa v-model="myCroppa" :show-remove-button="false" :width="250" :height="267.44" :quality="2">
<div class="croppa-pad mt-1" v-on:mouseup="refreshCropPreview" v-on:touchend="refreshCropPreview">
<croppa ref="croppa" v-model="myCroppa" :show-remove-button="false" :width="250" :height="267.44" :quality="2">
<img slot="initial" crossOrigin="anonymous" :src="message.message.file_url" style="width: 300px">
</croppa>
</div>
<template v-if="cropPreview">
<label class="cds--label mt-2" style="display:block;">Cropped preview</label>
<div class="croppa-pad mt-1">
<img :src="cropPreview" alt="cropped preview">
</div>
</template>
<template v-if="cleanedImage">
<label class="cds--label mt-2" style="display:block;">Cleaned preview</label>
<div class="croppa-pad mt-1">
@@ -122,7 +128,7 @@
<div class="row-gap" style="align-items:flex-end;">
<div style="min-width:150px; max-width:200px;">
<label class="cds--label" for="otpSave">OTP</label>
<input id="otpSave" class="cds--text-input" v-model.trim="otpSave" autocomplete="one-time-code">
<input id="otpSave" class="cds--text-input" v-model.trim="otp" autocomplete="one-time-code">
</div>
<cds-button v-bind:disabled="!canSave" v-on:click="saveChanges">
<span v-if="!busy.save">Crop and upload picture</span>
@@ -134,15 +140,20 @@
<div v-if="reviewed && !accepted" class="mt-2">
<label class="cds--label">Please state why this picture is not good enough.</label>
<div class="review-options mt-1">
<div v-for="item in reviewOptions" v-bind:key="item.index" style="display:flex; align-items:center; gap:0.5rem; margin-bottom:0.35rem;">
<input type="checkbox" class="cds--checkbox" v-bind:id="'reason-' + item.index" v-bind:value="item.name" v-model="selectedReviewOptions">
<label v-bind:for="'reason-' + item.index" style="margin:0;" v-on:click.prevent>{{ item.name }}</label>
<div class="review-options mt-1" v-for="item in reviewOptions" v-bind:key="item.index">
<div class="cds--form-item cds--checkbox-wrapper">
<input class="cds--checkbox" type="checkbox" v-bind:id="'reason-' + item.index"
v-bind:value="item.name" v-model="selectedReviewOptions">
<label class="cds--checkbox-label" v-bind:for="'reason-' + item.index">
<span class="cds--checkbox-label-text">{{ item.name }}</span>
</label>
</div>
</div>
<div class="mt-1" style="max-width:480px;">
<div class="mt-1" style="max-width:480px;" v-if="otherOptionSelected">
<label class="cds--label" for="otherReviewOption">Others (please specify).</label>
<textarea id="otherReviewOption" rows="3" class="cds--text-area" v-model="otherReviewOption"></textarea>
<div class="cds--text-area__wrapper">
<textarea id="otherReviewOption" rows="3" class="cds--text-area" v-model="otherReviewOption"></textarea>
</div>
</div>
<div class="row-gap mt-2">
<cds-button kind="danger" v-bind:disabled="!canReview" v-on:click="reviewPicture()">