From 387bd060e1499ba7a8fe027c3cbab4fea64ebe9a Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Thu, 30 Jul 2026 12:28:08 +0530 Subject: [PATCH] feat(clients): draw-signature option for Specimen Signature, drop its webcam capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DrawSignatureDialogComponent (a mouse/touchpad signature pad) already existed and was used from the client profile view, but wasn't wired into the KYC step used during client creation/edit — a stale comment there even referred to a "draw-pad" option as if it were already present. Wired it in as a third capture method alongside Choose & Crop, then removed Use Webcam from the signature box specifically (kept for Client Photograph) per follow-up feedback — a signature is drawn or scanned, not photographed. Refactored the file-picker/webcam/draw-pad paths' duplicated stage+upload logic into shared applySignatureFile()/uploadSignatureFile() helpers. --- .../client-kyc-step.component.html | 4 +- .../client-kyc-step.component.ts | 88 +++++++++---------- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html index 11b4e42c7..81232f0e4 100644 --- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html +++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html @@ -1481,8 +1481,8 @@ Choose & Crop - @if (photoSigTouched) { diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts index 13bfad537..3ebaea616 100644 --- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts +++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts @@ -31,6 +31,7 @@ import { Dates } from 'app/core/utils/dates'; import { ProofType, normalizeProofType, groupProofsByCategory } from '../../shared/kyc-proof-types'; import { KycUploadHelperService } from '../../shared/kyc-upload-helper.service'; import { CaptureImageDialogComponent } from '../../clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component'; +import { DrawSignatureDialogComponent } from '../../clients-view/custom-dialogs/draw-signature-dialog/draw-signature-dialog.component'; import { dataUrlToFile } from 'app/shared/utils/data-url.util'; import { DocumentPreviewDialogComponent } from '../../shared/document-preview-dialog/document-preview-dialog.component'; @@ -1657,20 +1658,6 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { await this.openSignatureCropDialog(file); } - /** Webcam capture entry point for the specimen signature — photographs a paper - * signature, then reuses the same crop dialog + staging/upload path as the file-picker - * flow above (additive to the existing draw-pad / upload options, not a replacement). */ - async captureWebcamSignature(): Promise { - const dialogRef = this.dialog.open(CaptureImageDialogComponent, { - data: { captureTarget: 'signature' } - }); - dialogRef.afterClosed().subscribe(async (imageDataUrl: string) => { - if (!imageDataUrl) return; - const file = dataUrlToFile(imageDataUrl, 'webcam-signature.jpg'); - await this.openSignatureCropDialog(file); - }); - } - private async openSignatureCropDialog(file: File): Promise { const { ImageCropDialogComponent } = await import('./image-crop-dialog/image-crop-dialog.component'); const dialogRef = this.dialog.open(ImageCropDialogComponent, { @@ -1680,42 +1667,51 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { }); dialogRef.afterClosed().subscribe((result: any) => { if (result) { - this.signatureFile = result.croppedFile; - this.signaturePreview = result.previewUrl; - // In edit mode, delete old signature doc then upload new one - if (this.clientId) { - if (this.existingSignatureDocId) { - this.clientsService.deleteClientDocument(this.clientId, this.existingSignatureDocId).subscribe({ - next: () => { - this.clientsService.uploadClientSignatureImage(this.clientId, result.croppedFile).subscribe({ - next: (res: any) => { - if (res?.resourceId) this.existingSignatureDocId = res.resourceId.toString(); - }, - error: () => {} - }); - }, - error: () => { - this.clientsService.uploadClientSignatureImage(this.clientId, result.croppedFile).subscribe({ - next: (res: any) => { - if (res?.resourceId) this.existingSignatureDocId = res.resourceId.toString(); - }, - error: () => {} - }); - } - }); - } else { - this.clientsService.uploadClientSignatureImage(this.clientId, result.croppedFile).subscribe({ - next: (res: any) => { - if (res?.resourceId) this.existingSignatureDocId = res.resourceId.toString(); - }, - error: () => {} - }); - } - } + this.applySignatureFile(result.croppedFile, result.previewUrl); } }); } + /** Draw-with-mouse/touchpad entry point for the specimen signature — same staging/upload + * path as the file-picker and webcam flows, minus the crop step (the pad's canvas is + * already the right shape/size). */ + drawSignature(): void { + const dialogRef = this.dialog.open(DrawSignatureDialogComponent); + dialogRef.afterClosed().subscribe((file: File) => { + if (file) { + this.applySignatureFile(file, URL.createObjectURL(file)); + } + }); + } + + /** Stages the signature locally and, in edit mode, persists it — replacing any existing + * signature doc first so a client never ends up with two. Shared by the file-picker, webcam + * and draw-pad entry points. */ + private applySignatureFile(file: File, previewUrl: string): void { + this.signatureFile = file; + this.signaturePreview = previewUrl; + if (!this.clientId) { + return; + } + if (this.existingSignatureDocId) { + this.clientsService.deleteClientDocument(this.clientId, this.existingSignatureDocId).subscribe({ + next: () => this.uploadSignatureFile(file), + error: () => this.uploadSignatureFile(file) + }); + } else { + this.uploadSignatureFile(file); + } + } + + private uploadSignatureFile(file: File): void { + this.clientsService.uploadClientSignatureImage(this.clientId, file).subscribe({ + next: (res: any) => { + if (res?.resourceId) this.existingSignatureDocId = res.resourceId.toString(); + }, + error: () => {} + }); + } + removePhoto() { this.photographFile = null; this.photographPreview = null;