Files
cbsnexora/src/app/clients/clients-view/custom-dialogs/welcome-kit-dialog/welcome-kit-dialog.component.ts
T
Anshul Rathore a560cd6533
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s
feat: add Welcome Kit dispatch dialog and Share Holders view
2026-07-31 14:06:27 +05:30

348 lines
11 KiB
TypeScript

/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, OnInit, inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
MAT_DIALOG_DATA,
MatDialogRef,
MatDialogTitle,
MatDialogContent,
MatDialogActions,
MatDialogClose
} from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ClientsService } from '../../../clients.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { DatePipe } from '@angular/common';
import { jsPDF } from 'jspdf';
import autoTable from 'jspdf-autotable';
@Component({
selector: 'mifosx-welcome-kit-dialog',
templateUrl: './welcome-kit-dialog.component.html',
styleUrls: ['./welcome-kit-dialog.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatDialogTitle,
MatDialogContent,
MatDialogActions,
MatDialogClose,
MatProgressSpinnerModule
],
providers: [DatePipe]
})
export class WelcomeKitDialogComponent implements OnInit {
private dialogRef = inject(MatDialogRef<WelcomeKitDialogComponent>);
data: { clientId: string } = inject(MAT_DIALOG_DATA);
private clientsService = inject(ClientsService);
private formBuilder = inject(FormBuilder);
private snackBar = inject(MatSnackBar);
private datePipe = inject(DatePipe);
isLoading = true;
welcomeKitsData: any = null;
dispatchForm: FormGroup = this.formBuilder.group({
deliveryAddress: [
'',
Validators.required
],
deliveryMode: [
'POSTMAN',
Validators.required
],
deliveryModeOtherText: ['']
});
deliveryModes = [
'POSTMAN',
'COURIER',
'OTHER'
];
constructor() {
this.dispatchForm.get('deliveryMode')?.valueChanges.subscribe((mode) => {
const otherControl = this.dispatchForm.get('deliveryModeOtherText');
if (mode === 'OTHER') {
otherControl?.setValidators([Validators.required]);
} else {
otherControl?.clearValidators();
}
otherControl?.updateValueAndValidity();
});
}
ngOnInit(): void {
this.fetchData();
}
fetchData() {
this.isLoading = true;
this.clientsService.getWelcomeKits(this.data.clientId).subscribe({
next: (response: any) => {
this.welcomeKitsData = response;
if (this.welcomeKitsData && this.welcomeKitsData.canDispatchNew) {
this.fetchPrimaryAddress();
} else {
this.isLoading = false;
}
},
error: () => {
this.isLoading = false;
this.snackBar.open('Failed to load welcome kits data', 'Close', { duration: 3000 });
}
});
}
fetchPrimaryAddress() {
this.clientsService.getClientAddressData(this.data.clientId).subscribe({
next: (addresses: any) => {
this.isLoading = false;
if (addresses && addresses.length > 0) {
// Attempt to build a readable address from the first address returned
const primary = addresses[0];
const parts = [];
if (primary.addressLine1) parts.push(primary.addressLine1);
if (primary.addressLine2) parts.push(primary.addressLine2);
if (primary.city) parts.push(primary.city);
if (primary.stateName) parts.push(primary.stateName);
if (primary.postalCode) parts.push(primary.postalCode);
if (primary.countryName) parts.push(primary.countryName);
const clientNameStr = this.welcomeKitsData?.clientName ? this.welcomeKitsData.clientName + ' — ' : '';
this.dispatchForm.patchValue({
deliveryAddress: clientNameStr + parts.join(', ')
});
}
},
error: () => {
this.isLoading = false;
}
});
}
get activeDispatches() {
if (this.welcomeKitsData && this.welcomeKitsData.dispatches && this.welcomeKitsData.dispatches.length > 0) {
return this.welcomeKitsData.dispatches;
}
return [];
}
submit() {
if (this.dispatchForm.invalid) {
this.dispatchForm.markAllAsTouched();
return;
}
this.isLoading = true;
const formValue = this.dispatchForm.value;
const today = new Date();
const formattedDate = this.datePipe.transform(today, 'dd MMMM yyyy');
const payload = {
deliveryAddress: formValue.deliveryAddress,
deliveryMode: formValue.deliveryMode,
deliveryModeOtherText: formValue.deliveryMode === 'OTHER' ? formValue.deliveryModeOtherText : null,
dispatchedOnDate: formattedDate,
locale: 'en',
dateFormat: 'dd MMMM yyyy'
};
this.clientsService.dispatchWelcomeKit(this.data.clientId, payload).subscribe({
next: () => {
this.snackBar.open('Welcome Kit Dispatched successfully', 'Close', { duration: 3000 });
this.fetchData();
},
error: () => {
this.isLoading = false;
this.snackBar.open('Failed to dispatch welcome kit', 'Close', { duration: 3000 });
}
});
}
getDisplayAddress(dispatch: any): string {
const address = dispatch.deliveryAddress || '';
const name = this.welcomeKitsData?.clientName || '';
if (name && !address.startsWith(name)) {
return `${name} — ${address}`;
}
return address;
}
downloadPdf(dispatch: any) {
const doc = new jsPDF();
const clientName = this.welcomeKitsData?.clientName || dispatch.clientName || 'N/A';
const address = this.getDisplayAddress(dispatch);
const mode =
dispatch.deliveryMode === 'OTHER'
? `${dispatch.deliveryMode} (${dispatch.deliveryModeOtherText || ''})`
: dispatch.deliveryMode;
const trackingId = dispatch.trackingId || 'N/A';
const status = dispatch.status || 'N/A';
const dispatchedOn = this.datePipe.transform(dispatch.dispatchedOnDate, 'mediumDate') || 'N/A';
const primaryColor: [number, number, number] = [
31,
73,
125
]; // Navy Blue
const grayColor: [number, number, number] = [
100,
100,
100
];
const lightGray: [number, number, number] = [
230,
230,
230
];
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
// --- 1. Header Banner ---
doc.setFillColor(...primaryColor);
doc.rect(0, 0, pageWidth, 40, 'F');
// Decorative circles
doc.setFillColor(255, 255, 255);
doc.circle(pageWidth - 20, 15, 3, 'F');
doc.circle(pageWidth - 30, 25, 2, 'F');
doc.circle(pageWidth - 10, 30, 1.5, 'F');
doc.setFontSize(28);
doc.setFont('helvetica', 'bold');
doc.setTextColor(255, 255, 255);
doc.text('CBSNexora', 20, 22);
doc.setFontSize(11);
doc.setFont('helvetica', 'normal');
doc.text('Core Banking Solution', 22, 30);
// --- 2. Title ---
let currentY = 55;
doc.setFontSize(16);
doc.setFont('helvetica', 'bold');
doc.setTextColor(...primaryColor);
doc.text('Welcome Kit Dispatch Confirmation', pageWidth / 2, currentY, { align: 'center' });
currentY += 12;
// --- 3. Date Line ---
doc.setFontSize(11);
doc.setFont('helvetica', 'normal');
doc.setTextColor(0, 0, 0);
const today = this.datePipe.transform(new Date(), 'longDate');
doc.text(`Date: ${today}`, pageWidth - 20, currentY, { align: 'right' });
currentY += 15;
// --- 4. Greeting ---
doc.text(`Dear ${clientName},`, 20, currentY);
currentY += 10;
// --- 5. Opening welcome paragraph ---
const p1 =
'Welcome to the CBSNexora family! We are delighted to have you on board as a valued client. As part of our onboarding process, we have dispatched your Welcome Kit to your registered address, which includes your account documentation and essential banking materials to help you get started with us.';
const splitP1 = doc.splitTextToSize(p1, pageWidth - 40);
doc.text(splitP1, 20, currentY);
currentY += splitP1.length * 6 + 8;
// --- 6. Short paragraph about the company ---
const p2 =
'CBSNexora is committed to providing secure, reliable, and customer-first banking services. Our team is always available to assist you with any banking needs, from account management to digital services, ensuring a seamless banking experience.';
const splitP2 = doc.splitTextToSize(p2, pageWidth - 40);
doc.text(splitP2, 20, currentY);
currentY += splitP2.length * 6 + 8;
// --- 7. Transition line before details ---
doc.text('Please find your Welcome Kit dispatch details below for your reference:', 20, currentY);
currentY += 8;
// --- 8. Details Section ---
autoTable(doc, {
startY: currentY,
theme: 'plain',
margin: { left: 20, right: 20 },
styles: { fontSize: 11, cellPadding: 6, textColor: [
0,
0,
0
] },
columnStyles: {
0: { fontStyle: 'bold', cellWidth: 50 },
1: { cellWidth: 'auto' }
},
didDrawCell: (data) => {
if (data.row.index >= 0) {
doc.setDrawColor(...lightGray);
doc.setLineWidth(0.2);
doc.line(
data.cell.x,
data.cell.y + data.cell.height,
data.cell.x + data.cell.width,
data.cell.y + data.cell.height
);
}
},
body: [
[
'Delivery Address',
address
],
[
'Delivery Mode',
mode
],
[
'Tracking ID',
trackingId
],
[
'Status',
status
],
[
'Dispatched On',
dispatchedOn
]
]
});
// --- 9. Closing paragraph ---
currentY = (doc as any).lastAutoTable.finalY + 12;
const p3 =
'Should you have any questions about your Welcome Kit or need any assistance, please do not hesitate to reach out to our support team. We look forward to serving you and building a long-term relationship with you.';
const splitP3 = doc.splitTextToSize(p3, pageWidth - 40);
doc.text(splitP3, 20, currentY);
currentY += splitP3.length * 6 + 12;
// --- 10. Sign-off ---
doc.text('Warm regards,', 20, currentY);
currentY += 7;
doc.setFont('helvetica', 'bold');
doc.text('CBSNexora Operations Team', 20, currentY);
// --- 11. Footer ---
// Ensure footer doesn't float too high on short pages, but pushes down if content goes long
let footerY = currentY + 30;
if (footerY < pageHeight - 30) {
footerY = pageHeight - 30;
}
doc.setDrawColor(...primaryColor);
doc.setLineWidth(0.3);
doc.line(20, footerY - 5, pageWidth - 20, footerY - 5);
doc.setFontSize(9);
doc.setFont('helvetica', 'italic');
doc.setTextColor(...grayColor);
const timestamp = this.datePipe.transform(new Date(), 'medium');
doc.text(`This is a system-generated document. Generated on: ${timestamp}`, 20, footerY + 2);
doc.save(`WelcomeKit_Dispatch_${trackingId !== 'N/A' ? trackingId : new Date().getTime()}.pdf`);
}
}