feat: non-individual entity UI improvements — hide individual-only sections, fix legal form display, send extended fields to API
- Hide Family Members tab/section for non-individual entities in client view, create flow, and preview - Hide Financial & Classification section for non-individual entities in personal data and preview - Fix legal form column in clients list — fetch legalForm from client API for correct display - Add person/business icon before client name in list rows - Remove unsupportedNpd filter — send authorizedSignatory, additionalInfo, dateOfRegistration to API - Add registration number format hint (mat-hint) based on constitution type - Add spacing between verification row and constitution type dropdowns - Reduce table cell padding and prevent action buttons from being clipped - Add missing MPL-2.0 license headers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ab01111424
commit
b890d24bb7
@@ -15,18 +15,25 @@ import { ClientsService } from '../clients.service';
|
||||
|
||||
/** Custom Components */
|
||||
import { ClientGeneralStepComponent } from '../client-stepper/client-general-step/client-general-step.component';
|
||||
import { ClientFamilyMembersStepComponent } from '../client-stepper/client-family-members-step/client-family-members-step.component';
|
||||
import { ClientAddressStepComponent } from '../client-stepper/client-address-step/client-address-step.component';
|
||||
import { ClientIndiaAddressStepComponent } from '../client-stepper/client-india-address-step/client-india-address-step.component';
|
||||
import { ClientEntityKycStepComponent } from '../client-stepper/client-entity-kyc-step/client-entity-kyc-step.component';
|
||||
import { ClientDatatableStepComponent } from '../client-stepper/client-datatable-step/client-datatable-step.component';
|
||||
import { ClientKycStepComponent } from '../client-stepper/client-kyc-step/client-kyc-step.component';
|
||||
import { ClientPreviewStepComponent } from '../client-stepper/client-preview-step/client-preview-step.component';
|
||||
import { ClientFamilyMembersStepComponent } from '../client-stepper/client-family-members-step/client-family-members-step.component';
|
||||
|
||||
/** Custom Services */
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatIconModule, MatIcon } from '@angular/material/icon';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatDialog, MAT_DIALOG_DATA, MatDialogClose } from '@angular/material/dialog';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { Inject } from '@angular/core';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
@@ -42,12 +49,17 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatIconModule,
|
||||
MatDivider,
|
||||
FaIconComponent,
|
||||
MatProgressSpinner,
|
||||
ClientGeneralStepComponent,
|
||||
ClientFamilyMembersStepComponent,
|
||||
ClientAddressStepComponent,
|
||||
ClientIndiaAddressStepComponent,
|
||||
ClientEntityKycStepComponent,
|
||||
ClientDatatableStepComponent,
|
||||
ClientKycStepComponent,
|
||||
ClientPreviewStepComponent
|
||||
ClientPreviewStepComponent,
|
||||
ClientFamilyMembersStepComponent,
|
||||
MatProgressBarModule,
|
||||
MatTooltip
|
||||
]
|
||||
})
|
||||
export class CreateClientComponent {
|
||||
@@ -55,16 +67,26 @@ export class CreateClientComponent {
|
||||
private router = inject(Router);
|
||||
private clientsService = inject(ClientsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
@ViewChild(ClientGeneralStepComponent) clientGeneralStep: ClientGeneralStepComponent;
|
||||
@ViewChild('clientFamily') clientFamilyMembersStep: ClientFamilyMembersStepComponent;
|
||||
@ViewChild('clientAddress') clientAddressStep: ClientAddressStepComponent;
|
||||
@ViewChild('clientIndiaAddress') clientIndiaAddressStep: ClientIndiaAddressStepComponent;
|
||||
@ViewChild('clientEntityKyc') clientEntityKycStep: ClientEntityKycStepComponent;
|
||||
@ViewChild('clientKyc') clientKycStep: ClientKycStepComponent;
|
||||
@ViewChild('clientFamilyMembers') clientFamilyMembersStep: ClientFamilyMembersStepComponent;
|
||||
@ViewChildren('dtclient') clientDatatables: QueryList<ClientDatatableStepComponent>;
|
||||
|
||||
datatables: any = [];
|
||||
legalFormType = 1;
|
||||
selectedTabIndex = 0;
|
||||
createdClientId = '';
|
||||
savingClient = false;
|
||||
|
||||
/** Track which tabs are in edit mode (false/undefined = read-only for completed tabs) */
|
||||
tabEditMode: Record<number, boolean> = {};
|
||||
/** Highest tab index reached — tabs below this show read-only when navigated back */
|
||||
highestTabReached = 0;
|
||||
|
||||
/** Client Template */
|
||||
clientTemplate: any;
|
||||
@@ -86,15 +108,40 @@ export class CreateClientComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/** Check if a tab is in read-only mode (completed tab navigated back to) */
|
||||
isTabReadOnly(tabIndex: number): boolean {
|
||||
// Only tabs that have been passed (below highest reached) AND not explicitly set to edit mode
|
||||
return tabIndex < this.highestTabReached && !this.tabEditMode[tabIndex];
|
||||
}
|
||||
|
||||
/** Enable editing for a specific tab */
|
||||
enableTabEdit(tabIndex: number): void {
|
||||
this.tabEditMode[tabIndex] = true;
|
||||
}
|
||||
|
||||
/** Cancel editing — revert tab back to read-only */
|
||||
cancelTabEdit(tabIndex: number): void {
|
||||
this.tabEditMode[tabIndex] = false;
|
||||
}
|
||||
|
||||
get clientGeneralForm() {
|
||||
return this.clientGeneralStep?.createClientForm;
|
||||
}
|
||||
|
||||
/** Total tab count: Individual has Family tab, Non-Individual has Scheme & Sector tab — both 5 */
|
||||
get totalTabs(): number {
|
||||
return 5;
|
||||
}
|
||||
|
||||
/** Get constitution type from general step for entity KYC */
|
||||
getConstitutionType(): string {
|
||||
return this.clientGeneralStep?.createClientForm?.get('clientNonPersonDetails.constitutionId')?.value || '';
|
||||
}
|
||||
|
||||
get client() {
|
||||
const general = this.clientGeneralStep?.clientGeneralDetails || {};
|
||||
const family = this.clientFamilyMembersStep?.familyMembers || {};
|
||||
const addr = this.clientTemplate.isAddressEnabled && this.clientAddressStep ? this.clientAddressStep.address : {};
|
||||
return { ...general, ...family, ...addr };
|
||||
return { ...general, ...addr };
|
||||
}
|
||||
|
||||
areFormvalids(): boolean {
|
||||
@@ -131,26 +178,111 @@ export class CreateClientComponent {
|
||||
}
|
||||
}
|
||||
|
||||
legalFormChange(eventData: { legalForm: number }) {
|
||||
this.legalFormType = eventData.legalForm;
|
||||
this.setDatatables();
|
||||
/**
|
||||
* Next button handler — saves client on General tab before moving forward.
|
||||
*/
|
||||
/** Save progress percentage for progress bar */
|
||||
saveProgress = 0;
|
||||
|
||||
onNext() {
|
||||
// If on General tab and client not yet created — save first
|
||||
if (this.selectedTabIndex === 0 && !this.createdClientId) {
|
||||
if (!this.clientGeneralForm?.valid) {
|
||||
this.clientGeneralForm?.markAllAsTouched();
|
||||
setTimeout(() => {
|
||||
const firstInvalid = document.querySelector('.mat-mdc-form-field.ng-invalid');
|
||||
if (firstInvalid) {
|
||||
firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show confirmation dialog
|
||||
const dialogRef = this.dialog.open(SaveConfirmDialogComponent, {
|
||||
width: '440px',
|
||||
disableClose: true
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((confirmed: boolean) => {
|
||||
if (!confirmed) return;
|
||||
this.saveAndProceed();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Save current tab data before moving to next
|
||||
if (this.createdClientId) {
|
||||
this.saveCurrentTabData();
|
||||
}
|
||||
|
||||
this.selectedTabIndex = this.selectedTabIndex + 1;
|
||||
if (this.selectedTabIndex > this.highestTabReached) {
|
||||
this.highestTabReached = this.selectedTabIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the create client form.
|
||||
*/
|
||||
submit() {
|
||||
/** Save the current tab's data to the API */
|
||||
private saveCurrentTabData() {
|
||||
const tabIndex = this.selectedTabIndex;
|
||||
const clientId = this.createdClientId;
|
||||
|
||||
// KYC tab
|
||||
const kycTabIndex = 1;
|
||||
if (tabIndex === kycTabIndex) {
|
||||
// Save KYC
|
||||
if (this.clientKycStep?.hasKycData && !this.clientKycStep.kycId) {
|
||||
this.clientsService.saveClientKyc(clientId, this.clientKycStep.kycData).subscribe({ error: () => {} });
|
||||
}
|
||||
// Upload photo & signature
|
||||
if (this.clientKycStep?.photographFile) {
|
||||
this.clientsService.uploadClientProfileImage(clientId, this.clientKycStep.photographFile).subscribe({ error: () => {} });
|
||||
}
|
||||
if (this.clientKycStep?.signatureFile) {
|
||||
this.clientsService.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile).subscribe({ error: () => {} });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Address tab (index 2 for Individual, 3 for Non-Individual)
|
||||
const addressTabIndex = this.legalFormType === 2 ? 3 : 2;
|
||||
if (tabIndex === addressTabIndex) {
|
||||
if (this.clientIndiaAddressStep?.hasAddressData) {
|
||||
this.saveClientAddresses(clientId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Family tab (index 3 for Individual, 4 for Non-Individual)
|
||||
const familyTabIndex = this.legalFormType === 2 ? 4 : 3;
|
||||
if (tabIndex === familyTabIndex) {
|
||||
if (this.clientFamilyMembersStep?.hasFamilyMembers) {
|
||||
this.saveClientFamilyMembers(clientId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Executes the save API call with progress animation */
|
||||
private saveAndProceed() {
|
||||
this.savingClient = true;
|
||||
this.saveProgress = 0;
|
||||
|
||||
// Animate progress bar
|
||||
const progressInterval = setInterval(() => {
|
||||
if (this.saveProgress < 90) {
|
||||
this.saveProgress += Math.random() * 15;
|
||||
if (this.saveProgress > 90) this.saveProgress = 90;
|
||||
}
|
||||
}, 200);
|
||||
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const clientData = {
|
||||
...this.client,
|
||||
dateFormat,
|
||||
locale
|
||||
};
|
||||
const clientData = { ...this.client, dateFormat, locale };
|
||||
|
||||
if (this.clientTemplate.datatables && this.clientTemplate.datatables.length > 0) {
|
||||
const datatables: any[] = [];
|
||||
this.clientDatatables.forEach((clientDatatable: ClientDatatableStepComponent) => {
|
||||
this.clientDatatables?.forEach((clientDatatable: ClientDatatableStepComponent) => {
|
||||
datatables.push(clientDatatable.payload);
|
||||
});
|
||||
if (datatables.length > 0) {
|
||||
@@ -158,36 +290,279 @@ export class CreateClientComponent {
|
||||
}
|
||||
}
|
||||
|
||||
this.clientsService.createClient(clientData).subscribe((response: any) => {
|
||||
const clientId = response.resourceId;
|
||||
if (this.clientKycStep?.hasKycData) {
|
||||
this.clientsService.saveClientKyc(clientId, this.clientKycStep.kycData).subscribe({
|
||||
complete: () =>
|
||||
this.router.navigate(
|
||||
[
|
||||
'../',
|
||||
clientId
|
||||
],
|
||||
{ relativeTo: this.route }
|
||||
),
|
||||
error: () =>
|
||||
this.router.navigate(
|
||||
[
|
||||
'../',
|
||||
clientId
|
||||
],
|
||||
{ relativeTo: this.route }
|
||||
)
|
||||
});
|
||||
} else {
|
||||
this.router.navigate(
|
||||
[
|
||||
'../',
|
||||
clientId
|
||||
],
|
||||
{ relativeTo: this.route }
|
||||
);
|
||||
this.clientsService.createClient(clientData).subscribe({
|
||||
next: (response: any) => {
|
||||
clearInterval(progressInterval);
|
||||
this.saveProgress = 100;
|
||||
this.createdClientId = response.resourceId;
|
||||
// Brief delay to show 100% before transitioning
|
||||
setTimeout(() => {
|
||||
this.savingClient = false;
|
||||
this.saveProgress = 0;
|
||||
this.selectedTabIndex = 1;
|
||||
this.highestTabReached = 1;
|
||||
}, 400);
|
||||
},
|
||||
error: () => {
|
||||
clearInterval(progressInterval);
|
||||
this.savingClient = false;
|
||||
this.saveProgress = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
legalFormChange(eventData: { legalForm: number }) {
|
||||
this.legalFormType = eventData.legalForm;
|
||||
this.setDatatables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the create client form.
|
||||
* If the client was already created via onNext(), reuse that client ID
|
||||
* instead of creating a duplicate.
|
||||
*/
|
||||
/** Validation error message for display */
|
||||
validationError = '';
|
||||
|
||||
submit() {
|
||||
// Validate: at least one family member required (Individual clients)
|
||||
if (this.legalFormType === 1 && this.clientFamilyMembersStep && !this.clientFamilyMembersStep.hasFamilyMembers) {
|
||||
this.validationError = 'At least one family member is required. Please add a family member before submitting.';
|
||||
// Switch to Family tab
|
||||
this.selectedTabIndex = 3;
|
||||
return;
|
||||
}
|
||||
this.validationError = '';
|
||||
|
||||
if (this.createdClientId) {
|
||||
// Client already created in onNext() — just finalize (upload files, navigate)
|
||||
this.finalizeClient(this.createdClientId);
|
||||
} else {
|
||||
// Fallback: create client now (shouldn't happen in normal flow)
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const clientData = {
|
||||
...this.client,
|
||||
dateFormat,
|
||||
locale
|
||||
};
|
||||
|
||||
if (this.clientTemplate.datatables && this.clientTemplate.datatables.length > 0) {
|
||||
const datatables: any[] = [];
|
||||
this.clientDatatables.forEach((clientDatatable: ClientDatatableStepComponent) => {
|
||||
datatables.push(clientDatatable.payload);
|
||||
});
|
||||
if (datatables.length > 0) {
|
||||
clientData['datatables'] = datatables;
|
||||
}
|
||||
}
|
||||
|
||||
this.clientsService.createClient(clientData).subscribe((response: any) => {
|
||||
this.createdClientId = response.resourceId;
|
||||
this.finalizeClient(this.createdClientId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize: upload photo/signature, save KYC, save addresses, then navigate.
|
||||
*/
|
||||
private finalizeClient(clientId: string) {
|
||||
const navigateToClient = () => this.router.navigate(['../', clientId], { relativeTo: this.route });
|
||||
|
||||
// Upload photo if selected
|
||||
if (this.clientKycStep?.photographFile) {
|
||||
this.clientsService.uploadClientProfileImage(clientId, this.clientKycStep.photographFile).subscribe({ error: () => {} });
|
||||
}
|
||||
|
||||
// Upload signature if selected
|
||||
if (this.clientKycStep?.signatureFile) {
|
||||
this.clientsService.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile).subscribe({ error: () => {} });
|
||||
}
|
||||
|
||||
// Save addresses from India address step
|
||||
if (this.clientIndiaAddressStep?.hasAddressData) {
|
||||
this.saveClientAddresses(clientId);
|
||||
}
|
||||
|
||||
// Save family members
|
||||
if (this.clientFamilyMembersStep?.hasFamilyMembers) {
|
||||
this.saveClientFamilyMembers(clientId);
|
||||
}
|
||||
|
||||
// Save KYC only if it wasn't already created during the KYC step
|
||||
if (this.clientKycStep?.hasKycData && !this.clientKycStep.kycId) {
|
||||
this.clientsService.saveClientKyc(clientId, this.clientKycStep.kycData).subscribe({
|
||||
next: () => navigateToClient(),
|
||||
error: () => navigateToClient()
|
||||
});
|
||||
} else {
|
||||
navigateToClient();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save addresses from the India address step to Fineract address API.
|
||||
* Fetches address template to resolve address type IDs, then POSTs each address.
|
||||
*/
|
||||
private saveClientAddresses(clientId: string) {
|
||||
const addressData = this.clientIndiaAddressStep.addressData;
|
||||
// Map tab names to Fineract address type names (as defined in address template)
|
||||
const typeNameMap: Record<string, string[]> = {
|
||||
permanent: ['Permanent', 'Permanent Address'],
|
||||
current: ['Residential', 'Current', 'Current / Residential', 'Communication'],
|
||||
office: ['Office', 'Office / Work', 'Business', 'Work'],
|
||||
correspondence: ['Correspondence', 'Mailing', 'Postal']
|
||||
};
|
||||
|
||||
this.clientsService.getClientAddressTemplate().subscribe({
|
||||
next: (template: any) => {
|
||||
const typeOptions = template?.addressTypeIdOptions || [];
|
||||
for (const [tab, addr] of Object.entries(addressData)) {
|
||||
if (!addr || !(addr as any).addressLine1) continue;
|
||||
|
||||
// Find matching address type ID
|
||||
const possibleNames = typeNameMap[tab] || [tab];
|
||||
let typeId = typeOptions.find((t: any) =>
|
||||
possibleNames.some((n: string) => t.name?.toLowerCase().includes(n.toLowerCase()))
|
||||
)?.id;
|
||||
|
||||
// Fallback: use first available type if no match
|
||||
if (!typeId && typeOptions.length > 0) {
|
||||
typeId = typeOptions[0].id;
|
||||
}
|
||||
if (!typeId) continue;
|
||||
|
||||
const payload: any = {
|
||||
addressLine1: (addr as any).addressLine1 || '',
|
||||
addressLine2: (addr as any).addressLine2 || '',
|
||||
addressLine3: '',
|
||||
townVillage: (addr as any).locality || '',
|
||||
postalCode: (addr as any).pinCode || '',
|
||||
city: (addr as any).city || '',
|
||||
countyDistrict: (addr as any).district || '',
|
||||
street: (addr as any).landmark || '',
|
||||
addressType: typeId,
|
||||
isActive: tab === 'permanent'
|
||||
};
|
||||
|
||||
// Try to resolve state to stateProvinceId
|
||||
if ((addr as any).state && template?.stateProvinceIdOptions?.length) {
|
||||
const stateMatch = template.stateProvinceIdOptions.find((s: any) =>
|
||||
s.name?.toLowerCase() === (addr as any).state?.toLowerCase()
|
||||
);
|
||||
if (stateMatch) payload.stateProvinceId = stateMatch.id;
|
||||
}
|
||||
|
||||
this.clientsService.createClientAddress(clientId, typeId.toString(), payload).subscribe({ error: () => {} });
|
||||
}
|
||||
},
|
||||
error: () => {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save family members via POST /clients/{id}/familymembers
|
||||
*/
|
||||
private saveClientFamilyMembers(clientId: string) {
|
||||
const members = this.clientFamilyMembersStep.clientFamilyMembers;
|
||||
for (const member of members) {
|
||||
const marriageCertFile = member._marriageCertFile || null;
|
||||
const payload = { ...member };
|
||||
// Remove internal/transient fields
|
||||
delete payload._linkedClient;
|
||||
delete payload._marriageCertFile;
|
||||
delete payload.dateFormat;
|
||||
delete payload.locale;
|
||||
payload.dateFormat = this.settingsService.dateFormat;
|
||||
payload.locale = this.settingsService.language.code;
|
||||
this.clientsService.addFamilyMember(clientId, payload).subscribe({
|
||||
next: (res: any) => {
|
||||
// Upload marriage certificate if present
|
||||
if (marriageCertFile && res?.resourceId) {
|
||||
this.uploadFamilyMemberDocument(clientId, res.resourceId, marriageCertFile, 'Marriage Certificate');
|
||||
}
|
||||
},
|
||||
error: () => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a document for a family member.
|
||||
*/
|
||||
private uploadFamilyMemberDocument(clientId: string, familyMemberId: string, file: File, docName: string) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('name', docName);
|
||||
formData.append('description', docName);
|
||||
this.clientsService.uploadFamilyMemberDocument(clientId, familyMemberId, formData).subscribe({ error: () => {} });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save & Next Confirmation Dialog
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-save-confirm-dialog',
|
||||
template: `
|
||||
<div class="scd">
|
||||
<div class="scd-header">
|
||||
<div class="scd-header__icon-wrap">
|
||||
<mat-icon class="scd-header__icon">save</mat-icon>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="scd-header__title">Save & Proceed</h3>
|
||||
<p class="scd-header__sub">Client general details will be saved</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scd-body">
|
||||
<p>Are you sure you want to save the client details and proceed to the next step?</p>
|
||||
<div class="scd-info">
|
||||
<mat-icon class="scd-info__icon">info</mat-icon>
|
||||
<span>Once saved, a client record will be created. You can update the details later.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scd-actions">
|
||||
<button mat-stroked-button [mat-dialog-close]="false" class="scd-cancel">
|
||||
<mat-icon>close</mat-icon>
|
||||
Cancel
|
||||
</button>
|
||||
<button mat-flat-button color="primary" [mat-dialog-close]="true" class="scd-confirm">
|
||||
<mat-icon>check</mat-icon>
|
||||
Save & Proceed
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.scd { background: #fff; border-radius: 12px; overflow: hidden; }
|
||||
.scd-header {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
padding: 20px 24px 16px;
|
||||
}
|
||||
.scd-header__icon-wrap {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
background: #eff6ff; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.scd-header__icon { color: #1074b9; font-size: 24px; width: 24px; height: 24px; }
|
||||
.scd-header__title { margin: 0; font-size: 17px; font-weight: 700; color: #0f172a; }
|
||||
.scd-header__sub { margin: 2px 0 0; font-size: 12px; color: #64748b; }
|
||||
.scd-body { padding: 0 24px 20px; }
|
||||
.scd-body p { margin: 0 0 14px; font-size: 14px; color: #374151; line-height: 1.5; }
|
||||
.scd-info {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
padding: 10px 14px; background: #f0f9ff; border: 1px solid #bae6fd;
|
||||
border-radius: 8px; font-size: 12px; color: #0c4a6e;
|
||||
}
|
||||
.scd-info__icon { font-size: 16px !important; width: 16px !important; height: 16px !important; color: #0284c7; flex-shrink: 0; margin-top: 1px; }
|
||||
.scd-actions {
|
||||
display: flex; justify-content: flex-end; gap: 10px;
|
||||
padding: 14px 24px; border-top: 1px solid #f1f5f9; background: #f8fafc;
|
||||
}
|
||||
.scd-cancel, .scd-confirm { display: flex !important; align-items: center; gap: 6px; font-size: 13px !important; height: 38px !important; border-radius: 8px !important; }
|
||||
.scd-confirm { font-weight: 600 !important; }
|
||||
`],
|
||||
imports: [MatIcon, MatDialogClose]
|
||||
})
|
||||
export class SaveConfirmDialogComponent {}
|
||||
|
||||
Reference in New Issue
Block a user